Skip to main content
Resource Publishing

Code Lists

A code list is a named set of clinical or medicinal concept identifiers. Patient query variables never carry codes directly: they name a code list, and the query engine resolves that name to a flat list of concept identifiers at run time.

Every code list a query uses must be declared in the query's own <codeLists> block. There is no inheritance from a parent query or from the report that contains it.

<codeLists>
<codeList>
<name>DIABETES</name>
<codeListDefinition>
<rules>
<inclusion>&lt;&lt; 73211009 |Diabetes mellitus|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>

<variables>
<codedEntryVariable name="DIABETES_DAT" code="DIABETES" .../>
</variables>

Declaring a code list

<codeLists> holds zero or more <codeList> elements. Each one has a <name> followed by exactly one payload element.

PayloadCodes come fromRe-resolved at query time
<codeListDefinition>Expressions evaluated against the terminologyYes, on every run
<fixedCodeList>The literal identifiers you listNot applicable

<name> is a child element, not an attribute, and it must come first. <codeList name="DIABETES"> is not valid, and neither is putting the payload before the name.

The name is the identifier used by variable attributes. It is scoped to the query, so it is yours to choose. Names must be unique within a query: if two entries share a name, the last one silently wins.

Inline definition

Evaluates one or more expressions against the terminology to build the list. Expressions are re-evaluated on every query run, so a list defined this way follows the SNOMED CT release: a concept added under 73211009 in a later release starts matching without the query changing.

<codeList>
<name>DIABETES</name>
<codeListDefinition>
<rules>
<inclusion>&lt;&lt; 73211009 |Diabetes mellitus|</inclusion>
<exclusion>&lt;&lt; 420825003 |Maturity-onset diabetes of the young|</exclusion>
</rules>
</codeListDefinition>
</codeList>

<rules> takes one or more <inclusion> and <exclusion> elements in any order and any mix. Each element holds exactly one expression. See Expression syntax below.

Note the XML escaping: < must be written &lt;, so the descendant-or-self operator << becomes &lt;&lt;.

Fixed code list

An explicit list of concept identifiers, with no terminology lookup at all. Nothing is expanded, so descendants of the concepts you list are not included.

<codeList>
<name>SPECIFIC_CODES</name>
<fixedCodeList>
<codes>
<code>73211009</code>
<code>44054006</code>
</codes>
</fixedCodeList>
</codeList>

At least one <code> is required. The values are not checked against the terminology, either at publish time or at run time: an identifier that does not exist simply matches nothing.

Expression syntax

<inclusion> and <exclusion> accept a single expression from the table below. An optional |Display term| label may follow the identifier and is ignored.

ExpressionResolves to
123456789That one concept
< 123456789All descendants of the concept
<< 123456789The concept and all its descendants
<! 123456789Immediate children of the concept
<<! 123456789The concept and its immediate children
> 123456789All ancestors of the concept
>> 123456789The concept and all its ancestors
>! 123456789Immediate parents of the concept
>>! 123456789The concept and its immediate parents
^ 123456789Members of the reference set with that identifier
DMD 123456789dm+d products related to the given product concept
DMD INGREDIENT 123456789dm+d products containing the given ingredient substance
BNF CHAPTER 5.1dm+d products in the given BNF chapter or sub-chapter
READ H33%Concepts forward-mapped from the matching CTV3 codes, % is a trailing wildcard

There is exactly one space between the operator and the identifier. An expression that matches none of the forms above throws at run time with Could not parse rule on DIABETES expression: ....

note

Despite the resemblance, this is not SNOMED CT Expression Constraint Language. Each expression is matched against the fixed set of prefixes above, so ECL constructs beyond that set are not supported: no MINUS, AND or OR, no attribute or refinement constraints (:), no wildcards (*), no parentheses, and no nesting or composition of any kind. Use separate <inclusion> and <exclusion> elements instead of set operators, and a separate code list instead of a compound expression.

<! and <<! are not ECL operators at all. ^ takes the numeric reference set identifier only, not a PCD cluster name: write ^ 12463601000001108, not ^ ASTTRT_COD. The cluster name is conventionally carried in the display label for readability.

Everything is resolved against the terminology tables directly, and the result is flattened into the query as a literal list of identifiers. There is no call out to an external terminology server, and no limit on how far a hierarchy expression expands.

How inclusions and exclusions combine

Elements are applied strictly in document order. Each <inclusion> adds its concepts to the set, and each <exclusion> removes its concepts from whatever the set holds at that point.

This is not set algebra, and the order matters. An <exclusion> placed before the <inclusion> that would have added the code removes nothing, and the code ends up in the list:

<rules>
<!-- Wrong: the exclusion runs first and removes nothing. -->
<exclusion>&lt;&lt; 420825003</exclusion>
<inclusion>&lt;&lt; 73211009</inclusion>
</rules>

Put every exclusion after the inclusions it is meant to narrow.

tip

A bare concept identifier removes only that one concept, and leaves its descendants in the list. This definition resolves to diabetes mellitus and every subtype except the one named:

<rules>
<inclusion>&lt;&lt; 73211009 |Diabetes mellitus|</inclusion>
<exclusion>420825003 |Maturity-onset diabetes of the young|</exclusion>
</rules>

Writing the exclusion as &lt;&lt; 420825003 removes that concept and its descendants, which for an exclusion is usually what you want.

Medications and other code systems

There is no separate medication code list. dm+d products live in ordinary code lists, declared the same way and referenced by the same variable attributes, using the DMD, DMD INGREDIENT and BNF CHAPTER expressions.

<codeList>
<name>SGLT2_INHIBITORS</name>
<codeListDefinition>
<rules>
<inclusion>DMD 28775211000001102 |Canagliflozin 100mg tablets|</inclusion>
</rules>
</codeListDefinition>
</codeList>

<codeList>
<name>CARDIAC_DRUGS</name>
<codeListDefinition>
<rules>
<inclusion>BNF CHAPTER 5.1</inclusion>
</rules>
</codeListDefinition>
</codeList>

Because a code list is just a set of identifiers, nothing stops a single list mixing clinical concepts and dm+d products, and nothing checks that the codes in a list belong to the code system a variable expects. Matching the list to the variable is the author's responsibility: pass drug lists to productCode attributes and clinical lists to code attributes.

READ expressions exist for content migrated from CTV3 and resolve through the forward map to SNOMED CT concepts. Prefer native SNOMED CT expressions in new content.

Where code lists are used

Every attribute below takes one or more declared code list names, and each is resolved through the same mechanism.

VariableAttributeRestricts to
<codedEntryVariable>codeEntries with one of these codes
<codedEntryVariable>encounterTypeCodeEntries recorded in an encounter of one of these types
<codedEntryVariable>valueUnitCodeEntries whose value carries one of these units
<codedEntryVariable>bodySiteCodeEntries recorded against one of these body sites
<codedEntryVariable>routeCodeEntries recorded with one of these administration routes
<prescriptionVariable>productCodePrescriptions for one of these products
<prescriptionIssueVariable>productCodeIssues of one of these products
<prescriptionIssueVariable>quantityUnitCodeIssues whose quantity carries one of these units

Appointment variables do not take code lists.

Any of these attributes accepts a comma-separated list of names, and the union of those lists is used:

<codedEntryVariable name="VACCINATED_DAT" code="VACCINE_COD,VACCINE_DRUG_COD" .../>

Whitespace around each name is trimmed, so code="A_COD, B_COD" is fine. Each name must still be declared in <codeLists>.

Validation

The Resource Publishing API does not validate the patient query inside published content beyond XML well-formedness. Code list problems therefore surface when the query runs, not when you publish, and there is no partial success: a query whose code list fails to resolve fails outright rather than returning a smaller cohort.

The checks that do exist, and where they run:

ProblemDetected
<name> missing or not firstInternal report authoring only, via the XSD
No <rules> in an inline definitionInternal report authoring only
Inline definition with no <inclusion>Internal report authoring only
Unrecognised expressionAt query run time
Variable naming an undeclared code listAt query run time
Duplicate <name> in one queryNever, the last declaration wins
Code list declared but never usedInternal report authoring only, as a warning
Concept identifiers that do not existNever, they match nothing

The internal report authoring path applies one further restriction that the query engine itself does not: its expression check accepts only the hierarchy operators, ^, DMD and bare identifiers. BNF CHAPTER, READ and DMD INGREDIENT expressions run correctly but are rejected by that authoring check, so a code list using them cannot currently be built or edited in the report builder even though a published query may use it.