Skip to content

CQL

Clinical Quality Language is how clinical criteria are expressed against this specification: the population logic behind a report, a cohort, or a decision-support rule. One language, so a criterion written once means the same thing whether it is evaluated for one patient at the point of care or for a whole panel in a report.

This page is the contract for authoring against this IG rather than against base FHIR.

The model

A CQL library compiled against the stock FHIR R4 model can only see base R4. A retrieve written [Observation: "HbA1c"] resolves to base Observation, and anything derived from it names http://hl7.org/fhir/StructureDefinition/Observation. Criteria written that way are not bound to this specification at all: they cannot see its profiles, its slices or its extensions.

This IG therefore publishes its own CQL model definition, generated from its profiles. Declare it at the top of a library:

using Nexus version '1.26.0'

Each type in the model is one profile. Retrieves then carry the profile's canonical, so a criterion's declared data requirements name this specification's profiles:

define "Latest HbA1c":
  Last([NexusEmrCoreObservation: "HbA1c"] O
       sort by (effective as FHIR.dateTime).value)

The primary code path

[SomeType: "a-code"] is the natural way to filter a retrieve by code, and it works only when the type declares which element the code applies to — its primary code path. For most resources base FHIR already declares one (Observation.code, Encounter.type, MedicationRequest.medication). For some it declares none, and the consequence is not a compile error.

A bare code filter on a type with no primary code path is not an error

It compiles. The translator emits a warning, the retrieve is left filtering on nothing determinable, and the data requirements derived from it carry a code filter with no path — which is invalid against FHIR's own drq-1 invariant. Nothing fails.

Tooling for this IG treats that warning as an error. If you are using other tooling, name the element explicitly and the question does not arise:

[NexusEmrCoreFamilyMemberHistory: condition.code in "Colorectal Cancer"]

This specification declares a primary code path on the profiles below, using HL7's cqf-modelInfo-primaryCodePath extension. Base FHIR declares none for any of these resources, so a bare code filter on them works only against this IG's model.

Profile Resource Primary code path
DocumentReference DocumentReference type
Invoice Invoice type
Schedule Schedule serviceType
Slot Slot serviceType

A profile that is absent from this table and has no base-FHIR code path is absent on purpose. FamilyMemberHistory is the instructive case: its only required coded element is relationship (mother, father), and a criterion about family history means condition.code. Declaring relationship would make the natural authoring form compile, run, and match the wrong element — worse than declaring nothing, because it would appear to work.

Data requirements

Static analysis of a criteria library yields a second artifact: a Library with type = module-definition, declaring the resource types it reads, the code and value-set filters it applies, and the elements it touches. It is derived from the logic, never authored beside it, so it cannot drift from what the criteria actually do.

That manifest is what makes a cohort cheap to keep current: intersect what changed with what each cohort declares it depends on, and re-evaluate only the affected ones. It also distinguishes criteria whose membership changes by pure passage of time ("no visit in six months") from criteria anchored to another event ("two results ninety days apart") — only the first needs re-checking when nothing has happened.

Units

Quantity comparisons carry units, and unit conversion is not always safe to infer. UCUM converts 55 mmol/mol to 5.5 % because both are dimensionless ratios; for HbA1c that is wrong by nearly two points, because IFCC and NGSP are related by an affine formula rather than a ratio. Conversions that genuinely cannot be inferred — creatinine in umol/L against a threshold in mg/dL, which needs a molecular weight — are correctly refused.

Criteria over a measurement should therefore state the unit they mean rather than assume one.