1.22.6 Terminology Service
This specification includes support for the provision of a consumer level terminology service - that is, a
service that lets healthcare applications make use of codes and value sets without having to become
experts in the fine details of the value set resource, and the underlying code systems.
A server that supports all the functionality described here can be described as a "FHIR Terminology Service",
and SHALL conform to this conformance statement: [link to be provided].
1.22.6.1 Open issues to be addressed
- improve the definitions of mapping equivalence, and change "inexact" map to "overlap"
1.22.6.2 Security
Generally, SSL SHOULD be used for all production health care data exchange.
Even though terminology servers do not directly handle patient information,
hackers may still be able to infer information about patients by observing
the codes and concepts that the terminology service is asked about, so
encryption is still recommended.
A terminology server may choose not to authenticate the clients/users in any fashion,
but can do so in order to limit or account for usage.
For a value set maintenance server that allows terminologies to be edited,
some form of authorization and/or authentication would be appropriate.
This specification does not require any particular approach to security.
Note to reviewers/implementers: can we? it would increase interoperability if we could bind
to a particular approach. If we said, for the operations, SSL+server certs
mandatory, use client certificates if you want to authenticate the clients,
and to use Smart on FHIR for editing value sets and concept maps, is there
any problems with that?
1.22.6.3 Basic Concepts
A FHIR terminology service is a simply a set of functions built on the definitions
provided by a set of ValueSet and
ConceptMap resources, with additional inherently known terminologies providing support.
The terminology service builds on the basic principles for using terminologies
in FHIR. Implementers should be familiar with:
- Using codes in FHIR
- The ValueSet resource
- The ConceptMap resource
In addition, implementers should be familiar with the operations
framework. Further useful information may be found in:
- Underlying Principles: V3 Core Principles
- SNOMED CT technical documentation. Note; "namespace" is used differentyl here from the way it is used by IHTSDO
1.22.6.3.1 External Code Systems
In order to be used with a value set, a code must be defined somewhere. They can be defined as part of
an inline code system definition, or they can be defined elsewhere,
and then used in a value set by quoting the correct namespace. The FHIR specification defines a set of
namespaces for commonly encountered code systems, and defines how some work with FHIR (e.g.
SNOMED-CT, LOINC, RxNorm).
These code systems are often large, and have many internally defined properties that are part of their
formal definitions. Inline code systems in Value Set resources are not an appropriate way to define
these code systems; FHIR does not provide a formal representation at all. Instead, these terminologies
provide their own distribution formats, and it is assumed that they are externally known to the
terminology server.
Most useful terminology servers will make one or more of these external code systems available for use
within the value sets that they manage. The list of additional terminologies that a terminology server supports
beyond those defined in its value sets is published to clients by (still to figure out how this works).
A server SHOULD publish the additional code systems that it supports through intrinsic knowledge
using the http://hl7.org/fhir/StructureDefinition/conformance-common-supported-system extension:
{ "resourceType" : "Conformance", "extension" : [ { "url" : "http://hl7.org/fhir/StructureDefinition/supported-system", "valueUri" : "http://loinc.org" }] }
This extension is added to the root Conformance Statement.
1.22.6.3.2 Implementation Note
When a terminology server exposes an external code system, it makes a set of services available
internally that serve the operational interfaces below. The internal server depends on the following
logical information for a terminology:
- its URL (namespace, and how versioning works)
- what codes are valid what properties can be used to select codes what implicit value sets exist
The FHIR specification itself defines these things for common terminologies (including
SNOMED-CT, LOINC, RxNorm),
and provides the Value Set infrastructure for supporting typical relatively simple small code systems.
Implementers Note: Implementers interested in working with existing published terminologies for
which the Value Set infrastructure is not suitable should discuss their needs with HL7 to get the
list above extended.
Note: A terminology service may choose to expose additional external code system specific
related functionality such as exploration, or structured search, but these services are
outside the scope of the FHIR terminology service.
1.22.6.3.3 Terminology Maintenance
The terminology service uses the value set resources defined on the system - both
the implicit ones associated with the external code systems and those explicitly
available at the /ValueSet endpoint - to serve the operational interface defined below.
As value sets are created, updated or deleted, the outcomes of the operational services
change. A terminology server should validate incoming resources, and ensure integrity
of the terminology services. Typically, servers would provide a test and production
environment, but there is no explicit notion of this in the interface itself.
1.22.6.4 Value Set Expansion
A value set describes a set of rules for what codes or concepts are considered to be in the value set.
These rules might be simple (e.g. a direct list of codes from a specified version of a code system), or
they might be quite complex (e.g. all codes with a particular property from an unspecified version of
a code system).
A FHIR enabled application can simply ask the server to figure out all the details, and return it a list
of the current codes in the value set. This is known as "expanding" the valueset.
As a summary, the client passes the server the following information:
- the value set (either by its URL on the RESTful interface, by its logical identifier (ValueSet.url), or directly as a parameter to the call)
- (Optionally) a text filter to use to restrict the codes that are returned (e.g. user input text). It is left to server discretion to choose how to apply the text filter
- (Optionally) a date at which the expansion should be evaluated (usually, this is the current date/time, but there are circumstances where that is not appropriate)
The server returns a value set that contains the current list of codes that meet the filter criteria (or an
OperationOutcome with an error if the expansion fails).
Note that some value sets expand to many thousands of codes, or even an infinite number,
and for these, the server SHOULD return an error code
too-costly. In these cases the client can try again with a more specific text filter
to reduce the number of codes returned.
For further information, consult the definition of the operation.
Note to reviewers/implementers: Open Issue for connectathons: is paging support required for the iterating through an expansion?
Some example uses for the expansion operation:
- get a list of codes to display in a User interface (e.g. a drop down interface)
- a variation on this is to offer the user a text box to type in. As the user types, call the expand operation to provide the user with a list of matching codes/concepts (like a browser search)* fetch a list of codes to use when generating software programming instructions
- get a list of codes so that software can check whether a code is valid or not in a particular context
Examples
Expanding a value set that is already registered on the server as "23", with a text filter of "abdo" :
GET [base]/ValueSet/23/$expand?filter=abdo
Expanding a value set that is specififed by the client (using JSON):
POST [base]/ValueSet/23/$expand
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "valueSet",
"resource" : {
"resourceType" : "ValueSet",
[value set details]
}
}
]
}
The server responds with a value set (this example in XML):
HTTP/1.1 200 OK
[other headers]
<ValueSet xmlns="http://hl7.org/fhir">
<!– the server SHOULD populate the id with a newly created UUID
so clients can easily track a particular expansion –>
<id value="43770626-f685-4ba8-8d66-fb63e674c467"/>
<!– no need for meta, though it is allowed for security labels, profiles –>
<!– other value set details –>
<expansion>
<!– when expanded –>
<timestamp value="20141203T08:50:00+11:00"/>
<contains>
<!– expansion contents –>
</contains>
</expansion>
</ValueSet>
1.22.6.5 Concept Lookup
A system can ask a terminology server to return a set of information about a particular system/code
combination using the lookup operation. The server returns information for both display and processing
purposes. The client passes the server the following information:
- the code value (either a code+system, or a Coding data type
- (Optionally) a date at which the code information should be returned (usually, this is the current date/time, but there are circumstances where that is not appropriate)
The server returns the following information:
- A human description of the system
- A recommended display for the code* Whether the code is abstract or not
- Other designations for the code (a value, optionally with language and/or a use code)
The recommended display to the code is a text representation of the code
that the terminology server recommends as the default choice to show
to the user, though a client may choose out of the other designations if it has reason to.
Examples
Looking up a code/system :
GET [base]/ValueSet/$lookup?system=http://loinc.org&code=1963-8
Lookup using a Coding (this example in XML):
POST [base]/ValueSet/$lookup
[other headers]
<Parameters xmlns="http://hl7.org/fhir">
<parameter>
<name value="coding"/>
<valueCoding>
<system value="http://loinc.org"/>
<code value="1963-8"/>
</valueCoding>
</parameter>
</Parameters>
The server responds with a set of information (JSON this time):
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "name",
"valueString" : "LOINC"
},
{
"name" : "version",
"valueString" : "2.48"
},
{
"name" : "designation",
"valueString" : "Bicarbonate [Moles/volume] in Serum"
},
{
"name" : "abstract",
"valueString" : "false"
},
{
"name" : "designation",
"part" : [
{
"name" : "value",
"valueString" : "Bicarbonate [Moles/volume] in Serum "
}
]
}
]
}
1.22.6.6 Value Set Validation
One of the ways to determine whether a code is in a value set is to expand the value set
(as described above), and then look at the returned codes to see if the code is in the expansion.
However this is not an efficient way to test whether a code is valid, and for some value sets
(e.g. with infinite number of members), it cannot work. Instead, a FHIR terminology server
provides a "validate" operation. The client passes the server the following information:
- the value set (either by its URL on the RESTful interface, by its logical identifier (ValueSet.url), or directly as a parameter to the call)
- the code value (either a code+system, a Coding data type, or a CodeableConcept
- (Optionally) a date at which the expansion should be evaluated (usually, this is the current date/time, but there are circumstances where that is not appropriate)
The server returns a true/false indicating whether the code/concept is valid, and a list
of errors and warnings associated with it. The server should also return an appropriate
display for the concept for use in a UI context.
Note that if the server is passed a CodeableConcept, the server is able to check
whether any of the codes are valid against the value set, and also check whether
multiple codings are allowed and/or the codings provided are consistent with each other.
Every code system has an implicit value set that is "all the concepts defined
in the code system". For some code systems, these value set URIs are defined
in advance (e.g. for LOINC, it’s http://loinc.org/vs
).
However for some code systems, they are not known. Clients can refer to these implicit
value sets by providing the URI for the code system itself.
Examples
Simple validation of a code/system against a known value set:
GET [base]/ValueSet/23/$validate?system=http://loinc.org&code=1963-8&display=test
Validate a CodeableConcept against a client specified value set (this example in JSON):
POST [base]/ValueSet/$validate
[other headers]
{
"ResourceType" : "Parameters",
"parameter" : [
{
"name" : "coding",
"valueCodeableConcept" : {
"coding" : {
"system" : "http://loinc.org",
"code" : "1963-8",
"display" : "test"
}
}
},
{
"name" : "valueSet",
"resource": {
"resourceType" : "ValueSet",
[etc]
}
}
]
}
The server responds with validation information (JSON this time):
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "result",
"valueBoolean" : "false"
},
{
"name" : "message",
"valueString" : "The display \"test\" is incorrect"
},
{
"name" : "display",
"valueString" : "Bicarbonate [Moles/volume] in Serum"
}
]
}
1.22.6.7 Subsumption testing
The Expand and Validate operations can be used to perform subsumption testing. To test whether
code A subsumes code B, perform a validate specifying a value set built of all the
codes that are subsumed by code A, and test whether code B is subsumed by it.
Note that a server is allowed (and SHOULD, but is not required to) consider concept maps when
doing subsumption testing. E.g. if A is a LOINC code, and it has a precise mapping to a
SNOMED CT code that subsumes B, with an appropriate scope, then the server can indicate
that this it is true that LOINC code A subsumes SNOMED CT code B.
In order to make it convenient to perform this subsumption testing, code systems that define
subsumption hierarchies should define simple URLs to express a value set that includes all the
codes subsumed by a code.
Examples
Test whether a Snomed Concept 399211009 (History of myocardial infarction) is subsumed by 22298006 (Myocardial infarction):
GET [base]/ValueSet/$validate?system=http://snomed.info/sct&code=X&uri=http://hl7.org/fhir/ValueSet/snomedct?base=Y
Server response:
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "result",
"valueBoolean" : "false"
},
]
}
1.22.6.8 Batch Validation
It’s also possible to validate a set of concepts against their relevant value
sets in a single batch operation, to support high-volume process and reduce network
latency effects. In this case, the client passes the server the following information:
- a series of pairs of parameters, where each pair contains
- a CodeableConcept containing the concept to be validated
- a uri referring to the value set against which the code should be validated
- (Optionally) a date at which the expansion should be evaluated (usually, this is the current date/time, but there are circumstances where that is not appropriate)
Code system URIs may be used to refer to the implicit value set that includes
all the codes in the code system (as described for the validation operation).
The server returns a series of parameters, one for each submitted item, each of
which has a result, a message, and a display (same as for the $validate operation).
This operation could be used, for instance, to validate a set of codes in a
CDA document, by converting from the CDA CD data type to CodeableConcepts.
Examples
A request to validate 2 concepts from a CDA document, with OIDs for value set identifiers:
POST [base]/ValueSet/$batch
[other headers]
{
"ResourceType" : "Parameters",
"parameter" : [
{
"name" : "item",
"part" : [
{
"name" : "concept",
"valueCodeableConcept" : {
"system" : "http://loinc.org",
"code" : "2324-4"
},
}
"name" : "uri",
"valueUri" : "urn:oid:1.2.3.4.6"
]
},
{
"name" : "item",
"part" : [
{
"name" : "concept",
"valueCodeableConcept" : {
"system" : "http://snomed.info/sct",
"code" : "22298006"
},
}
"name" : "uri",
"valueUri" : "urn:oid:1.2.3.4.7"
]
}
]
}
The server responds with a series of validation outcomes (JSON this time):
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "item",
"part" : [
{
"name" : "result",
"valueBoolean" : "false"
},
{
"name" : "message",
"valueString" : "’2324-4’ is not a valid LOINC code"
}
]
},
{
"name" : "item",
"part" : [
{
"name" : "result",
"valueBoolean" : "false"
},
{
"name" : "message",
"valueString" : "The concept is not in the specified value set (\"Organisms\")"
},
{
"name" : "display",
"valueString" : "Myocardial infarction"
}
]
}]
}
1.22.6.9 Translations
A client can ask a server to translate a concept from one value set to another. Typically, this
is used to translate between code systems (e.g. from LOINC to SNOMED CT, or from a v3 code to
a v2 code). The client calls the translate operation and passes the following parameters:
- A code+system, Coding, or CodeableConcept
- A Concept Map to use for the translation
- The value set for the context of the source* The value set for the destination
The client passes either a concept map, or the value sets for the source and destination context.
If there is no concept map, then the server may determine the appropriate map to use from
context provided in the value sets.
If there is no particular context, the appropriate value sets would be the value sets for the
entire coding system at question (e.g. from http://snomed.info/sct to http://loinc.org/vs).
The server performs the translation as it is able based on the concept maps that it knows about.
If no single mapping can be determined then the
server returns an error. Some servers may require a concept map to use for the translation.
Example
Translate from FHIR Composition status to V3 Act Status (based on
this defined concept map:
GET [base]/ConceptMap/$validate?system=http://hl7.org/fhir/composition-status
&code=preliminary&valueSet= http://hl7.org/fhir/vs/composition-status
&target=http://hl7.org/fhir/v3/vs/ActStatus
The server responds with validation information:
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "result",
"valueBoolean" : "true"
},
{
"name" : "outcome",
"valueCoding" : {
"system" : "http://hl7.org/fhir/v3/ActStatus",
"code" : "active",
}
}
]
}
1.22.6.10 Batch Translation
It’s also possible to translate a set of concepts in a single batch operation, to support high-volume process and reduce network
latency effects. In this case, the client passes the server the following information:
- a series of parameter tuples, where each pair contains
- a CodeableConcept containing the concept to be translated
- a uri that identifies the target value set into which to translate the concept
- (optiona) A reference to a particular map to use for the translation
The server returns a series of parameters, one for each submitted item, each of
which has a result, a message, and an outcome (same as for the $translate operation).
Examples
A request to translate 2 concepts from a CDA document, with OIDs for value set identifiers:
POST [base]/ConceptMap/$batch
[other headers]
{
"ResourceType" : "Parameters",
"parameter" : [
{
"name" : "item",
"part" : [
{
"name" : "concept",
"valueCodeableConcept" : {
"system" : "http://loinc.org",
"code" : "2324-4"
},
}
"name" : "target",
"valueUri" : "urn:oid:1.2.3.4.6"
]
},
{
"name" : "item",
"part" : [
{
"name" : "concept",
"valueCodeableConcept" : {
"system" : "http://snomed.info/sct",
"code" : "22298006"
},
}
"name" : "target",
"valueUri" : "urn:oid:1.2.3.4.7"
]
}
]
}
The server responds with a series of validation outcomes:
HTTP/1.1 200 OK
[other headers]
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "item",
"part" : [
{
"name" : "result",
"valueBoolean" : "false"
},
{
"name" : "message",
"valueString" : "’2324-4’ is not a valid LOINC code"
}
]
},
{
"name" : "item",
"part" : [
{
"name" : "result",
"valueBoolean" : "false"
},
{
"name" : "outcome",
"valueCodeableConcept" : {
"coding" : {
"system" : "http://example.com/codesystems/example",
"code" : "xxxx"
}
}
}
]
}]
}
1.22.6.11 Maintaining a Closure Table
The 4 operations Expand, Lookup, Validate, and Translate account for most operational requirements
associated with terminology use. However there is one difficult but important use case that
they do not address, which is integrating terminologically based logic into application search.
A typical example of this is a user that wants to find any observations for male patients over
the age of 50 who attended a particular clinic within a particular 2 week period, with a
diagnosis of gout, and who had an elevated serum creatinine.
In this case, both "diagnosis of gout" and "serum creatinine" involve valueset and/or subsumption queries
(e.g. against SNOMED CT and LOINC respectively). This search has to be executed by some
logical processing engine that knows how to find this data in a given persistence store.
Often, this is some kind of SQL query, though many other technological choices are available.
However this is done, the challenge with an operation like this is to integrate the
terminological knowledge with search execution based on the other relationships the criteria is expressed.
On approach to this problem would be to using the expand operation above, so that the system executing
the search could generate expansions, and then search for these expansions. This has a couple of problems:
- the list of subsumed codes could be very long, and the search operation becomes correspondingly inefficient
- the expansion of the subsumption might not be closed, and so the search operation can’t be correct
An alternative approach is to generate a subsumption closure table,
which lists all the possible relationships, and allows for rapid execution of these kind of queries. However this has other problems:
- the subsumption table can be very large (>500000 records for SNOMED CT), even though very few of the codes are used
- subsumption tables are generally built up front, and don’t deal with new codes as they are encountered very well
- they still don’t offer a solution for non-closed expansions
This is the main reason why most systems don’t support post-coordination or other forms
of coded expressions.
In FHIR, this problem is solved by building a closure table on the fly, as new codes are seen.
This technique leaves the FHIR terminology server responsible for the terminological reasoning,
and the client responsible for the closure table maintenance. To the client, it doesn’t matter
whether the concept is post-coordinated or not. Here’s a description of how the process works:
- The client defines a name associated with a particular context in which it wishes to maintain a subsumption based closure table.
- The client registers this name with the FHIR Terminology server using the $closure operation (described below), with only one parameter, the name of the context
- any time the client system encounters a new Coding that isn’t entered in the closure table, it calls the $closure operation with the context name, and the Coding value it has encountered
the server returns a ConceptMap resource with a list of new entries (code : system -> code : system) that the client should add to its closure table
* the server can indicate that entries should be removed from the table by providing a (code : system -> code : system) with equivalence "unmatched" (though it's not known why that would be needed)
- The client makes these entries into its closure table6. to facilitate the initialization process, a server can call $closure with multiple Coding values
The $closure operation takes 2 parameters:
- Closure table context name
- Coding to enter into the table (0 or more - 0 codings is a request to (re-)initialise the table)
The operation returns a concept map which has a list of mappings that represent new entries to make in the closure table.
The closure table can be resynchronized by passing an additional "version" parameter, which is a value taken from the
version in one of the delta responses. This is a request to replay all the mapping changes since that delta was sent.
Example
The client sees a new SNOMED CT concept "22298006" in a data element it is tracking as "patient-problems":
POST [base]/$closure
{
"resourceType" : "Parameters",
"parameter" : [
{
"name" : "name",
"valueId : "patient-problems"
},
{
"name" : "concept",
"valueCoding" : {
"system" : "http://snomed.info/sct",
"code" : "22298006",
}
}
]
}
The server responds with a set of new entries to add to the concept map.
It provides one important piece of metadata - the version, which the client
can use to resynchronize the closure table:
HTTP/1.1 200 OK
[other headers]
{
"resourceType": "ConceptMap",
"identifier": "49088976-d54d-4d19-b868-3d4c18cebabb",
"version": "8",
"status": "active",
"experimental": true,
"date": "2012-06-13",
"element": [
{
"codeSystem": "http://snomed.info/sct",
"code": "22298006",
"map": [
{
"codeSystem": "http://snomed.info/sct",
"code": "128599005",
"equivalence": "narrower"
}
]
},
]
}
Notes:
- In this simple example, the addition of a single new SNOMED CT concept created one existing subsumption relationship. In general clinical use, more than one relationship would be expected
- For the purposes of the closure table, the relationships are considered to be bi-directional; e.g. if A subsumes B, then B is subsumed by A
- As well as entering codes that are actually used, the client also enters search terms into the closure table
- The combination of the system and code is the key to the closure table; if the server encounters two different codes that have the same meaning (e.g. syntactical variation), it should create an "equals" relationship between them
1.22.6.12 Functional Operations
In order to support terminology operations in FHIR a minimal set of terminology operations would be necessary. These operations are a sub set of the available terminology service operations defined in the [http://www.omg.org/spec/CTS2/1.0/ Common Terminology Services - Release 2 (CTS2) specification] and can be categorized as:
- Administrative Operations
- Search/Query Operations
- Authoring/Maintenance Operations
Functional operations within these categories support the access and management of terminology objects such as Code Systems, Concepts, Value Sets and Concept Mappings. The functional operations necessary to support a FHIR terminology service are outlined here.
1.22.6.12.1 Administrative Operations
- Be able to load a standard or local code system
1.22.6.12.2 Search/Query Operations
Concepts
- Retrieve the concept details (display name, qualifiers, associations, etc.) for a given code/code system
- Return possible concept matches based on search criteria* Validate whether a code is valid within a given code system (content)
- Retrieve a list of codes (for example, to populate a user interface)
Return the decedents of a given concept
Code System
Retrieve the metadata for a code system
Value Set
Retrieve the metadata for a value set
- Return a value set based on search criteria
- Determine if a code is valid in a value set
Generate the Value set Expansion from the Value Set Definition.
Mapping
Retrieve the metadata for map set
- Retrieve a translation (mapping) of concept(s) from a given source code system to target concept(s) from a target code system
1.22.6.12.3 Authoring/Maintenance Operations
Concepts
Maintain a closure table
Value Set
Create/editing a value set
Mapping
Translate (map) from a source code system to a target code system