Skip to content

CQL Engine v5#1755

Merged
antvaset merged 39 commits into
mainfrom
v5
Jun 26, 2026
Merged

CQL Engine v5#1755
antvaset merged 39 commits into
mainfrom
v5

Conversation

@antvaset

@antvaset antvaset commented May 1, 2026

Copy link
Copy Markdown
Contributor

This PR includes multiple changes set to be released in v5. The changes are further documented in the v4 to v5 migration guide included in the PR.

Changes to CQL value classes (#1775, ADR 005)

  • All non-null CQL values are represented in the engine as instances (implementors) of the sealed Value interface. Boolean, Integer, Long, Decimal, String, and List from the org.opencds.cqf.cql.engine.runtime package are used intead of kotlin.Boolean, kotlin.Int, kotlin.Long, BigDecimal, kotlin.String, and kotlin.collections.Iterable<kotlin.Any?>.
  • Value.toString() returns the CVL representation of the CQL value (see Value.toString() returns the CVL representation of the CQL value #1782).
  • The evaluation visitor, evaluators, engine API, FhirTypeConverter, and related tests are updated accordingly to use Value? instead of kotlin.Any?.

New ClassInstance class for CQL class instances, changes to ModelResolver, Java platform independence

  • Non-system structured types (e.g. instances of FHIR.Patient, FHIR.string, etc.) are now represented in the engine as ClassInstances (and not as e.g., HAPI FHIR structures). Informally, a ClassInstance is a structured value (Tuple) with a QName type tag like {http://hl7.org/fhir}Patient. As a result:
    • HAPI FHIR structures and other external classes do not bleed into the engine,
    • resolvePath(), as(), setValue(), objectEqual(), resolveType() are no longer needed in the ModelResolver interface.
  • Java Class<T> references are replaced with type specifiers and type QNames (both multiplatform) in the engine. As a result:
    • packageName(s) is no longer needed in ModelResolver and is(value: Any?, type: Class<*>?): Boolean? changed to is(valueType: String, type: QName): Boolean?,
    • type testing is improved so e.g. Interval[1,2] is Interval<Integer> returns true and Interval[1,2] is Interval<Time> returns false,
    • function resolution is more robust when function refs are compiled without signatures.

Additional changes, fixes, improvements

  • In FhirModelResolver : ModelResolver, the is(valueType: String, type: QName): Boolean? method now returns true only if {http://hl7.org/fhir}_valueType_ extends type or is the same as type. As a result, expressions like FHIR.integer { ... } is FHIR.positiveInt and FHIR.Quantity { ... } is FHIR.Age return false because FHIR.integer is a supertype of FHIR.positiveInt and FHIR.Quantity is a supertype of FHIR.Age.
  • JUnit and Hamcrest are replaced with kotlin.test in the majority of tests along the way.
  • The original Value object is renamed to Constants. The CqlList class is replaced with the SortHelper.compare() method.
  • DescendentsEvaluator logic is fixed along the way.

Enhancements for the engine JS target and CQL playground

  • Initial implementation of FHIR model resolver in JS with support for createInstance() and is() methods.
  • JS-export CQL value classes, CQL type classes.
  • Add type hints for expression results in the online playground.
image

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

Formatting check succeeded!

@antvaset antvaset mentioned this pull request May 1, 2026
@antvaset antvaset marked this pull request as draft May 1, 2026 08:22
Initial implementation of FHIR model resolver in JS. JS-export CQL value classes, CQL type classes. Type hints for expression results.
@codecov

codecov Bot commented May 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 49.33090% with 833 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.74%. Comparing base (e5bb97f) to head (da0a81b).

Files with missing lines Patch % Lines
...pencds/cqf/cql/engine/elm/executing/IsEvaluator.kt 16.17% 51 Missing and 6 partials ⚠️
.../cql/engine/elm/executing/HighBoundaryEvaluator.kt 0.00% 53 Missing ⚠️
...f/cql/engine/elm/executing/LowBoundaryEvaluator.kt 0.00% 52 Missing ⚠️
...f/cql/engine/elm/executing/FunctionRefEvaluator.kt 46.42% 33 Missing and 12 partials ⚠️
.../cqf/cql/engine/elm/executing/InstanceEvaluator.kt 15.21% 27 Missing and 12 partials ⚠️
.../cqf/cql/engine/elm/executing/PropertyEvaluator.kt 41.66% 21 Missing and 14 partials ⚠️
...cds/cqf/cql/engine/elm/executing/EqualEvaluator.kt 45.16% 19 Missing and 15 partials ⚠️
...qf/cql/engine/elm/executing/EquivalentEvaluator.kt 55.38% 17 Missing and 12 partials ⚠️
...cql/engine/fhir/converter/BaseFhirTypeConverter.kt 43.75% 1 Missing and 17 partials ⚠️
.../cqf/cql/engine/elm/executing/ChildrenEvaluator.kt 0.00% 18 Missing ⚠️
... and 119 more
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1755      +/-   ##
============================================
+ Coverage     68.55%   68.74%   +0.19%     
+ Complexity     1761     1699      -62     
============================================
  Files           532      536       +4     
  Lines         30836    30568     -268     
  Branches       6954     6990      +36     
============================================
- Hits          21139    21015     -124     
+ Misses         7213     6997     -216     
- Partials       2484     2556      +72     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

antvaset added 2 commits May 8, 2026 23:04
# Conflicts:
#	Src/java/elm-fhir/src/main/kotlin/org/cqframework/cql/elm/requirements/fhir/utilities/constants/CqfConstants.kt
#	Src/java/elm-fhir/src/test/kotlin/org/cqframework/cql/elm/requirements/fhir/DataRequirementsProcessorTest.kt
#	Src/java/engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/elm/executing/FlattenEvaluator.kt
#	Src/java/engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/elm/executing/QueryEvaluator.kt
#	Src/java/engine/src/jvmTest/kotlin/org/opencds/cqf/cql/engine/execution/ListOperatorsTest.kt
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
63.2% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@antvaset antvaset marked this pull request as ready for review June 3, 2026 20:49
antvaset added 5 commits June 5, 2026 08:09
# Conflicts:
#	Src/java/engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/elm/executing/DurationBetweenEvaluator.kt
…#1779)

* #1778 Ignore "id" elements when checking FHIR complex types for equivalence

* Run detekt
…1782)

#1781 `Value.toString()` returns the CVL representation of the CQL value
@antvaset antvaset changed the title CQL v5 CQL Engine v5 Jun 10, 2026
@github-actions

Copy link
Copy Markdown

Related Issues

The following open issues may be related to this PR:

Issue Title Score Matched Terms
#889 FhirModelResolver crashing on static inner classes for of FHIR entities e.g. DosageDoseAndRateComponent 14 "cql engine", type (path), java (path), src (path), terminology (path), opencds (path), engine (path), function (path), system (path), code (path), helper (path), context (path), fhir (path), library (path), main (path), org (path), cql (path), cqf (path), model (path), lib (path), hl7 (path), data (path), value (path)
#1430 Correctly support CQL to FHIR (and vice versa) type mapping 14 "cql engine", resources (path), type (path), cqframework (path), java (path), support (path), src (path), opencds (path), engine (path), code (path), context (path), fhir (path), converter (path), library (path), main (path), org (path), cql (path), cqf (path), evaluation (path), resource (path), hl7 (path), data (path), set (path)
#1133 ToList is not implemented according to the spec 13.5 "cql engine", type (path), cqframework (path), java (path), src (path), terminology (path), opencds (path), engine (path), code (path), retrieve (path), fhir (path), element (path), main (path), org (path), cql (path), cqf (path), elm (path), execution (path), hl7 (path), set (path), value (path), test (path)
#1103 Add an engine quick start to document plugging the engine in to a project 12.5 "cql engine", result (path), resources (path), java (path), src (path), opencds (path), engine (path), add (path), named (path), fhir (path), element (path), library (path), main (path), org (path), cql (path), cqf (path), resource (path), elm (path), execution (path), class (path)
#862 Attempting to perform inValueSet operation on value sets passed into a function throws an exception in the engine 12 "cql engine", java (path), evaluator (path), opencds (path), engine (path), function (path), system (path), code (path), context (path), fhir (path), library (path), org (path), cql (path), cqf (path), elm (path), execution (path), set (path), value (path), test (path)

Tip: If this PR addresses any of these issues, please link them using Closes #NNN or Refs #NNN in the PR description.

# Conflicts:
#	Src/java/engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/execution/EvaluationVisitor.kt
#	Src/java/engine/src/commonMain/kotlin/org/opencds/cqf/cql/engine/execution/State.kt
@c-schuler c-schuler self-requested a review June 16, 2026 18:49

@c-schuler c-schuler left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice work! I really only have some small nits. I noted one of them in the EquivalentEvaluator and another is below.

The EqualEvaluator, EquivalentEvaluator, the four comparison evaluators (Greater/Less/…), and the binary-arithmetic evaluators each repeat a long when/is ladder over the same set of Value subtypes, differing only in the operation. Adding a new Value type requires editing ~10+ files. The new sealed hierarchy is exactly the structure that would let this move to polymorphic methods on Value (e.g. compareTo, equalTo). Not a blocker, but the refactor stopped one level short of the payoff it sets up.

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
63.5% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@antvaset

Copy link
Copy Markdown
Contributor Author

The EqualEvaluator, EquivalentEvaluator, the four comparison evaluators (Greater/Less/…), and the binary-arithmetic evaluators each repeat a long when/is ladder over the same set of Value subtypes, differing only in the operation. Adding a new Value type requires editing ~10+ files. The new sealed hierarchy is exactly the structure that would let this move to polymorphic methods on Value (e.g. compareTo, equalTo). Not a blocker, but the refactor stopped one level short of the payoff it sets up.

Hi @c-schuler thank you! At this stage, I think it's OK to keep the Value interface and classes as simple and dumb and possible. This helps with the transition and going forward, may be beneficial because we have a clear separation of concerns in which Value packages the data and evaluators implement the CQL equality, CQL arithmetic, etc. logic on Value instances. I like the idea of removing duplicate code so can look separately into adding more helper and extension functions within the scope of org.opencds.cqf.cql.engine.elm.executing so they can be shared by multiple evaluators.

@raleigh-g-thompson raleigh-g-thompson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@antvaset antvaset merged commit aea363e into main Jun 26, 2026
9 of 10 checks passed
@antvaset antvaset deleted the v5 branch June 26, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants