FHIR Chat · Validating medication code in MedicationRequest · cql

Stream: cql

Topic: Validating medication code in MedicationRequest


view this post on Zulip Adam Rodriguez (Jan 23 2019 at 21:39):

@Bryn Rhodes , @Chris Moesel , et al.
Hello - I'm trying to determine how to validate a medication code within a MedicationRequest FHIR resource.

I'm getting the following error in CQL-runner tool (http://cql-runner.dataphoria.org/)
Member medicationCodeableConcept not found for type FHIR.MedicationRequest.

I have a patient , MedicationRequest, and value set resource created in Hapi-FHIR (http://hapi.fhir.org/) - links pasted below.
These resources were designed so that the code in the MedicationRequest will match the code in the value set.

http://hapi.fhir.org/baseDstu3/Patient/1232869/_history/1
http://hapi.fhir.org/baseDstu3/MedicationRequest/1232872/_history/1
http://hapi.fhir.org/baseDstu3/ValueSet/1232871/_history/2

This is the CQL I'm trying to run:

/*
Setup:
1. CQL Engine: http://cql-runner.dataphoria.org/
2. Config: Set 'Source Data' to 'http://hapi.fhir.org/baseDstu3'
3. Config: Set 'Terminology Service to 'http://hapi.fhir.org/baseDstu3'
3. Config: Set 'Patient Id to '1232869'
4. Run the library
*/

library CQLDemo version '1.0'

using FHIR version '3.0.0'

include FHIRHelpers version '3.0.0' called FHIRHelpers

codesystem "RxNorm": 'http://www.nlm.nih.gov/research/umls/rxnorm'

valueset "Medications": 'http://www.valuesettesting.com/vs/mr-sm'

context Patient

define "Medications Requested":
  [MedicationRequest] Meds
    where Meds.medicationCodeableConcept.coding[0] in "Medications"


define "No Medications Requested":
   not exists("Medications Requested")

Any help or guidance for how to properly write the "Medications Requested" retrieve so that I can avoid the error and validate the code would be greatly appreciated.

view this post on Zulip Chris Moesel (Jan 24 2019 at 02:11):

Hi @Adam Rodriguez. I only have a few minutes before I need to board a plane, but I think the issue is that in the STU3 model definition, MedicationRequest's medication property is reflected as a choice type with name medication -- so you can't access it a Meds.medicationCodeableConcept -- you must access it as Meds.medication and cast as necessary.

So in your example, I think you may be able to use one of these approaches (but I can't test them right now):

define "Medications Requested":
  [MedicationRequest] Meds
    where (Meds.medication as CodeableConcept).coding[0] in "Medications"

define "Medications Requested Alternate":
  [MedicationRequest: medication in "Medications"]

view this post on Zulip Adam Rodriguez (Jan 24 2019 at 05:13):

Hi @Chris Moesel , thank you for the quick response!

Though I don't fully understand the issue (I've got some studying to do) I went ahead tried both suggestions - here's what I got:

For the first solution, I get a new error message that seems to have an issue with the RxNorm code system:

>> Error [23:1]: HTTP 400 Bad Request: unable to find code system http://www.nlm.nih.gov/research/umls/rxnorm
>> Error [31:1]: HTTP 400 Bad Request: unable to find code system http://www.nlm.nih.gov/research/umls/rxnorm

For the second solution, it is error free, but the result is not as expected. I thought that I should be getting the MedicationRequest record to display as well as get a 'false' for the "No Medications Requested" existence check.

>> Medications Requested [28:1] []
>> No Medications Requested [31:1] true

Hope you had (or having) a safe flight and look forward to thoughts on where to go from here.

Very much appreciated.

view this post on Zulip Chris Moesel (Jan 24 2019 at 05:31):

@Adam Rodriguez - Well, I think different errors might be a sign of progress? At least we can hope so!

Regarding the issue with RxNorm, it sounds like maybe the HAPI server you're using as the terminology server doesn't support RxNorm. This query comes up empty: http://hapi.fhir.org/baseDstu3/CodeSystem?url=http://www.nlm.nih.gov/research/umls/rxnorm

That said, I'm not familiar enough with how cql-runner works to know why it's querying for the code system at all and what it's doing with it. I think you're going to have to check with @Bryn Rhodes on the details of cql-runner.

Regarding the fact that you're getting nothing back with the second approach -- I actually wasn't sure if that one would work or not. The CQL grammar doesn't give us a way to cast the medication property when we use it inside a retrieve, so I wasn't sure how the cql-runner engine would handle it. In theory, the context of how the property is used in the retrieve should be enough to hint that it's a code -- but I haven't confirmed that it works in practice. Again, that's getting more to the details of the cql-runner and Java engine, so you may need to ask @Bryn Rhodes about that.

view this post on Zulip Bryn Rhodes (Jan 25 2019 at 14:07):

@Adam Rodriguez , @Chris Moesel is correct, it's trying to $expand the "Medications" value set, which HAPI reports can't be done without the RxNorm code system. Looking at the value sets currently on that server, there are actually 3 with that url: http://hapi.fhir.org/baseDstu3/ValueSet?url=http%3A%2F%2Fwww.valuesettesting.com%2Fvs%2Fmr-sm, so it's not clear in that case what the underlying HAPI server will do with the request. Note that for choices, the translator will automatically cast when the appropriate type can be inferred by the use. For example:

view this post on Zulip Bryn Rhodes (Jan 25 2019 at 14:11):

define "Medications Requested":
  [MedicationRequest] Meds where Meds.medication in "Medications"

This is valid CQL, the choice is resolved as a concept, which has a membership testing operator for value sets. My guess though is that depending on how HAPI is resolving that value set URL, you get one of the 3 value sets.

view this post on Zulip Adam Rodriguez (Feb 27 2019 at 22:03):

Just want to let you know that the following structure did in fact work for another experimental case.

Thank you @Chris Moesel and @Bryn Rhodes again for all your help!

define "Medications Requested":
[MedicationRequest] Meds
where (Meds.medication as CodeableConcept).coding[0] in "Medications"

view this post on Zulip Adam Rodriguez (Apr 10 2019 at 15:53):

Hello again @Chris Moesel , @Bryn Rhodes ,

I'm very appreciative of the assistance I've received on this topic. I'm revisiting this type of query and wondering if either of you could point me in the right direction to reference a literature source as to where this query structure is explained - specifically the last line of the following segment where you can indicate "...as CodeableConcept).coding[0]...":

define "Medications Requested": [MedicationRequest] Meds where (Meds.medication as CodeableConcept).coding[0] in "Medications"

I'm not sure if I would have ever figured this out on my own without your support.

view this post on Zulip Chris Moesel (Apr 10 2019 at 16:03):

It's kind of mentioned here: https://cql.hl7.org/03-developersguide.html#choice-types -- although that implies that perhaps the cast actually was not necessary?

Documentation on casting in general is here:https://cql.hl7.org/03-developersguide.html#casting and here: https://cql.hl7.org/09-b-cqlreference.html#as.

Neither of those talk about it specifically in regard to the where clause, but a cast can pretty much be used where ever it might be needed.

If you're looking for more documentation on queries, they're introduced here: https://cql.hl7.org/02-authorsguide.html#queries

More advanced topics regarding queries start here: https://cql.hl7.org/03-developersguide.html#introducing-context-in-queries

And the query evaluation section can also be helpful for understand how queries ought to work: https://cql.hl7.org/05-languagesemantics.html#query-evaluation

view this post on Zulip Bryn Rhodes (Apr 10 2019 at 17:31):

Hi @Adam Rodriguez , the specification discusses casting here and the indexer here, but the fact that that's how those are represented in the FHIR model info is documentation that we are still working on.

view this post on Zulip Bryn Rhodes (Apr 10 2019 at 17:31):

Whoops, I didn't see Chris' response before I posted this, his is much better, thank you Chris!

view this post on Zulip Chris Moesel (Apr 10 2019 at 19:12):

Actually, I made a mistake copy/pasting links in my original post, but I fixed it now.

view this post on Zulip Adam Rodriguez (May 30 2019 at 18:20):

Hi @Chris Moesel , @Bryn Rhodes

Thank you for all the posted resources. I don't know why I never noticed before, but at the bottom of the FHIR STU3 Medication Request resource page there exists a 'Search Parameters' section - example: https://www.hl7.org/fhir/STU3/medicationrequest.html

For the 'code' parameter in the MedicationRequest FHIR STU3 resource, it specifies 'MedicationRequest.medication.as(CodeableConcept)' as its search expression.

Can this expression be directly used for a CQL retrieve? As in:

define "Medications Requested": [MedicationRequest] Meds where Meds.medication.as(CodeableConcept) in "Medications"

In comparison, the following retrieve had worked for me previously:

define "Medications Requested": [MedicationRequest] Meds where (Meds.medication as CodeableConcept).coding[0] in "Medications"

For a beginner, it's difficult for me to determine whether or not these are equivalent expressions or whether both should be valid.

So, maybe another way to frame my new inquiry is:

If I am using FHIR version 3.0.0 for a given CQL library artifact, are the search expressions provided in a given FHIR resource directly applicable for a CQL retrieve/query?

view this post on Zulip Chris Moesel (May 30 2019 at 20:00):

Hi @Adam Rodriguez!

If I am using FHIR version 3.0.0 for a given CQL library artifact, are the search expressions provided in a given FHIR resource directly applicable for a CQL retrieve/query?

The expressions you see in the search parameter documentation (such as MedicationRequest.medication.as(CodeableConcept)) are expressed using FHIRPath. The FHIRPath specification actually has a section on using FHIRPath in CQL: http://hl7.org/fhirpath/#use-of-fhirpath-in-clinical-quality-language-cql

So... at a basic level, CQL does support using FHIRPath expressions as described in that section -- which means that the expressions you see in the search parameter documentation should be valid. That said, some organizations/programs/style-guides may prohibit or discourage CQL authors from using some of these FHIRPath features in clinical logic, and the CQL-to-ELM translator can be configured to disallow them. In addition, CQL code may look inconsistent if it contains a mixture of "traditional" CQL operators and "method-style invocation" -- so authors should also keep that in mind.

For a beginner, it's difficult for me to determine whether or not these are equivalent expressions or whether both should be valid.

The CQL specification has an appendix that actually shows FHIRPath functions and their equivalent "traditional" CQL representations: https://cql.hl7.org/16-i-fhirpathtranslation.html

view this post on Zulip Adam Rodriguez (May 31 2019 at 01:44):

@Chris Moesel Thank you so much for the quick response!

Ok, here's where I'm at now...

In the first link you recently provided (http://hl7.org/fhirpath/#use-of-fhirpath-in-clinical-quality-language-cql) I think I understand in general why Patient.telecom.use can equate to Patient.telecom X where X.use is not null return X.use.

However, I think I failed to understand how that can be used to understand how the FHIR Path MedicationRequest.medication.as(CodeableConcept) equates to MedicationRequest.medication as CodeableConcept).coding[0] when trying to follow the Patient.telecom.use example. I think the part that is throwing me off is how the portion .as(CodeableConcept) gets to as CodeableConcept).coding[0].

Additionally, after browsing through the second link (https://cql.hl7.org/16-i-fhirpathtranslation.html) my immediate reaction was to search for .as(), which I didn't see. So I think I'm missing the point here too. Again, my failure.

For what it's worth, in that same first link, I reviewed polymorphism in FHIR (http://hl7.org/fhirpath/#polymorphism-in-fhir) and choice types (which you directed me to before) and I think something clicked. I think after seeing the example of (Observation.value as Quantity).unit and reviewing the Observation resource structure I can now see why MedicationRequest.medication as CodeableConcept).coding[0] works.

A few more follow up questions if that's ok:

1. I think the last portion of MedicationRequest.medication as CodeableConcept).coding[0] that I am trying to understand is the addition of the [0] appending to .coding. Is this necessary for 'strict' CQL evaluation?
2. Based on what I shared above about the FHIR path translations, I'm not too confident that I would be able to directly translate a FHIR path without referencing the resource structure (especially for my example FHIR path: MedicationRequest.medication.as(CodeableConcept)). So with that, can I reliably use what I think I now understand about writing retrieves based on the structure itself (not the FHIR path) in all cases that assume a CQL-to-ELM translator is not configured to disallow for some reason?
3. Are either of the following retrieves valid?
define "Medications Requested": [MedicationRequest.medication as CodeableConcept).coding[0] : "Medications"] MedicationsRequested

Or

define "Medications Requested": [MedicationRequest.medication.as(CodeableConcept).coding[0]: "Medications"] MedicationsRequested

Of course, if you think there's another way that might help me get FHIR translations through my thick skull that'd be appreciated as well. It'd be nice to have the confidence that I can take a FHIR path alone and convert that to a retrieve/query.

I very much appreciate your continued help and patience!

view this post on Zulip Chris Moesel (May 31 2019 at 03:23):

In regard to your question about how the Patient.telecom.use example leads to MedicationRequest.medication.as(CodeableConcept) -- it doesn't! Path traversal is not the same as method-style invocation, so you're not missing something here -- you're right. I was directing you to the whole section, but the part that addresses the .as() (in broad terms) is the subsection "C.4. Method-style Invocation" -- which basically says that you can use these method-style invocations in CQL (although it doesn't enumerate all the possibilities).

Additionally, after browsing through the second link (https://cql.hl7.org/16-i-fhirpathtranslation.html) my immediate reaction was to search for .as(), which I didn't see. So I think I'm missing the point here too. Again, my failure.

You're not missing the point. It appears that .as() may have been omitted from the FHIRPath translations documentation by mistake. As it turns out, as() is listed in the most recently balloted version of the spec, available here: https://cql.hl7.org/2019May/16-i-fhirpathtranslation.html#as

To answer your follow-up questions:

1. I think the last portion of MedicationRequest.medication as CodeableConcept).coding[0] that I am trying to understand is the addition of the [0] appending to .coding. Is this necessary for 'strict' CQL evaluation?

The .coding[0] indicates that you only want to check the first coding in the CodeableConcept. It's not strictly necessary and is, in fact, not the usual approach. You actually had .coding[0] in your very first example when you started this thread, so I may have assumed it was intentional and you wanted to only check the first coding. If that's not the case, you can drop .coding[0] altogether, so you're left with something that looks a lot more like that search expression you found -- and when you check for membership in a value set, it will look for a match on any of the codings in the CodeableConcept.

2. ... So with that, can I reliably use what I think I now understand about writing retrieves based on the structure itself (not the FHIR path) in all cases that assume a CQL-to-ELM translator is not configured to disallow for some reason?

I'm not sure I understand the question, but perhaps the answer to #1 above helps with this? In short, however, the FHIRPath does still rely on (and relate to) the structure of the thing the path is on -- but it works a little different due to path traversal (e.g., it's less strict about singular vs. plural types). As for choices, what you see as medication[x] in the FHIR spec (or serialized to medicationCodeableConcept or medicationReference in JSON instances) is represented in CQL as a choice, with the root name (e.g., medication for medication[x], value for value[x]) as the property name (at least in FHIR 3+; FHIR DSTU2 is different). If you really want to see the exact data model the CQL is operating against, you should take a look at the modelinfo XMLs here: https://github.com/cqframework/clinical_quality_language/tree/master/Src/java/quick/src/main/resources/org/hl7/fhir

3. 3. Are either of the following retrieves valid? ...

No. The part on the left-hand side of the : must refer only to a type -- the type you want to retrieve. It cannot contain a path. The part on the right-hand side of the : can be a value set (which is most common) or code (somewhat common), but also may optionally include a path indicating what property of the type should be checked against the value set / code. That said, there are rules about what is and is not supported in that path -- and based on CQL 1.3, .as() would not be allowed within the retrieve. In 1.4 we are more formally defining that path as a strict subset of FHIRPath -- but I'm still not sure .as() will be allowed in that context. @Bryn Rhodes?

For completeness sake, I'll also mention that the right-hand side of : can also contain date-based filters, but that's not really relevant to this specific use case. Still, for a better explanation of retrieves, check out this section in the spec:
https://cql.hl7.org/02-authorsguide.html#retrieve

I hope that helps!

view this post on Zulip Adam Rodriguez (Jun 04 2019 at 03:56):

@Chris Moesel This is indeed very helpful as always.

In the effort to bring all this together, I'll try to design a new set of questions.

Context: I'm interested in writing CQL to investigate data structured in FHIR (as opposed to QDM).

Specifically in this case, validating a code in a MedicationRequest resource using a value set (I have other questions outside of this particular resource and I'll start a new topic on that).

I think I now learned that the left side of a retrieve expression must refer to a type, not a path.

1. Are these the same 'types' that are found in this logical table? (https://www.hl7.org/fhir/STU3/formats.html#table). Or, maybe these data types? (https://www.hl7.org/fhir/STU3/datatypes.html)
I realize that my second inquiry in my last response could be way clearer - my apologies. What I'm ultimately trying to establish here is a standard way of writing retrieves before considering whether a CQL-to-ELM translator is configured to disable any particular style. Though, I can see why that may not be a best practice.

So, back to my given context and case, it seems that one way I can validate a code in a MedicationRequest resource is by looking down the medicationCodeableConcept path. The FHIR path expression for this is MedicationRequest.medication.as(CodeableConcept).

Using the FHIR path translator guide (https://cql.hl7.org/2019May/16-i-fhirpathtranslation.html#as), where it indicates that for .as(), X.as(T) === X as T, then MedicationRequest.medication.as(CodeableConcept) should translate to (MedicationRequest.medication as CodeableConcept).

2. Would it be safe if I assumed that using the FHIR path can be my starting point for writing any retrieve in general, and can the following retrieve become my "go-to" standard in this case when using the FHIR path expression?
define "Medications Requested": [MedicationRequest] Meds where (Meds.medication as CodeableConcept) in "Medications"

Note in the above retrieve I've now also omitted the .coding[0] since I was not intentionally only checking the first coding as you pointed out (thank you for catching that!).

I am still intrigued by some of the retrieve examples that look like they are based on the QDM data model. For example:

[Condition: severity in "Acute Severity"]

and

[Condition: "Acute Pharyngitis"] C where C.onsetDateTime during MeasurementPeriod

3. Is this simplicity achieved because the QDM data model seems to have some sort of primary, or default, code attribute for which the value set is compared against?
4. If, in my case, the context is using FHIR, as opposed to QDM, does the FHIR structure have an analogous primary or default "attribute" such that a retrieve can be written like those examples. For example, if I wrote the FHIR retrieve at the medicationRequest level would it default to looking for codes with a path of type CodeableConcept?
In contrast, notice how my question above about establishing a "go-to" retrieve format incorporates a value set in the where clause.

Again, I am very appreciative of your continued time and guidance.

view this post on Zulip Bryn Rhodes (Jun 04 2019 at 04:56):

Hi @Adam Rodriguez:

1. No, the model that CQL uses for FHIR selects the Resources as the retrievable types. See the FHIR Model Info for the detailed description, but basically, any Resource type can appear as the focus of a retrieve. So [Encounter] or [Condition], but not [string] or [HumanName].
2. Not really, since FHIRPath doesn't have retrieves or queries, only path traversal and manipulation operators. We've recently written a Developer's Introduction to CQL and would love any input/feedback you might have. Hopefully it is helpful.
3. Yes, exactly, each type specified in the Model Info specifies a "primary code path" that determines which element to use for terminology filtering if no code path is specified in the retrieve. For QDM, this is trivial because all the types have a code element.
4. In FHIR, we have selected a reasonable default (which you can see in the primaryCodePath attribute in the FHIR model info above), but not all the resources have a reasonable default. When there is no primaryCodePath specified in the model info, the translator will require that the retrieve specify a code path.

view this post on Zulip Bryn Rhodes (Jun 04 2019 at 04:58):

And based on the context of your question, I would recommend that you have a look at the examples in the FHIR Quality Measure IG, there are several fully worked examples of QDM-based measures that have been expressed in FHIR-based CQL. And of course, we would love any feedback you have on the IG and the examples.

view this post on Zulip Bryn Rhodes (Jun 04 2019 at 05:05):

And to answer your question from earlier @Chris Moesel , no, the simple subset will only allow identifiers, qualifiers, and indexers with literal arguments. I don't think we would ever need an as within the path anyway, because the terminology expression is already required to evaluate to a terminology data type (code, concept, or list of code or concept), and so the data access layer would need to select the code-valued option of a choice element anyway.

view this post on Zulip Adam Rodriguez (Jun 04 2019 at 07:16):

Hello @Bryn Rhodes ,

I took a look at the FHIR model (which is a deep dive for someone like me) and wondering if line 5128 is what I should be referring to when thinking about types and primarycodepaths:
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="MedicationRequest" retrievable="true" primaryCodePath="medication" baseType="FHIR.DomainResource">

Taking a stab at this, is the implication here that the below retrieve is valid for the medicationRequest resource and utilizes the primary code path?

Define "Medication Requested": [MedicationRequest: "medicationValueSet"]

Or possibly:

Define "Medication Requested": [MedicationRequest: medicationCodeableConcept in "medicationValueSet"]

Or, did I just take two steps back here because medication is a choice type?

Similarly, looking at line 7453:
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="Condition" retrievable="true" primaryCodePath="code" baseType="FHIR.DomainResource">

With code noted above as being the primary code path for the condition resource, would the below be a valid retrieve?

Define "Patient Condition": [Condition: "conditionValueSet"]

Or, possibly:

Define "Patient Condition": [Condition: code in "conditionValueSet"]

Again, what I'm really trying to figure out is establishing a baseline standard for writing retrieves in FHIR - initially my guess was that it would be either first looking at the FHIR path, or the FHIR resource structure itself and just explicitly choosing a path to a CodeableConcept.

Since it sounds like the starting point is not to refer to the FHIR path for this purpose, I'll start digging into the Developer's Introduction to CQL link you provided. But now this begs the question for me, which is: If the FHIR path is not ideal for drafting CQL retrieves, what is the primary benefit of the FHIR path as a search parameter being referenced for in each FHIR resource section?

Hopefully I'm making some progress here.

view this post on Zulip Adam Rodriguez (Jun 04 2019 at 08:23):

@Bryn Rhodes By the way, I just ran into the following example from:
http://cql-runner.dataphoria.org/

define "Active Ambulatory Opioid Rx": [MedicationRequest: "Ambulatory Abuse Potential Opioids"] Rx where Rx.status.value = 'active' and Rx.category.coding[0] ~ "Outpatient"

So, hopefully that means one of my examples is valid?

that is...

Define "Medication Requested": [MedicationRequest: "medicationValueSet"]

Having your confirmation on this would put me at ease.

view this post on Zulip Chris Moesel (Jun 06 2019 at 21:43):

Hi @Adam Rodriguez ,

I apologize for the delay in responding. It's been quite the week! I expect Bryn would say the same -- except every week is probably like that for him!

I'm just going to start from your oldest message and go down from there...

First up: translating the QDM logic for a diagnosis (condition) overlapping an encounter.

Your "Patient Encounter" expression works, but would be more succinct (and potentially more efficient) if you used this form:

define "Patient Encounter":
  [Encounter: class in "encounterValueSet"]

The reason it would be potentially be more efficient is because this form puts the code filter into the retrieve statement -- allowing the data layer to potentially make a more efficient query. Otherwise, depending on implementation, it might query for all encounters and then apply the filter in memory.

Similarly, your "Patient Diagnosis" expression should work as-is, but would be better as:

define “Patient Diagnosis”:
  [Condition: "conditionValueSet"]

In this case, since code is identified as the primaryCodePath in the modelinfo file here, we don't even need to specify the property name code in the retrieve.

As for "Encounter with Diagnosis", I don't think that what you suggested is valid CQL. In particular, I don't think that overlaps between is a valid combination of keywords. In addition, since I think you're using FHIR STU3, you can't use onsetDateTime and abatementDateTime; you need to use (onset as DateTime) and (abatement as DateTime).

Unfortunately, what you're trying to do actually gets a little tricky because of a few issues at play:

1. In FHIR, the onset and abatement might be stored as dateTime, but they also could be stored as a date range, an age, and age range, or a simple string. On top of that, abatement has the added possibility of being stored as a boolean to indicate abatement without a specific date or age.
2. In CQL, (onset as DateTime) and (abatement as DateTime) will return null if the data is stored in a non-DateTime format. So it may look like there is no onset or no abatement when there really is; it's just not stored as a DateTime.
3. Ideally you could convert these separate properties to an Interval in order to check the overlap (e.g., Interval[onset as DateTime, abatement as DateTime]) -- but that has it's own problems due to #2 above, and the fact that CQL interprets a closed null boundary as ongoing. So if abatement was stored as a boolean true, trying to construct an interval using abatement as DateTime would result in a closed null boundary and be interpreted as ongoing -- which is exactly the opposite of what we want.
A full solution would try to convert the onset and abatement into an interval in a meaningful and robust way; which would include:

  • converting ages and age ranges to DateTimes (based on the Patient.birthDate)
  • interpreting strings as an open null boundary (which essentially means "I don't know the date")
  • interpreting true abatementBoolean as an open null boundary (again, meaning, "I don't know the date")
  • interpreting false abatement as Boolean as a closed null boundary (meaning it hasn't abated, so ongoing)
  • deciding how to deal with onset or abatement when they are null

That's fairly complex, so I'll leave that as an exercise to the reader (or @Bryn Rhodes if he feels so inclined)! That said, if you know your environment, and you know that onset and abatement will always be DateTimes and never any of the other types, then it's quite a bit easier. If you want me to work out that example for you, let me know.

view this post on Zulip Chris Moesel (Jun 06 2019 at 21:58):

Next up: the modelinfo and primaryCodePath

Yes, you found the relevant line (for MedicationRequest) in the modelinfo XML file, and your interpretation is correct. The expression below is essentially filtering MedicationRequests down to only those for which the medication value (as a CodeableConcet) is in the "medicationValueSet".

define "Medication Requested":
  [MedicationRequest: "medicationValueSet"]

Specifying [MedicationRequest: medicationCodeableConcept in "medicationValueSet"], however, would not be valid. As you noted, medication is a choice type; you still refer to it as only medication. You don't have to do medication as CodeableConcept here because the CQL spec indicates that only concepts are valid in this context, so the CQL-to-ELM translator figures it out. In fact, based on the grammar, you couldn't use medication as CodeableConcept here even if you wanted to!

On to your next example: yes, [Condition: "conditionValueSet"] is the preferred way to filter Conditions on the code property. While [Condition: code in "conditionValueSet"] is valid, and equivalent, it's unnecessary -- unless you just really like the explicitness of it (which I can understand).

Again, what I'm really trying to figure out is establishing a baseline standard for writing retrieves in FHIR

I usually (a) determine the correct resource for what I want to query, (b) determine the path to the code I want to filter on, usually by looking at the resource definition in FHIR, and (c) check the modelinfo to see if it is the primaryCodePath or not. In step (b) you have to remember that if the FHIR property name ends in [x], it is a choice, and you drop the [x] to get the property name in CQL.

If the FHIR path is not ideal for drafting CQL retrieves, what is the primary benefit of the FHIR path as a search parameter being referenced for in each FHIR resource section?

The FHIR specification uses FHIRPath to be explicit about what the search parameter matches against in the resource. It's there for FHIR Server implementors to know how to implement the search parameter and for FHIR consumers to better understand what the search parameter means. It's not really intended to be useful to CQL at all -- it's about defining the actual HTTP search functionality on a FHIR server.

view this post on Zulip Chris Moesel (Jun 06 2019 at 22:01):

And last:

So, hopefully that means one of my examples is valid?
that is...
Define "Medication Requested":
[MedicationRequest: "medicationValueSet"]
Having your confirmation on this would put me at ease.

Yep, it's valid! (Well, aside from Define vs. define).

view this post on Zulip Adam Rodriguez (Jun 11 2019 at 19:16):

@Chris Moesel Thank you for the thorough and thoughtful guidance! No apologies necessary at all! I know you're an incredibly valuable resource and I'm grateful for your time.

Regarding the dates, I'll try to better understand the environment at hand and see how that affects the CQL design.

Regarding the retrieves, I think things are starting to really click over here. And to (hopefully) verify that, I'd just like to also confirm whether or not the following retrieve is also valid in my attempt to incorporate what I think I now understand about making an efficient query (by putting the code filter in the retrieve statement) and handling choice types (by simply dropping the [x]).

define "Medication Requested": [MedicationRequest: medication in "medicationValueSet"]

If the above is all good, then I think I'll feel more confident and can move on to more advanced topics.

view this post on Zulip Chris Moesel (Jun 11 2019 at 19:26):

Hi @Adam Rodriguez. Yes, your query above is absolutely valid. That said, since medication is defined as the primaryCodePath for MedicationRequest in the modelinfo (here), your query can be shortened to:

define "Medication Requested":
  [MedicationRequest: "medicationValueSet"]

That said, the shortened form is completely optional and 100% equivalent to the longer form you typed above.


Last updated: Apr 12 2022 at 19:14 UTC