FHIRPath
FHIRPath is a language, similar to XPath, that allows you to navigate an HL7® FHIR® resource to evaluate and extract data from its fields using a straightforward syntax that includes paths, functions, and operations. For example, you could evaluate whether the given name of a Patient contained a value: Patient.name.given.empty(). Or you could extract the value of the Patient resource’s telecom field, but only if offical is the value of its use field: Patient.telecom.where(use = 'official').
In FHIRPath, expressions are collection-based. Each function works on one input collection and each binary operator operates on two input collections, and the values returned by the expression are gathered into an output collection. Some functions and operations place constraints on the size of their input collections.
For complete details about FHIRPath including how to build an expression, see the HL7 FHIRPath specificationOpens in a new tab. InterSystems supports a subset of the functions and operations that are defined in the specification.
Workflow
With InterSystems technology, the process of using FHIRPath to evaluate and extract data from a resource is straightforward:
The following sections provide details about each step in the workflow.
Instantiate HS.FHIRPath.API
The process of using FHIRPath to evaluate and extract data from a resource begins with calling HS.FHIRPath.API.getInstance()Opens in a new tab. When you call this method, you must specify the FHIR package that corresponds to a version of FHIR. For example, if the resources you are evaluating conform to FHIR R4, the corresponding package ID is currently hl7.fhir.r4.core@4.0.1. In this case, instantiating HS.FHIRPath.APIOpens in a new tab would look like:
set fhirPathAPI = ##class(HS.FHIRPath.API).getInstance($lb("hl7.fhir.r4.core@4.0.1"))
You can obtain the IDs of the currently loaded packages using the Management Portal or ObjectScript:
-
Management Portal — Navigate to Home > Health > MyFHIRNamespace > FHIR Configuration, and select the Package Configuration card. The package ID is obtained by appending the @ symbol and version number to the name of the package. For example, the ID of the following package is hl7.fhir.r4.core@4.0.1:
-
ObjectScript — To list package IDs programmatically, see Listing Available Packages.
The HS.FHIRPath.APIOpens in a new tab object includes the methods used to parse FHIRPath expressions and evaluate resources. This object is also included as a property on the HS.FHIRMeta.APIOpens in a new tab object under the FHIRPathAPI property.
Parse the FHIRPath Expression
Once you have instantiated the HS.FHIRPath.APIOpens in a new tab object, you are ready to parse the FHIRPath expression. The method that parses the expression, HS.FHIRPath.API.parse()Opens in a new tab, returns a tree structure that is used by the methods that evaluate a resource. For example, assuming you have an object named fhirPathAPI instantiated as shown in the previous section:
set tree = fhirPathAPI.parse("name.given.empty()")
Evaluate the Resource
Once you have parsed the FHIRPath expression, you can use its tree structure to evaluate or extract data from a resource. Two evaluation methods are available:
-
HS.FHIRPath.API.evaluate()Opens in a new tab — The evaluate() method returns the results of the evaluation in a multidimensional array.
-
HS.FHIRPath.API.evaluateToJson()Opens in a new tab — The evaluateToJson() method returns the collection in a dynamic array.
In both cases, the resource being evaluated is passed into the method as a dynamic object. The tree that was returned by the parse() method is also passed as an argument. For example:
set tree = fhirPathAPI.parse("name.given.empty()")
// myResource is a dynamic object
do fhirPathAPI.evaluate(myResource, tree, .OUTPUT)
set DynArray = fhirPathAPI.evaluateToJson(myResource, tree)
An additional method, HS.FHIRPath.API.evaluateArray()Opens in a new tab, can be used to parse the multidimensional array returned by the evalaute() method.
Work with the Results
While working with results in a dynamic array that is produced by evaluateToJson() has its benefits, the multidimensional array produced by evaluate() contains additional information that is not otherwise available. The following provides a guide to the data in the multidimensional array, assuming that your response to evaluate() was returned in a variable named OUTPUT.
Node | Description |
---|---|
OUTPUT | Number of nodes in the array that contain values. |
OUTPUT(n) | Value of the nth element of the array. |
OUTPUT(n,"t") | Data type of the nth element in the array, including identifying FHIR data types. |
You can further parse the returned multidimensional array using the evaluateArray() method.
By contrast. when using the evaluateToJson() method to produce a dynamic array, you can determine whether the data type is a string, boolean, number, or object from looking at the values in the array, but you cannot determine the FHIR data type.
Workflow Example: evaluate() Method
This example includes the resource being evaluated, the ObjectScript needed to evaluate the resource, and a look at the multidimensional array produced by the evaluation.
set myResource = {
"resourceType":"Patient",
"telecom": [
{
"system": "phone",
"value": "(03) 5555 6473",
"use": "official"
},
{
"system": "phone",
"value": "(03) 5555 6473",
"use": "home"
},
{
"system": "email",
"value": "myName@email.com",
"use": "official"
}
]
}
set fhirVersion = $lb("hl7.fhir.r4.core@4.0.1")
set fhirPathAPI = ##class(HS.FHIRPath.API).getInstance(fhirVersion)
set tree = fhirPathAPI.parse("telecom.where(use = 'official')")
do fhirPathAPI.evaluate(myResource, tree, .OUTPUT)
If you used the zw OUTPUT command in the InterSystems Terminal to view the multidimensional array returned by evaluate(), the result would be:
OUTPUT=2
OUTPUT(1)={"system":"phone","value":"(03) 5555 6473","use":"official"}
OUTPUT(1,"t")="ContactPoint"
OUTPUT(2)={"system":"email","value":"myName@email.com","use":"official"}
OUTPUT(2,"t")="ContactPoint"
Notice that the values are identified as a ContactPoint FHIR data type.
Workflow Example: evaluateArray() Method
This example takes the multidimensional array produced by the evaluation in the evaluate() example above as input and demonstrates the ObjectScript needed to evaluate the resulting array, and looks at the multidimensional array produced by the evaluation.
Merge INPUT = OUTPUT
Kill OUTPUT
Set tree = fhirPathAPI.parse("ContactPoint.value")
do fhirPathAPI.evaluateArray(.INPUT, tree, .OUTPUT)
If you used the zw OUTPUT command in the InterSystems Terminal to view the multidimensional array returned by evaluateArray(), the result would be:
OUTPUT=2
OUTPUT(1)="(03) 5555 6473"
OUTPUT(1,"t")="string"
OUTPUT(2)=”myName@email.com"
OUTPUT(2,"t")="string"
Notice that the values are identified by their ObjectScript data type (string, boolean, number, or object).
Workflow Example: evaluateToJson() Method
This example includes the resource being evaluated, the ObjectScript needed to evaluate the resource, and a look at the dynamic array produced by the evaluation.
set myResource = {
"resourceType":"Patient",
"name": [
{
"family": "Cooper",
"given": [
"James",
"Fenimore"
]
}]
}
set fhirVersion = $lb("hl7.fhir.r4.core@4.0.1")
set fhirPathAPI = ##class(HS.FHIRPath.API).getInstance(fhirVersion)
set tree = fhirPathAPI.parse("name.given.empty()")
set dynArray = fhirPathAPI.evaluateToJson(myResource, tree)
If you used the zw dynArray command in the InterSystems Terminal to view the dynamic array, the result would be:
dynArray=[false]
Functions
The FHIRPath specification defines a wide range of functions that can be used in an expression. InterSystems supports a subset of those functions.
Function | Example |
---|---|
[ ] (index)Opens in a new tab | Practitioner.name[1] |
aggregateOpens in a new tab | item.factor.aggregate($total+$this,0) |
asOpens in a new tab | Condition.abatement.as(string) |
childrenOpens in a new tab | Encounter.participant.children().ofType(Reference) |
descendantsOpens in a new tab | Bundle.descendants().ofType(Patient) |
emptyOpens in a new tab | Patient.contact.where(relationship = 'N').name.empty() |
endsWithOpens in a new tab | 'abcdefg'.endsWith('efg') |
existsOpens in a new tab | Patient.telecom.exists(system = 'phone') |
extensionOpens in a new tab | extension('http://intersystems.com/fhir/extn/sda3/lib/code-table-detail-care-provider-description').value as string |
firstOpens in a new tab | Patient.telecom.where(system = 'phone').first() |
hasExtensionOpens in a new tab | Returns true if any of the input collection have an extension with the specified URL. (This function is not in the FHIRPath v2.0.0 specification.) |
iifOpens in a new tab | iif(1=1,2,3) |
indexOfOpens in a new tab | 'abcdefg'.indexOf('cd') |
isOpens in a new tab | Condition.abatement.is(dateTime) |
lastOpens in a new tab | Patient.name.first().given.last() |
notOpens in a new tab | Bundle.entry.resource.ofType(Patient).gender.not() |
ofTypeOpens in a new tab | Bundle.entry.resource.ofType(Patient) |
resolveOpens in a new tab | Organization.partOf.resolve() |
skipOpens in a new tab | Bundle.entry.resource.ofType(Encounter).skip(5) |
startsWithOpens in a new tab | 'abcdefg'.startsWith('abc') |
substringOpens in a new tab | 'abcdefg'.substring(1, 2) |
tailOpens in a new tab | Bundle.entry.resource.ofType(Observation).tail() |
takeOpens in a new tab | Patient.name.take(1) |
unionOpens in a new tab | Practitioner.name.family.union(Practitioner.id) |
whereOpens in a new tab | Patient.telecom.where(use = 'official') |
Operations
The FHIRPath specification defines a wide range of operations that can be used in an expression. InterSystems supports a subset of those operations.
Operation | Example |
---|---|
+ (addition)Opens in a new tab |
8 + 3 5 seconds + 3 seconds 'string1' + ' and ' + 'string2' |
& (string concatenation)Opens in a new tab | 'string1' & ' and ' & 'string2' |
= (equals)Opens in a new tab |
Practitioner.name[0].family = 'Cooper' Practitioner.meta.versionId = 10 |
!= (not equals)Opens in a new tab | Practitioner.name[1].family != 'Smith' |
| (union collections)Opens in a new tab | Practitioner.name.family | Practitioner.id |
andOpens in a new tab | true and false |
asOpens in a new tab | See implementation notes. |
impliesOpens in a new tab | Patient.name.given.exists() implies Patient.name.family.exists() |
isOpens in a new tab | Practitioner.name[0] is HumanName |
orOpens in a new tab | true or false |
xorOpens in a new tab | true xor false |
According to the FHIRPath specification, the left operand of the asOpens in a new tab operation must be a collection with a single item. However, the InterSystems implementation of FHIRPath allows this collection to have multiple values. For example, suppose you have an Observation with multiple extensions that reference a Patient. With the InterSystems implementation of FHIRPath, the following expression would still be valid: extension.value as Reference.
Improving Performance
InterSystems provides an in-memory cache that can store parsed FHIRPath expressions, improving performance when you have a set of expressions that are repeated frequently. Once the cache is enabled, tree structures produced by the parse() method are stored until the cache is cleared.
To enable the in-memory cache, call:
do fhirPathAPI.enableCache(1)
To disable the cache, call:
do fhirPathAPI.enableCache(0)