cqframework / clinical_quality_language

Clinical Quality Language (CQL) is an HL7 specification for the expression of clinical knowledge that can be used within both the Clinical Decision Support (CDS) and Clinical Quality Measurement (CQM) domains. This repository contains complementary tooling in support of that specification.
https://confluence.hl7.org/display/CDS/Clinical+Quality+Language
Apache License 2.0
253 stars 121 forks source link

CQL expression is not returning expected result. #1015

Open umeshprasad123 opened 4 years ago

umeshprasad123 commented 4 years ago

Following CQL expression suppose to return non-empty list, however it is returning an empty list.

define "Nonacute Inpatient Encounter Dates": [Procedure] P where IsCompleted(P) and GetStrippedCode(P.code) in "Nonacute Inpatient" and "Has Hypertension" and (date from P.performed."start" > "Hypertension Index Date" or (not "Hypertension Index Date In Year Prior to Measurement Period" and date from P.performed."start" = "Hypertension Index Date")) return date from P.performed."start"

Please note, the following functions evaluated against patient test case data, return results as expected, but not in the grouped expression as shown above.

define function IsCompleted(p Procedure): p.status is null or p.status = 'completed' define Separator: '-' define function ExtractCodeStr(CodeStr String): if PositionOf(Separator, CodeStr) > 0 then Substring(CodeStr, 0, PositionOf(Separator, CodeStr)) else CodeStr define function GetStrippedCode(C CodeableConcept): Code {code: ExtractCodeStr(C.coding[0].code), system: C.coding[0].system, display: C.coding[0]."display"}

c-schuler commented 4 years ago

Thank you @umeshprasad123 for raising this issue. Unfortunately, the description you have given is not very helpful to determine what the actual issue is. I would recommend splitting up the expression into smaller parts to determine where the failure surfaces.

For some general feedback, I would recommend using the DateTime comparison operators (https://cql.hl7.org/09-b-cqlreference.html#datetime-operators-2) instead of the general comparison operators to account for precision differences.

umeshprasad123 commented 4 years ago

Thanks Chris for your comment and suggestion! After doing further investigation, here is cause of the issue and also a resolution.

Issue description : In a CQL code if there are numerous expressions with same alias name e.g. 'P' in the attached example ( see CQL file : 'CQL_example- Has issue - returns_empty_list_for_expression_Remote_BP_Dates.cql' ) then correct expression alias is not picked up. Actually as per cql-engine code, some other previous expression is picked up from stack; see method 'internalEvaluate' in java file : QueryEvaluator.java. Please note, there is only one stack defined at Context level. i.e. there is only one stack being created for all expression operation evaluations. And if there are multiple same alias names then the oldest alias evaluation is picked from stack.

Issue : Expression "Remote BP Dates" returns an empty list

Fix : I was unable to push a 'Pull request'. Hence attaching code; see file QueryEvaluator.java.

I have also attached a work around solution CQL code. Idea here is to use different expression alias name ( see file : CQL_example - work_around_solution.cql )

After fixing CQL-Engine code or CQL code ( see file : CQL_example - work_around_solution.cql ), expression "Remote BP Dates" returns [2019-05-03]

I have also attached patient file for you to unit test code. issue_270.zip

Let me know if you need any additional information.

Thank you!

'Pull request' difference : diff --git a/cql-engine/src/main/java/org/opencds/cqf/cql/elm/execution/QueryEvaluator.java b/cql-engine/src/main/java/org/opencds/cqf/cql/elm/execution/QueryEvaluator.java index f5a1de2a..e8d968fc 100644 --- a/cql-engine/src/main/java/org/opencds/cqf/cql/elm/execution/QueryEvaluator.java +++ b/cql-engine/src/main/java/org/opencds/cqf/cql/elm/execution/QueryEvaluator.java @@ -155,6 +155,7 @@ public class QueryEvaluator extends org.cqframework.cql.elm.execution.Query { List result = new ArrayList<>(); boolean sourceIsList = false; int pushCount = 0;

  • context.pushWindow(); try { for (AliasedQuerySource source : this.getSource()) { QuerySource querySource = new QuerySource(source.getAlias(), source.getExpression().evaluate(context));@@ -202,6 +203,7 @@ public class QueryEvaluator extends org.cqframework.cql.elm.execution.Query { context.pop(); pushCount--; }
  • context.popWindow(); }

     if (this.getReturn() != null && this.getReturn().isDistinct()) {