Convert your FHIR JSON -> XML and back here. The CDA Book is sometimes listed for Kindle here and it is also SHIPPING from Amazon! See here for Errata.

Monday, October 31, 2011

Declarative vs. Procedural and QueryHealth

A couple of weeks ago and the S&I Framework Face to Face, the Query Health Technical workgroup was asked to pick an implementation to focus on.  I liked hQuery, but one of its challenges is that its queries are defined procedurally (in JavaScript).  In chatting with Marc Hadley, one of the developers, it became pretty clear that they are using "template" programming to generate the procedural code.

In my update on Query Health from the Face to Face Rich Elmore asks:
Would be interested in your thoughts on the differences between procedural and declarative approaches as outlined in the hQuery Summer Concert presentation (see charts 15 - 16 )
One of the tasks I took on last week was to start looking at how to implement the query declaratively, using the HQMF format.  The slides that Rich is referring to compare this JavaScript:

function population(patient) {
  return (patient.age(start)>=64);

To this chunk of (reported to be) HQMF:

<entry typeCode="DRIV">
  <observation classCode="OBS" moodCode="EVN.CRT"

    <id root="4AAEF95D-DCC6-459C-839C-C820DF310D60"/>
    <code code="ASSERTION" codeSystem="2.16.840.1.113883.5.4"/>
    <value xsi:type="CD" code="IPP"
      codeSystemName="HL7 Observation Value"

      displayName="Initial Patient Population"/>
    <sourceOf typeCode="PRCN">
      <conjunctionCode code="AND"/>
        <act classCode="ACT" moodCode="EVN"

          <templateId root="2.16.840.1.113883.3.560.1.25"/>
          <id root="52A541D7-9C22-4633-8AEC-389611894672"/>
          <code code="45970-1" displayName="Demographics"
          <sourceOf typeCode="COMP">
            <observation classCode="OBS"
              moodCode="EVN" isCriterionInd="true">

              <code code="2.16.840.1.113883.3.464.0001.14"
                displayName="birth date HL7 Code List"/>
              <title>Patient characteristic: birth date</title>
              <sourceOf typeCode="SBS">
                <pauseQuantity xsi:type="IVL_PQ">
                  <low value="64" unit="a" inclusive="true"/>
                <observation classCode="OBS" moodCode="EVN">
                  <title>Measurement period</title>

Admittedly, the XML is hideously ugly.  I took my own crack at generating it because frankly, I could barely understand what the above said.  And for good reason, because it doesn't actually follow HQMF.  For one, the data criteria go into the "Data Criteria Section".  

This is an example of the data criteria that the patient is 64 years old or older:
<observation classCode="OBS" moodCode="EVN.CRT">
  <id root="42e2aef0-73c4-11de-8a39-0800200c9a66"/>
  <code code="424144002" codeSystem="2.16.840.1.113883.6.96" 
  <value xsi:type="IVL_PQ">
    <low value="64" unit="a" inclusive="true"/>
  <participant typeCode="SBJ">
    <role classCode="PAT"/>

That's actually pretty easy (for me) to interpret, but could be a little easier (or perhaps the right word is "greener").  I borrowed some of the Green C32 Schema for result and recast it with a slightly different set of assumptions:

  <resultID root="42e2aef0-73c4-11de-8a39-0800200c9a66"/>
  <resultType code="424144002" codeSystem="2.16.840.1.113883.6.96" displayName="Age"/>
    <physicalQuantityInterval unit="a" low="64" />

Hmm, arguably not much better, and I don't see the patient referent.

Another thing that bugs me in the XML is that the identifiers for the data criteria are useless for content creators (they use OIDs or GUIDs, which are impossible to remember).  If you create a criterion for patients over 64 years of age in one place, and want to reference it from somewhere else WITHIN the same XML, ideally, you'd use ID and IDREF with meaningful names.

In the above example, it might appear as:

<resultCriteria ID='patientsOlderThan64'>
  <resultType code="424144002"
codeSystem="2.16.840.1.113883.6.96" displayName="Age"/>
    <physicalQuantityInterval unit="a" low="64" />

I also looked at the same set using the S&I Framework Clinical Information Model and SQL.  Here is a sample query that selects all the PatientInformation rows that qualify:

SELECT * FROM PatientInformation WHERE DATEADD(Year, 1, DateOfBirth) < GETDATE()

OK, so I cheated a little. I used the date of birth and computed the age. This is what most systems would probably need to do anyway [Note: One alternative for this one would be for the system to pre-compute a "virtual" observation of the age based on the patient's birth date.]

I'm still not satisfied with any of these declarative formats.  The SQL isn't quite right.  It selects from a table of patient Information, when what I really want is a table of patient identifiers.  The HQMF is still a little too dense.

And the procedural stuff won't fly in the real world, simply because it requires a particular implementation technology (JavaScript) that won't be readily applicable to all systems.  I know that there are JavaScript implementations for just about everything, but procedural is just the wrong way to go here.  It doesn't optimize for the ways that different systems want to work unless they all happen to use map/reduce, which isn't the case.

I'll have to spend more time on this tomorrow looking at it from a different perspective.