FHIR Searching using POST

Another little thing that has some up as we implement our FHIR services is what HTTP verbs you can use when submitting a query. The ‘usual’ way to do this is with the GET verb, but the spec states that it is also legitimate to use a POST to the _search endpoint, with the parameters as an x-multi-part-form submission in the POST body.

Read more of this post

Search receipts in FHIR 

When writing a server that can respond to FHIR queries, it may not be clear what to do when you get a search parameter that you don’t understand. Or, for that matter, which of the defined search parameters against each resource you do need to support.

Read more of this post

FHIR transactions: the Search functionality

FHIR supports transactions by POSTing a bundle of resources to the root of a server. The server processes them separately as if they had been sent as individual requests – except that the entire group of updates either succeeds or fails as a group (which is what a transaction is, after all…)

It’s one of the more complex processing that a server has to do – particularly as it needs to manage references between resources for new and existing resources within the bundle.

But there’s one particular aspect to this, and that is what if you don’t know if the resource already exists on the server?

For example, we’re working on a project to import glucose data from a personal device. Our current thinking is to create a bundle containing a Patient resource, a Device resource and Observation resources for each of the glucose results we want to save. (We’re probably not using the DeviceObservationReport as it doesn’t add anything extra for us in this scenario).

So when the client creates the bundle, what does it use for the Patient ID – and the Device ID for that matter? (The Observations will always be new, so cid: IDs are appropriate for them). We do have an identifier for both, but that’s not the same as the ID – and having to look up the resources before putting them in the bundle kind of defeats the purpose. And if we include the resource anyway with a cid: ID , then the server will always create a new one – not what we want.

The answer to this is a couple of paragraphs in the spec that talks about how a server can manage this. Here it is (it’s in the transaction description):

The application constructing a bundle may not be sure whether a particular resource will already exist at the time that the transaction is executed; this is typically the case with reference resources such as patient and provider. In this case, the bundle should contain a candidate resource with a cid: identifier, and an additional search parameter using an Atom link:

<link href="http://localhost/Patient?[parameters]" rel="search"/>

A search link with a root of http://localhost means to search the local resource store for a match as specified in the parameters (which must conform to the servers capability for searching as specified in its conformance statement). If the search returns no matches, the server process the resource normally. If the search returns one match, the server uses this matching resource instead, and ignores the submitted resource. If more than one resource is found, the transaction SHALL be rejected.

 This means that we can include the identifier of the Patient and the Device in the bundle, and the server will locate the resource if it exists – and create it if it does not.

So here’s a sample of a patient resource in a bundle:

<entry>
    <title>Patient details</title>
    <id>cid:patient@bundle</id>
    <updated>2014-05-28T22:12:21Z</updated>
    <link href="http://localhost/Patient?identifier=PRP1660" rel="search"/>
    <content type="text/xml">
        <Patient xmlns="http://hl7.org/fhir">
            <text>
                <status value="generated"/>
                <div xmlns="http://www.w3.org/1999/xhtml">Joe Bloggs</div>
            </text>
            <identifier>
                <value value="PRP1660"/>
            </identifier>
            <name>
                <text value="Joe Bloggs"/>
            </name>
        </Patient>
    </content>
</entry>

Note the <link> element where we specify that the search parameter is an identifier with the value of PRP1660. (and, of course, we have this identifier in the candidate resource – otherwise it will never work!)

This is incredibly useful! In fact it does raise the issue of whether a server claiming to be able to process transactions in its conformance statement SHALL be able to implement this functionality. I don’t think the spec is clear on this – and probably needs to be.

 

FHIR query syntax: Note to self…

Just a short post to point out a ‘quirk’ in the FHIR syntax for querying so I can look it up again when I forget. (actually, it’s not really a quirk at all – it’s perfectly reasonable, though can be confusing).

It all came about when Josh pointed out an error in my post on retrieving documentReference resources from an server.

We were discussing how you could retrieve a filtered list of documentReference resources for a patient over a period of time, and used the example:

GET  http://registryserver/DocumentReference?subject=100&period >=2013-01-01

However, as Josh pointed out – and as the spec indicates, you need an ‘=’ symbol in there to bind the HTTP parameter ‘period‘ to the value ‘>= 2013-01-01‘ which is what the FHIR server is going to query on. Thus the correct syntax is:

GET  http://registryserver/DocumentReference?subject=100&period= >=2013-01-01

Now, if I had only tested the query out on one of the reference servers before I posted it, I would have avoided the error, but there you go…

Adventures in FHIR Searching: Getting a list of patients in a Ward

Had an interesting set of exchanges on the implementers skype chat yesterday talking about searching in FHIR.

The background was that we are looking at building a tablet app to support the recording of medication dispensings. Orion has an application (a Hospital Information System or HIS) that records this using a desktop interface, so the plan is to build a FHIR server that will act as a ‘proxy’ between the HIS and the tablet, with the tablet app consuming JSON FHIR resources over a RESTful interface.

We are thinking that the user will log into the tablet, then get a list of the wards they are currently scheduled on. They will select a ward, which will give them a list of patients, then select the patient from which they will get the medications to administer (or a similar type of workflow). I was asked to help out with the design of the FHIR resources & calls – and started to think about how to get as far as the patient list (I’ll consider the medication administration stuff later).

Looking through the current FHIR resource list I found a Location resource that specifically mentions that it models a Ward, so my first thought was to add an extension to that resource of type List, and then the List could reference a collection of Patients. The proxy server would be responsible for communicating with the HIS and constructing the bundle of these resources, which could be returned to the tablet app on request.

I mentioned this on the skype chat, and after some discussion with Grahame and Lloyd, we figured that a better solution would be to model the relationships using an encounter, and then we could do a FHIR query to get the data we need, using an _include parameter on the query for efficiency (I’ll get to this later).

So the relationship between the resources is like this:

ward query

Our approach will be:

  1. get the ward we want
  2. then get the encounters that are associated with that ward
  3. and then we can get the patients from the encounter.

Getting the ward is a straight forward query. Suppose the name of the ward is ‘Ward1’ then it’s a simple GET query against Location:

GET /Location?name=Ward1

Note that we will get back a bundle, and we may need to check for multiples – eg Ward1 and Ward11 would be valid matches (it’s a string search). We might also want to include a Location.type parameter if that is appropriate in our situation. But, we now have the Location resource– the Ward – and the ID of that resource. Let’s pretend the ID is 100.

Now to get the Encounters associated with that Location. Encounter does have a location property which is a sub-object with properties of Location & Period. In other words, an encounter can have many locations, and each location has a period over which the location was valid. This makes sense of course, as a patient can be moved around different places (Wards, radiology, therapy etc). So, we want encounters that are associated with our specific Location (Ward) right now. (There are other subtleties, but let’s keep it simple for now). We’ll assume that if there is a Location that matches ours, and where the period.end property is not present then that means they are currently associated with our ward.

However, there is a problem: There is no search parameter defined for location against Encounter – and in fact we need 2 of them – one for the location.location property and one for the location.period property. There are 2 things we can do:

  1. Petition the FHIR team to add these parameters
  2. Make these search parameters that our server supports (which is quite legitimate, though does mean our query on’t work on other servers)

We will do both. We will define 2 search parameters:

  • location that matches Encounter.location.location (which is a reference)
  • location-period that matches Encounter.location.period (which is a date).

and also ask the core team to add them (actually it is the HL7 committee that has oversight of this resource – you can look this up from this page, but sending a message to the core team via any of the supported channels will achieve this).

So now we have:

GET /Encounter?location =100 &location-period => 25-10-2013

(assuming that the date today is 25th October 2013)

So, now we have the encounters we want – all we need are the patients.

We could make a query for each patient – we can get that from Encounter.subject – but we can also ask the server to do that in our query by using the _include parameter. This is an instruction to the server to include the referenced resources in our query as well as the directly matched resources. So our complete query is:

GET /Encounter?location =100 &location-period => 25-10-2013&_include=Encounter.subject

will return a bundle that will contain all the encounters currently associated with Ward1 (resourceID=100), plus all the patient resources that are in these encounters. Job done!

Some notes:

  • We have defined some server specific parameters. Should the committee decide not to add these parameters to the spec, then either we accept this (after all we’re only concerned with our server at the moment anyway), or we could re-jig the query. Another exercise for the reader…
  • Servers are not obliged to support all search parameters – even if they are named in the spec. If we wanted our client to be able to talk to another FHIR server, then we would need to examine its conformance resource to make sure it does.
  • Servers are also not obliged to honour the _include parameter. If they don’t, then it simply means that we have more work to do client side – we just make direct requests for each patient after we retrieve the encounters.
  • In an earlier version of this post I was querying ‘into’ search parameters – eg GET /Encounter?location.period=… – it doesn’t work like that – it’s not like a path. Part of the issue is that we can’t have ‘ad-hoc’ queries into resources  – to quote Grahame: we’ve agreed that clients can’t simply launch random queries into the content of the resources – it has to be pre-agreed fields – there’s no way for the server to make that efficient, except in the case of special technologies 
  • Date querying is complicated. There’s a whole section in the spec on it.

This became a rather longer post than I intended, but hopefully it is a useful exercise to run through the thinking behind what became quite an elegant query – and I learned a lot doing it. It’s not the only way to do it of course, but it was a fun process!

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.