FHIR searching

In this post I’m going to talk a little about searching using the REST paradigm within FHIR. This is a large topic, and I certainly won’t cover it in detail, but my intention is to provide enough detail for you to get started. (For the full details of searching (or queries) refer to the specification).

We’re going to focus on searching for a single type of resource – e.g. find a patient – how to specify the search parameters and how FHIR returns the results.

Each resource has a list of pre-defined search parameters that are defined in the specification for that resource. These are at the bottom of the page that describes each resource – for example here are the patient search parameters. Note that there is no requirement on a FHIR server that it must support all of these search parameters – and a server is also able to define their own searches – these are simply the list of parameters that the committee that defined the resource believes are the most common, or most obvious search parameters.

A FHIR server can indicate which parameters it supports for each resource in its conformance resource.

The format for this basic searching uses the GET method as follows:

GET [base]/[type]?[parameters] &_format=[mime-type]

or

GET [base]/[type]/_search?[parameters] &_format=[mime-type]

Where:

  • Base is the base URL to the server
  • Type is the resource type (like ‘Patient’)
  • Parameters are the search parameters you wish to use (and there can be more than one)
  • Format is the format (xml or json) you want the results in. (HTTP headers are the more elegant way to specify this, but it might not always be possible to easily set the headers, which is why this format is supported – in fact, it also applies to simple resource retrieval as well)

The parameters are expressed in name/value pairs, eg

http://hl7connect.healthintersections.com.au/svc/fhir/Patient?name=eve

would return all patients whose name contains the word ‘eve’

http://hl7connect.healthintersections.com.au/svc/fhir/Patient?name=eve&_format=json

is the same search, but specifying that the result should be in a json format rather than the default XML format.

Each resource parameter has a type that defines how it behaves – e.g. whether it is text, a number or part of a code set (a token). In most cases this is reasonably straightforward (though there are subtleties in some cases) – The one that causes the most difficulty at first is where one of the parameters is a resource reference – e.g. you want all the conditions for a particular patient. (In fact, this particular scenario has a specific solution – the compartment – that I’ll cover in a separate post, so bear with me for the moment).

The following search:

http://hl7connect.healthintersections.com.au/svc/fhir/condition?subject=351

will return all conditions where the ID of the subject (which is a patient) is 351 (The assumption is that you have previously performed a search on the patient to locate that ID). It is possible to get more complicated than that – for example you can chain query parameters to get, say, all conditions for patients whose name is david. Go look at the spec if that functionality is important in your use case. (Note: the format of this type of query has changed recently and not all servers may support it yet)

So we’ve talked about how to find resources based on matching their properties to some value, how to find resources based on their link to some other resource but we haven’t talked about the format that the results are returned in. It can’t just be the resource – as there will likely be more then one that matches – it has to be a set of some sort.

In fact, FHIR uses the Atom format to return the results of any search – regardless of how many resources matched the search – and we call this a bundle. Bundles are, in fact, used in a number of parts of FHIR – they form the basis of FHIR Documents and FHIR Messages, as well as the ability to batch multiple commands into a single transaction, but lets stay focussed on search for now.

If you look at the definition of the bundle, you’ll see it’s made up of a feed element, which contains any number of entry elements. Considering first the feed:

  • The root element is  <feed> – and it’s in the atom namespace. This means that any Atom reader can consume FHIR bundles – which raises a number of interesting possibilities that will likely be the subject of future posts! (Grahame, for example, uses this to monitor activity against his server, and it could be the basis for server replication). It also places some constraints on what we can do with bundles – it must remain compliant with the Atom spec.
  • There is a <title> element on both the feed and each entry within the feed. This is required by the Atom spec, but is not treated specially by FHIR. In particular, the contents should not be used for any automated processing.
  • Every feed has an <id> that must be a unique URI. In theory, a server could use this as the basis of an audit log, although that is not required by FHIR.
  • The feed has a number of <link> elements used for purposes such as pagination, as well as an element for the number of total number of results that matched the query (not just in this feed). These are optional.
  • An <updated> element that indicates the instant that the feed was created.
  • The <author> of the feed. Atom requires this – FHIR doesn’t care.
  • An optional <category> that contains tags associated with the feed. I’ll cover tags in a separate post – they’re used for things like security & privacy.
  • A <Signature> element that can be used for digital signatures.

Now lets’ look at the <entry> elements. Each entry element represents a single resource (in this case that matched the search/query) and has the following characteristics:

  • A <title> element (just like on the feed).
  • An <id> element. This is a URI that refers to the resource that is contained in the entry. It is NOT a version specific URI – it will refer to the most recent version of the resource on the server.
  • A <link rel=’self’> element that IS a version specific URI. This is not required (as not all servers will support versioning) but is recommended
  • An <updated> element. This is the date that the resource was last updated on the server – not when it was retrieved from the server. (There is a <published> element that states when it was copied into the feed – I’m not sure how useful this really is – maybe if you’re using the feed for server replication).
  • The <author> of the entry. If used, this will be the person who created the resource.
  • Any number of <category> elements. As in the feed, this is used to indicate the tags associated with the resource, and is much more useful when applied to a resource.
  • A <content> element that has the actual resource. This is what you are after as the result from the search. This will be the standard representation of the resource, with the resource type as the parent element, and the remaining properties as children (at least in XML – the format in JSON is in flux at the moment)
  • A <summary> element – this should be the same content as the text element within the resource, and is used to support the ability to display a feed using an XSLT transform – without needing to understand the FHIR resource structure.

And, of course, a bundle – and its contents – can be expressed in json as well as XML.

So, to summarize, a search in RESTful FHIR is quite simple

  1. Send a GET request with the desired parameters as name/value pairs in the URL
  2. Iterate over the <entry> elements in the response to get the results (or just use an XSLT transform displaying the title & summary elements if all you are doing is displaying it to the user or similar simple processing)

(you can even do this from a browser, though Graham will wrap the response in a UI if you do this – so the POSTman type approach I used in the previous post gives a result closer to the spec.).

Has a final note, it is also possible to use a POST to specify a search: as the query page states:  “or as an x-multi-part-form submission for a POST”, though this does seem to break the rules of REST as POST is not an idempotent operation, and in general is used to update a servers associated data store rather than query against it.