Skip to content

Implementing the CQL 1.5 Terminology Types

Bryn Rhodes edited this page Dec 30, 2021 · 2 revisions

CQL 1.5 introduced support for terminology types to allow references to terminology to be passed as arguments, rather than forcing an expand whenever a value set or code system is referenced. This capability is backwards-compatible, but there are cases where the translator is not accounting for the new capability correctly, resulting in errors when translating CQL that makes use of value set references as arguments to functions.

The following library illustrates this issue:

library TestVSCastFunction

using FHIR version '4.0.1'

include FHIRHelpers version '4.0.1'

valueset "Narcolepsy": 'https://example.org/fhir/ValueSet/narcolepsy'

define function "Conditions in ValueSet"(conditions List<Condition>, codes List<System.Code>):
  conditions C
    where FHIRHelpers.ToConcept(C.code) in codes

define TestConditions:
  [Condition] C
    where C.code in "Narcolepsy"

define TestConditionsDirectly:
  "Conditions in ValueSet"([Condition], "Narcolepsy")

Specifically, in 1.5, the invocation signature of the value set references is ValueSet, whereas in 1.4 it is list<System.Code>. To ensure that the introduction of the terminology ty8pes in CQL maintains backwards compatibility while at the same time preserving the design intent of statically identifying terminology usage and supporting ELM that is both backwards and forwards compatible (so that 1.4 and 1.5 CQL may be run on the same engine), the following fixes have been made to the translator along with an STU comment against the CQL specification, as well as several updates and fixes to the Java-based engine and evaluator to ensure correct and backwards-compatible functionality.

  1. Added a "name" element to the Vocabulary type. This was done to make the run-time representation of the terminology types more user-friendly.
  2. Added a "codesystem" element to the ValueSet type to complete the representation of code system binding overrides used in value set declarations.
  3. Added an "ExpandValueSet" ELM node to allow value set expansion to be represented explicitly in ELM.
  4. Added a "preserve" attribute to the ValueSetRef node to allow engines to decide how to process value set references (i.e. expand, or return a ValueSet instance)
  5. Added "expression" elements to the terminology membership nodes in ELM to preserve backwards compatible ELM deserialization (this was done in FHIR-32971)
  6. Added an implicit conversion in 1.5 from ValueSet to List using the ExpandValueSet operator

These changes have been proposed to the specification in FHIR-34804

To ensure the 1.5 translator running in 1.4 compatibility mode produces correct 1.4 ELM, the following changes were made (Github issue #707):

  1. In 1.4 compatibility mode, a reference to a codesystem or valueset declaration is typed as a List<Code>, rather than the ValueSet or CodeSystem types introduced in 1.5.
  2. In 1.4 compatibility mode, references to the new terminology types results in a compile-time error.
  3. In 1.4 compatibility mode, terminology membership operators can only be resolved with references to codesystem or valueset declarations
  4. In 1.5 or greater, a ValueSetRef will be output with the new preserve attribute set to true.

Consider the following function definition:

define function "Conditions in ValueSet"(conditions List<Condition>, codes List<System.Code>):
  conditions C
    where FHIRHelpers.ToConcept(C.code) in codes

This definition is valid in 1.4 and 1.5 and results in the same ELM in both.

When using the function in 1.4 CQL:

define TestConditionsDirectly:
  "Conditions in ValueSet"([Condition], "Narcolepsy")

The translator will output the following ELM:

<def name="TestConditionsDirectly" context="Patient" accessLevel="Public">
   <expression name="Conditions in ValueSet" xsi:type="FunctionRef">
      <operand dataType="fhir:Condition" templateId="http://hl7.org/fhir/StructureDefinition/Condition" xsi:type="Retrieve"/>
      <operand name="Narcolepsy" xsi:type="ValueSetRef"/>
   </expression>
</def>

Note that the second argument is a ValueSetRef with no preserve attribute set.

When using the function in 1.5 CQL, however, the translator will output the following ELM:

<def name="TestConditionsViaFunction" context="Patient" accessLevel="Public">
   <expression name="Conditions in ValueSet" xsi:type="FunctionRef">
      <operand dataType="fhir:Condition" templateId="http://hl7.org/fhir/StructureDefinition/Condition" xsi:type="Retrieve"/>
      <operand name="VS Cast Function" xsi:type="FunctionRef">
         <operand xsi:type="ExpandValueSet">
            <operand name="Narcolepsy" preserve="true" xsi:type="ValueSetRef"/>
         </operand>
      </operand>
   </expression>
</def>

In the 1.5 CQL, the reference to the "Narcolepsy" value set is of type ValueSet, and so the implicit conversion from ValueSet to List<Code> is used (i.e. ExpandValueSet). Note that the ValueSetRef has the preserve attribute set to true.

This approach allows engines to run 1.4 and 1.5 ELM. A 1.4 engine will always expand a ValueSetRef if it is evaluated, whereas a 1.5 engine will only expand a ValueSetRef if the preserve attribute is not present, or if it is set to false.

To implement this behavior in the Java engine the following changes were made (Github issue #523):

  1. Added runtime types to represent the new terminology types (Vocabulary, CodeSystem, and ValueSet), rather than the Def elements that were being used
  2. Added support for the ExpandValueSet operator to explicitly expand a ValueSet
  3. Updated the ValueSetRef implementation to expand the valueset if the preserve attribute is not present or set to false, and to return an instance of a ValueSet otherwise
  4. Updated the CodeSystemRef implementation to return an instance of a CodeSystem
  5. Updated the terminology membership implementations to use the new Expression elements only if they are present, and continue to use the Ref elements directly otherwise
  6. Updated the engine to use the new runtime representations of terminology types throughout

Wiki Index

Home

Authoring Patterns - QICore v4.1.1

Authoring Patterns - QICore v5.0.0

Authoring Patterns - QICore v6.0.0

Authoring Measures in CQL

Composite Measure Development

Cooking with CQL Examples

Cooking with CQL Q&A All Categories
Additional Q&A Examples

CQL 1.3 Impact Guidance

CQL Error Messages

Developers Introduction to CQL

Discussion Items

Example Measures

Formatting and Usage Topics

Formatting Conventions

Library Versioning

Negation in QDM

QDM Known Issues

Specific Occurrences

Specifying Population Criteria

Supplemental Data Elements

Terminology in CQL

Translator Options For Measure Development

Unions in CQL

Clone this wiki locally