-
Notifications
You must be signed in to change notification settings - Fork 24
Unions in CQL
The union operator in CQL combines two lists, resulting in a single set that contains elements from both lists. The union in CQL is a set operation, meaning that if an element appears in both lists, it will only appear once in the output. (NOTE: The union in CQL 1.1 does not eliminate duplicates, but the behavior was changed in CQL 1.2 to make the operator more intuitive. This discussion is based on the 1.2 version of CQL.)
To illustrate the basic operation, consider the following diagram:
The first list of integers is combined with the second list to produce a single list containing the elements that appear in either list.
Simple lists in CQL can only contain elements of the same type. When evaluating a strict union, this means that the following CQL is invalid:
define "Intervention Comfort Measures":
["Intervention, Order": "Comfort Measures"]
union ["Intervention, Performed": "Comfort Measures"]
This is because the structures of "Intervention, Order" and "Intervention, Performed" are different; they have different attributes. In particular, "Intervention, Order" has the following attributes:
id
code
patientId
reporter
recorder
authorDatetime
reason
negationRationale
While "Intervention, Performed" has the following attributes:
id
code
patientId
reporter
recorder
authorDatetime
relevantPeriod
reason
result
status
negationRationale
Note that the first five attributes, id
, code
, patientId
, reporter
, and recorder
, are common to all QDM data types. The id
attribute is the instance identifier for the data element (e.g. Encounter #11335). The code
attribute represents the coded type of the element (e.g. SNOMED-CT:305338009 (Admission to GP Hospital)). The patientId
attribute represents a unique identifier for the patient (e.g. Patient #1234), and the reporter
and recorder
attributes represent provenance information for the data element.
Because of this difference in structures, a strict union requires that the data elements be "converted" into the same format so they can appear in the same list. In CQL, this is done with a return
clause for one or the other of the inputs to the union:
define "Intervention Comfort Measures":
["Intervention, Order": "Comfort Measures"]
union (
["Intervention, Performed": "Comfort Measures"] P
return "Intervention, Order" {
id: P.id,
code: P.code,
patientId: P.patientId,
reporter: P.reporter,
recorder: P.recorder,
authorDatetime: Coalesce(start of P.relevantPeriod, P.authorDatetime)
}
)
The return clause in this case is "converting" the "Intervention, Performed" events into "Intervention, Order" events by providing the values for the corresponding attributes, and calculating a value for the authorDatetime
attribute based on the relevantPeriod
of the "Intervention Performed", if it is available.
The result of this union will now be a list of "Intervention, Order" structures. Note that this may not be the best way to do this, given that the result now looks like a list of "Intervention, Order", but some of them are actually "Intervention, Performed".
Using this approach it would not be possible to tell after the elements had been combined which ones were orders and which were actually performed interventions.
An alternative approach is to use the more flexible choice union, but it will result in a list with different elements:
define "Intervention Comfort Measures":
["Intervention, Order": "Comfort Measures"]
union ["Intervention, Performed": "Comfort Measures"]
This result will include both orders and performed interventions. It is possible to tell which elements are which using the is
operator:
"Intervention Comfort Measures" I
where I is "Intervention, Order"
However, because the structures are different, accessing attributes of the elements will return null
if the attribute does not exist for the instance:
"Intervention Comfort Measures" I
where I.relevantPeriod starts during "Measurement Period"
For the elements of the list that are "Intervention, Performed", the criteria is correct. But for elements of the list that are "Intervention, Order", they have no relevantPeriod attribute, so the expression will result in null
.
It is important to note at this point that QDM allowed unions of different types, and that this ambiguity about the date/time attribute being accessed was covered up by the fact that temporal relationships in QDM were always based on an implied primary temporal attribute for the event. With the added precision of CQL, we need to be able to say exactly what we mean when we apply a criteria like this against a list that contains elements of different types.
One way to do this is to use the Coalesce
operator to provide a "default" for the case where a given attribute is not present:
"Intervention Comfort Measures" I
where Coalesce(start of I.relevantPeriod, I.authorDatetime) during "Measurement Period"
This condition accesses the start of the relevantPeriod attribute if it is present, otherwise it will access the authorDatetime attribute.
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