-
Notifications
You must be signed in to change notification settings - Fork 24
Implementing the CQL 1.5 Terminology Types
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.
- Added a "name" element to the Vocabulary type. This was done to make the run-time representation of the terminology types more user-friendly.
- Added a "codesystem" element to the ValueSet type to complete the representation of code system binding overrides used in value set declarations.
- Added an "ExpandValueSet" ELM node to allow value set expansion to be represented explicitly in ELM.
- 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)
- Added "expression" elements to the terminology membership nodes in ELM to preserve backwards compatible ELM deserialization (this was done in FHIR-32971)
- 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):
- In 1.4 compatibility mode, a reference to a
codesystem
orvalueset
declaration is typed as aList<Code>
, rather than theValueSet
orCodeSystem
types introduced in 1.5. - In 1.4 compatibility mode, references to the new terminology types results in a compile-time error.
- In 1.4 compatibility mode, terminology membership operators can only be resolved with references to
codesystem
orvalueset
declarations - In 1.5 or greater, a
ValueSetRef
will be output with the newpreserve
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):
- Added runtime types to represent the new terminology types (
Vocabulary
,CodeSystem
, andValueSet
), rather than theDef
elements that were being used - Added support for the
ExpandValueSet
operator to explicitly expand aValueSet
- Updated the
ValueSetRef
implementation to expand the valueset if thepreserve
attribute is not present or set to false, and to return an instance of aValueSet
otherwise - Updated the
CodeSystemRef
implementation to return an instance of aCodeSystem
- Updated the terminology membership implementations to use the new
Expression
elements only if they are present, and continue to use theRef
elements directly otherwise - Updated the engine to use the new runtime representations of terminology types throughout
Authoring Patterns - QICore v4.1.1
Authoring Patterns - QICore v5.0.0
Authoring Patterns - QICore v6.0.0
Cooking with CQL Q&A All Categories
Additional Q&A Examples
Developers Introduction to CQL
Specifying Population Criteria