Skip to content

Intervals of Integer, Decimal and Time are returned as CQL text instead of FHIR.Range / FHIR.Period #1832

Description

@bryantaustin13

Intervals of Integer, Decimal and Time are returned as CQL text instead of FHIR.Range / FHIR.Period

Repository: cqframework/clinical_quality_language
Related: child of #1430 (Correctly support CQL to FHIR (and vice versa) type mapping).
Overlaps #1744 and clinical-reasoning #1090, both of which show this in their example
responses but are filed as wrong-value bugs. See "Relationship to existing issues" below.

Summary

Evaluating an expression that returns Interval<System.Integer>, Interval<System.Decimal> or
Interval<System.Time> via $cql produces a valueString containing CQL source text, marked with
a cqf-cqlText extension:

{
  "name": "return",
  "valueString": "Interval[1, 10]",
  "_valueString": {
    "extension": [
      { "url": "http://hl7.org/fhir/StructureDefinition/cqf-cqlText", "valueBoolean": true }
    ]
  }
}

Per Conformance Requirement 4.3 — FHIR Type Mapping these should be returned as
FHIR.Range and FHIR.Period respectively. Two things are wrong:

  1. The wrong FHIR type is used. A rendered CQL string is returned instead of the mapped type.
  2. The required cqf-cqlType extension is absent. The IG states that all CQL-valued
    parameters and results SHALL include a cqf-cqlType extension to unambiguously specify the
    type
    , and that in the absence of a cqf-cqlType extension, values are assumed to be the FHIR
    type
    . So a conformant consumer must read the above as a genuine FHIR.string whose value
    happens to be "Interval[1, 10]".

Additionally, http://hl7.org/fhir/StructureDefinition/cqf-cqlText is not a registered
extension
— the canonical url returns HTTP 404, unlike the registered cqf-cqlType. Nothing can
be expected to interpret it.

Expected mapping

CQL type Expected FHIR type
Interval<System.Integer> FHIR.Range
Interval<System.Long> FHIR.Range
Interval<System.Decimal> FHIR.Range (with quantity-precision extension)
Interval<System.Quantity> FHIR.Range
Interval<System.Date> FHIR.Period
Interval<System.DateTime> FHIR.Period
Interval<System.Time> FHIR.Period (time values as dateTime with an @0001-01-01 date)

The engine already does this correctly for two of the seven

This is an incomplete implementation rather than a representation gap — the engine emits the
mapped FHIR type for Date and Quantity intervals, and falls back to CQL text for the rest.

Expression Returned Conformant
Interval[1, 10] valueString + cqf-cqlText no — expected Range
Interval[1.0, 10.0] valueString + cqf-cqlText no — expected Range
Interval[@T01:00:00.000, @T02:00:00.000] valueString + cqf-cqlText no — expected Period
Interval[@2012-01-01, @2012-12-31] valuePeriod yes
Interval[1 'mg', 10 'mg'] valueRange yes

Verbatim, from HAPI FHIR 8.10.0 hosting the CQF engine (cqlEngineVersion 4.1.0):

// Interval[@2012-01-01, @2012-12-31]  -- correct
{ "name": "return",
  "valuePeriod": { "start": "2012-01-01T00:00:00-07:00", "end": "2012-12-31T00:00:00-07:00" } }

// Interval[1 'mg', 10 'mg']  -- correct
{ "name": "return",
  "valueRange": { "low":  { "value": 1,  "unit": "mg", "system": "http://unitsofmeasure.org", "code": "mg" },
                  "high": { "value": 10, "unit": "mg", "system": "http://unitsofmeasure.org", "code": "mg" } } }

// Interval[1, 10]  -- incorrect
{ "name": "return",
  "valueString": "Interval[1, 10]",
  "_valueString": { "extension": [
    { "url": "http://hl7.org/fhir/StructureDefinition/cqf-cqlText", "valueBoolean": true } ] } }

Note that Range.low/Range.high are Quantity, and a Quantity may carry a bare value with no
unit — so an integer or decimal interval is representable without inventing anything.

Impact

Running the cql-tests suite against this engine, 47 test results are returned this way. Every
one of them is an Interval, and all 47 currently fail:

Test file Count
CqlIntervalOperatorsTest 40
CqlDateTimeOperatorsTest 5
CqlTypesTest 2

cqf-cqlType was present on 0 of 47.

Of the 47, 41 fail only because of the representation — the value itself is correct, but a
consumer comparing a Range/Period against a returned string cannot match it. The remaining 6
have a genuine value defect as well (the uncertainty low-bound issue in #1724, and expand/
collapse returning only the first interval, #1744), so they would still fail after this is fixed.

To reproduce

Run the following against cql-tests-runner. The first three tests fail; the last two pass,
demonstrating that the mapping is implemented for some interval point types and not others.

<?xml version="1.0" encoding="utf-8"?>
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://hl7.org/fhirpath/tests" xsi:schemaLocation="http://hl7.org/fhirpath/tests ../../testSchema/testSchema.xsd"
	   name="CqlIntervalOperatorsTest" reference="https://hl7.org/fhir/uv/cql/3.0.0-202609-ballot/en/conformance.html#fhir-type-mapping" version="1.0">
	<capability code="interval-operators"/>
	<group name="IntervalFhirTypeMapping" version="1.0">
		<!-- Interval<System.Integer> SHALL be returned as FHIR.Range. Returns a valueString
		     of "Interval[1, 10]" carrying the unregistered cqf-cqlText extension. -->
		<test name="IntegerIntervalAsRange" version="1.0">
			<expression>Interval[1, 10]</expression>
			<output>Interval[1, 10]</output>
		</test>
		<!-- Interval<System.Decimal> SHALL be returned as FHIR.Range. -->
		<test name="DecimalIntervalAsRange" version="1.0">
			<expression>Interval[1.0, 10.0]</expression>
			<output>Interval[1.0, 10.0]</output>
		</test>
		<!-- Interval<System.Time> SHALL be returned as FHIR.Period. -->
		<test name="TimeIntervalAsPeriod" version="1.0">
			<expression>Interval[@T01:00:00.000, @T02:00:00.000]</expression>
			<output>Interval[@T01:00:00.000, @T02:00:00.000]</output>
		</test>
		<!-- Already correct: returned as valuePeriod. -->
		<test name="DateIntervalAsPeriod" version="1.0">
			<expression>Interval[@2012-01-01, @2012-12-31]</expression>
			<output>Interval[@2012-01-01, @2012-12-31]</output>
		</test>
		<!-- Already correct: returned as valueRange. -->
		<test name="QuantityIntervalAsRange" version="1.0">
			<expression>Interval[1 'mg', 10 'mg']</expression>
			<output>Interval[1 'mg', 10 'mg']</output>
		</test>
	</group>
</tests>

Or directly, without the runner:

curl -s -X POST http://localhost:8080/fhir/'$cql' \
  -H 'Content-Type: application/json' \
  -d '{"resourceType":"Parameters","parameter":[{"name":"expression","valueString":"Interval[1, 10]"}]}'

Suggested fix

In FhirTypeConverter (Src/java/engine-fhir/src/main/java/org/opencds/cqf/cql/engine/fhir/converter/FhirTypeConverter.java),
extend interval conversion to cover the Integer, Long, Decimal and Time point types alongside the
existing Date/DateTime and Quantity handling, and attach cqf-cqlType to every CQL-valued result.
Once intervals are returned as Range/Period, cqf-cqlText should no longer be emitted for them.
#1430 notes this converter predates the current IG, which is consistent with what is observed here.

Relationship to existing issues

Environment

  • CQL engine: Java CQFramework Engine 4.1.0, hosted by HAPI FHIR Server 8.10.0 (FHIR 4.0.1)
  • Operation: system-level $cql
  • CQL version: 1.5
  • Observed with cql-tests-runner against the full cql-tests suite (1809 tests executed)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions