In this module, we will go through some more advanced ways of searching. This module assumes you have basic knowledge on searching. If you prefer to start with the basics, please go through the Search operations and parameters module first. In the exercise of this module, we will run queries on our own Vonk server. The end-point of this server is http://vonk.fire.ly
The topics covered in this module are:
Chaining allows you to search on properties of a referenced resource. This means that you can go through a graph of connected resources and get back a result by using search parameters defined for the last resource type in a chain.
To illustrate how chaining works in practice, let's go through an example. An Observation contains a reference to a Patient resource in the subject
element. The search parameters patient
and subject
allow you to filter Observations on a specific subject (the difference being that the patient
search parameter is restricted to the resource type Patient, while the subject
search parameter searches for all resource types that can be the subject of the Observation). Now suppose you want to filter the Observations based on properties of the Observation's subject. To do so, you would only need to append the search parameter (e.g. patient
) with a .
followed by a valid search parameter for the referenced resource (e.g. birthdate
).
For example, to search for all Observations from Patients born since the 1st of January 2001, you would get:
GET [base]/Observation?patient.birthdate=ge2001-01-01
Note that you could also use the subject
search parameter, but in this case, you will still need to specify the resource type that you are looking for. It's not allowed to address multiple resource types at the same time (correctly implemented FHIR servers will reject searches in which this is the case).
The correct syntax for the same search based on the subject
parameter is as follows:
GET [base]/Observation?subject:Patient.birthdate=ge2001-01-01
Without chaining you would have needed multiple search requests to perform this search:
It would be nessesarry to do some kind of filtering and processing on the client side.
Please note, you can only chain search parameters of the type reference
that point to elements of type Reference.reference
. In FHIR, references using so-called logical references (using Reference.identifier
) are also allowed. However, because a FHIR Server cannot reasonably verify that the resources behind these references exist, chaining on these elements are not supported.
Chaining can be very efficient as you can cross more than one resource in a single chain. For example, try to search for all Observations based on a MedicationRequest for a Medication that contains the ingredient Lorazepam (substance):
GET [base]/Observation?basedOn:MedicationRequest.medication.ingredient-code=387106007
Note that except for the last parameter, all parameters are of the type reference
.
Instead of filtering resources based on properties of referenced resources, it's also possible to filter resources based on properties of resources referring to them. For example, you may want to select all Patients having an appointment today. In this case, you would need to use the _has
parameter followed by:
Encounter
)patient
)date
) and the comparison valueThese parts are separated by a :
. The correct syntax of this search would be:
GET [base]/Patient?_has:Encounter:patient:date=[today]
Let's make it a bit more complex. Say you want to select all Patients having an Observation performed by a specific Organization, let's assume the identifier of this Organization is 1234
.
GET [base]/Patient?_has:Observation:patient:performer:Organization.identifier=1234
It's also possible to add more than one _has
parameter to your search. But be aware that this would result in a joined set of two independently executed _has
queries. For example, the following query:
GET [base]/Patient?_has:Encounter:patient:date=[today]&_has:Encounter:patient:practitioner=1234
will not result in a list of Patients having an appointment with Practitioner 1234
today. Instead, it will return all Patients having an appointment today as well as all Patients having an appointment with Practitioner 1234
at any point in time.
A _filter expression can be used for more advanced searches in which multiple queries need to be combined. _filter provides a range of advantages compared to the regular way of using search parameters:
For example, you could use a filter to select all Patients from Organizations 1234
and 1235
with inactive records.
GET [base]/Patient?_filter=active eq false and ( organization eq 1234 and organization eq 1235 )
Note that this expression is equivalent to:
GET [base]/Patient?active=false&organization=1234,1235
It gets more challenging when you want to combine these queries with an or
. For example, when you want to select all Patients with an inactive record as well as all Patients from Organizations 1234
or 1235
you will definitely need a filter.
GET [base]/Patient?_filter=active eq false or ( organization eq 1234 or organization eq 1235 )
Would you have tried to define this expression as following:
GET [base]/Patient?active=false,organization=1234,1235
The server would interpret this as a search for Patients where the value of active
is either 'false', 'organization=1234' or '1235', which is obviously not what we try to achieve.
All search parameters that are normally defined for resources can be used in a _filter expression.
Additionally, negated expressions can be used in a filter search request. This feature can otherwise only be achieved with the :not
modifier for token search parameters.
For example, the following search request retrieves all Patient resources which contain a different name than "Sarah":
GET [base]/Patient?_filter=name ne Sarah
Another powerful search function is the compartment search. The syntax of a compartment search is as follows:
GET [base]/[compartment]/[id]/[ResourceWithOutgoingReference]?[SearchParam]=<value>
Adding search parameters to a compartment search is optional. To return all resources in a compartment use a *
instead. For example, use the following request to select all resources that have any reference to the Patient with id 1234
.
GET [base]/Patient/1234/*
You can also define the types of resources you want to retrieve using the _type
parameter. For example, the following request will return all Observations and Encounters of the Patient with id “1234”.
GET [base]/Patient/1234/?_type=Observation,Encounter
Finally, you can add additional criteria to your compartment search. Let's say you want to retrieve all Observation resources that contain a reference to the Patient with the logical id 1234
and which have the observation status set to final. The following requests, of which the first one is the compartment search, are both valid and will produce the exact same results.
GET [base]/Patient/1234/Observation?status=final
GET [base]/Observation?subject=Patient/1234&status=final
Composite search parameters are parameters that take a combination of values to evaluate. For example, the composite search parameter component-code-value-quantity
evaluates Observations against a pair of values for the elements code
and valueQuantity
. The values are separated by $
. The request given below returns all Observations with the given LOINC code and a value greater than 80kg.
GET [base]/Observation?component-code-value-quantity=http://loinc.org|29463-7$gt80||kg
Note that this search would be equivalent to the following request using the code
and value-quantity
search parameters:
GET [base]/Observation?code=http://loinc.org|29463-7&value-quantity=gt80||kg
The power of composite search parameters becomes more clear in the following examples based on the composite search parameter characteristic-value
, which searches for the pair of values for characteristic
and value
.
GET [base]/Group?characteristic-value=gender$mixed
GET [base]/Group?characteristic-value=gender$mixed,owner$peter
The first one returns all Groups where the gender is mixed. In this case using the parameters characteristics
and value
(e.g. characteristic=gender&value=mixed) would not return the same results. Groups having the characteristic gender and the value female and having the value mixed for another key would be returned as well.
The second one returns all Groups where gender is mixed as well as groups where the owner is peter. These kinds of searches would be impossible to express without composite search parameters.
Sometimes it can be useful to include related resources in your search results. For example, when you search for Observations, it can be useful to include the Patient resources that are subject of the returned Observations.
GET [base]/Observation?_include=Observation:patient
GET [base]/Observation?_include=Observation:patient&patient=1234
The first request returns all Observations and the related Patient resources. The second one returns all Observations for a specific Patient with id=1234 as well as the Patient resource itself. It is possible to include multiple related resources by repeating the include
parameter.
Instead of adding resources that are referenced from the returned resources in your search results, you may also want to add resources that refer to them. For example, when you are searching for Patients, you may want to add Observations and Conditions that have a reference to these Patients in their subject
fields. The request below returns a specific Patient (the Patient with id=1234) and includes all Observation and Condition resources of this Patient in the search Bundle.
GET [base]/Patient?_revinclude=Observation:patient&_revinclude=Condition:patient&_id=1234
A revinclude may come in handy in combination with a reverse chaining expression. You can get back the resources due to which the matches of the reverse chaining where included in the search results:
GET [base]/Patient?_has:Observation:patient:code=29463-7&_revinclude=Observation:patient
The include and revinclude process can be recursive. With a :recurse
modifier, resources can be included based on parameters defined for resources that were included through a different _include or _revinclude expression:
GET [base]/Patient?_has:Observation:patient:code=29463-7&_revinclude=Observation:patient&_revinclude:recurse=DiagnosticReport:result
FHIR provides a set of operations that can be invoked on the base endpoint, a resource type, a resource instance or a specific version of a resource instance. Usually, operations are invoked using a POST request, but in some cases, a GET request is allowed as well. To invoke an operation its name should be prefixed with a $
. Below is an example of an operation that retrieves all resources related to Patient with ID=1234. Note that this is similar to a compartment search.
POST [base]/Patient/1234/$everything
The body of a request invoking an operation often contains a Parameters resource to specify the in parameters of the operation. For example, in the everything
operation optional start and end dates can be provided. The body may also contain other resources, for example, the resource you want to validate by using the validate
operation.
Operations can be resource specific or apply to all resource types. In the last case, we speak about base operations. It is also possible for implementations to define their own operations using the OperationDefintion resource.
One operation that is often used is the validate
operation. Let's say you want to validate a Patient resource against the daf-patient profile. You could do so by running the following request and adding your Patient resource in the request body.
POST [base]/Patient/$validate?profile=http://hl7.org/fhir/StructureDefinition/daf-patient
ValueSets are often composed of codes from other ValueSets or CodeSystems by using include and exclude filters. As you will learn (or already have learned) in the [Terminology] module, ValueSets contain both a compose
element, which is used to create a ValueSet, and an expansion
element, which can be used by a FHIR Terminology server to return all codes in a ValueSet. Below are a couple of example requests.
GET [base]/ValueSet/1234$expand
POST [base]/ValueSet/$expand
POST [base]/ValueSet/1234/$expand
The GET request can be used when a ValueSet is already known to the server. When a POST request is used, the request body contains a Parameters resource in which one or more in parameters are defined. The Parameters may include the ValueSet resource or contain a canonical url of an existing ValueSet. The returned result of the expand
operation is the expanded ValueSet resource. Note that the expansion can change over time depending on changes in included ValueSets and CodeSystems.
Nictiz is the centre of expertise for standardization and eHealth in The Netherlands. HL7 Netherlands core and MedMij profiles are published on Simplifier. MedMij is a national project that aims to give Dutch citizens integrated access to all their health data in one personal health environment. FHIR is used as a standard to exchange health information between the involved parties. The profiles are based on standardized clinical building blocks called Health and Care Information Models (HCIM).
The Implementation Guide of Nictiz contains a section called 'List of invocations' in which examples are given on how the data can be retrieved using the Dutch national FHIR model.
Castor EDC is a company that supports researchers in collecting data for medical research. The Castor EDC application is linked to the electronic health record of the hospital, in which the researchers can mark his or her patients for inclusion. We implemented a Vonk Facade server for Castor EDC in a university hospital in the Netherlands, which maps the data from their clinical data warehouse to FHIR resources and sends them to the Castor EDC application. In the first pilot, three resources were implemented: Patient, Observation and Condition.
The Castor EDC application needs to be able to retrieve all resources from a specific Patient. The include
and revinclude
functions are supported in the implementation and can be used to retrieve a specific Patient including all Observations and Conditions referring to this Patient in the subject field.
[base]/Patient?_revinclude=Observation:patient&_revinclude=Condition:patient&_id=[id]
Note that in this query it's only possible to filter on search parameters implemented for the Patient resource. Researchers should however be able to filter Observations and Conditions on date. To do so, a more complex query is required. For example, to retrieve a specific Patient (let's assume the id of this Patient is 'example') as well as all his/her Observations since January 2018, the following query can be used.
[base]/Patient/example/Patient,Observation?date=ge2018-01-01T00:00:00.000+02:00
In this exercise you will run some advanced search requests. You may either use a REST client like Postman to run your requests or paste them directly in your browser. Start by reading the case description. Here below are a couple of links that you may find useful during this exercise:
Either try out these searches yourself or follow the steps below to get to formulate the right search request. In this exercise we assume you use the public Vonk server, but you may use any FHIR server you want. Note however that not all search functionalities may be implemented in all FHIR servers.
We are always looking for ways to improve our products. The Profiling Academy was built using our own IG-editor in Simplifier. If you have any feedback on this module or on our Profiling Academy in general, please leave a comment in the Issue Tracker of the project.
Most modules end with an exercise. Use Forge to start profiling yourself. Just contact us at simplifier@fire.ly if you need any help.
Follow one of our predefined or tailor-made courses. We will make sure you know FHIR inside-out.
Let us assist you with your FHIR use case. Visit our company website to know more about our services or get into contact with Rien Wertheim right away.
Powered by SIMPLIFIER.NET