Tuesday, November 1, 2011

Value Sets and QueryHealth

One of the challenges in Query Health is that a particular measure may reference a number of coded values.  These are known in HL7 parlance as "Value Sets".  For example, the TCNY Quality Measures (pdf) mentioned on the Query Health "Query and Data Analysis" page references a number of codes (which they call data elements).

Here is an example from it:

Eligible
encounters
(CPT codes)
99201; 99202; 99203; 99204; 99205; 99211; 99212; 99213; 99214; 99215; 99241; 99242;
99243; 99244; 99245; 99354; 99355; 99385; 99386; 99387; 99395; 99396; 99397; 99401;
99402; 99403; 99404; 99406; 99407

OK, so we have this list of codes that we need to access.  There are a couple of different ways to encode this value set in the query.  

In HQMF this could be written as separate criteria on encounters composed with "OR" between them.

<sourceOf typeCode="PRCN">
  <conjunctionCode code="OR"/>
  <encounter>
    ...
    <code code='99201' codeSystem='2.16.840.1.113883.6.12'/>
  </encounter>
</sourceOf>
<sourceOf typeCode="PRCN">

  <conjunctionCode code="OR"/>
  <encounter>
    ...
    <code code='99202' codeSystem='2.16.840.1.113883.6.12'/>
  </encounter>
</sourceOf>

That's not terribly pretty, or reusable (except by copy and paste).  HQMF does talk about how to specify a value set, but it is knarly.  Essentially you use a code from some code system that identifies the value set (even though every other HL7 V3 message knows that value sets are identified through OIDs).  I won't even show that, if you want an example, look at HQMF.

The next way to approach it is by using the valueSet attribute in the code.  The reason that HQMF doesn't suggest this is because someone in M&M thinks that is a bad idea.  We'll be revisiting this in the next revision of HQMF in a project that is currently being scoped by HL7 Structured Documents.  Here is how I would propose representing the value set.

<sourceOf typeCode="PRCN">
  <encounter>
    ...
    <code valueSet='2.16.840.1.113883.19.1091'/>
  </encounter>
</sourceOf>

The problem with this approach is that you've only referenced the value set, and the system that needs to process the query must then figure out how to dereference it.

There are at least two different ways to handle that.  One is to use a terminology service as defined by the HL7 Common Terminology Service specification.  For what Query Health needs, this is like swatting a fly with a sledgehammer.  The IHE Sharing Value Sets (SVS) profile is a more appropriate tool for swatting this fly.  This profile supports both SOAP and HTTP bindings (there are great uses for the HTTP binding in Schematron).


This is an example URL to retrieve a value set:
https://example.com/RetrieveValueSet?id=1.2.840.10008.6.1.308

And here is a sample of what it would return:

<RetrieveValueSetResponse xmlns="urn:ihe:iti:svs:2008" cacheExpirationHint="2008-08-15T00:00:00-05:00">
  <ValueSet id="1.2.840.10008.6.1.308
    displayName="Common Anatomic Regions Context ID 4031" version="20061023">
    <ConceptList xml:lang="en-US">
      <Concept code="T-D4000" displayName="Abdomen"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="R-FAB57" displayName="Abdomen and Pelvis"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-15420" displayName="Acromioclavicular joint"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-15750" displayName="Ankle joint"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-280A0" displayName="Apex of Lung"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-D8200" displayName="Arm"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-60610" displayName="Bile Duct"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-74000" displayName="Bladder"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-04000" displayName="Breast"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-26000" displayName="Bronchus"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-12770" displayName="Calcaneus"
        codeSystem="2.16.840.1.113883.6.5"/>
      <Concept code="T-11501" displayName="Cervical spine"
        codeSystem="2.16.840.1.113883.6.5"/>
      </ConceptList>
  </ValueSet>
</RetrieveValueSetResponse>


Now, there are some really nice features of using SVS for value sets.  If you want to put this value set into a SQL table, you can simply transform the result using XSLT:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:svs="urn:ihe:iti:svs:2008"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:template match="/">
        <xsl:apply-templates select=".//svs:ConceptList"/>
    </xsl:template>
    <xsl:template match="svs:ConceptList">
        <xsl:text>INSERT INTO VALUESETMEMBERS (VALUESETID, CODE, CODESYSTEMID, DISPLAYNAME)&#x0A;</xsl:text>
        <xsl:apply-templates select="svs:Concept"></xsl:apply-templates>
    </xsl:template>
    <xsl:template match="svs:Concept">
        <xsl:text>VALUES ('</xsl:text>
        <xsl:value-of select="../../@id"/>
        <xsl:text>', '</xsl:text>
        <xsl:value-of select="@code"/>
        <xsl:text>', '</xsl:text>
        <xsl:value-of select="@codeSystem"/>
        <xsl:text>', '</xsl:text>
        <xsl:value-of select="@displayName"/>
        <xsl:text>')&#x0A;</xsl:text>
    </xsl:template>
</xsl:stylesheet>


Another easy to write stylesheet would create JSON objects containing the members of the value set (I'll leave that as an exercise for the reader.

You can also access the value-set using the XPath document function:

document("https://example.com/RetrieveValueSet?id=1.2.840.10008.6.1.308")

The value of this expression is an XML Document from which you can do other selections.  It's a pretty handy trick for validation testing (which is why ITI put it in).  If you assume that $myValueSet is an XPath variable containing that document, and $code is another variable containing a code value, then $myValueSet//svs:Concept[@code=$code] is true if the value in code matches one of the codes in the document describing the value set [Note: I omitted but would in a production environment also test for the appropriate codeSystem match].

So, for dealing with value sets in Query Health, if we wind up with an XML representation of the query, I would suggest that they use the IHE SVS mechanism for referencing the value sets. Value sets are reusable resources.  There are a lot of different quality measures out there that would benefit from using similar definitions for ED visit, patient with a diagnosis of diabetes, HbA1c result, et cetera.

My first recommendation is to consider using the IHE SVS schema to represent a value set, and the HTTP binding to retrieve them for use in the query.  The reason for doing so is that it supports any of the previously mentioned mechanisms for representing the query, independently of the technology or standards that the query uses.  If queries are expressed in JavaScript, the SVS profile can generate objects that can be used to access the content in JSON format, if in SQL, the same, if in XPath, the same, and if in XML using HQMF, they use the same HL7 V3 data types to represent the content.  That sounds like a win/win/win/win solution.

     Keith

1 comment:

  1. Hi Keith,
    One comment: I agree that SVS is a reasonable choice, but it's reasonable primarily because IHE SVS is built upon and is therefore a small subset of CTS. So it essentially just allows you to use "only what you need" of the (well constructed IMHO) CTS sledgehammer. Of note, you then still need a terminology service, just a simpler one.

    Granted I'm biased here, but a terminology service--as you know--is the only game in town to support a process that will have any chance of being maintained.

    ReplyDelete