Skip to content
Clement NgFeb 8, 2017 12:00:00 AM4 min read

Custom Search Parameters in HAPI FHIR

HAPI FHIR’s JPA Module lets you quickly set up a FHIR server, complete with a database for whatever purpose you might have.

One of the most requested features in the last year has been for support of custom search parameters on that server. Out of the box, the JPA server has always supported the default/built-in search parameters that are defined in the FHIR specification.

This means that if you store a Patient resource in the database, the Patient.gender field will be indexed with a search parameter called gender, the Patient.birthDate field will be indexed with a search parameter called birthdate, etc.

To see a list of the default search parameters for a given resource, you can see a table near the bottom of any resource definition. For example, here are the Patient search parameters.

The Need for Custom Parameters

The built-in parameters are great for lots of situations but if you’re building a real application backend then you are probably going to come up with a need that the FHIR specification developers didn’t anticipate (or one that doesn’t meet FHIR’s 80% rule).

The solution for this is to introduce a custom search parameter. Search parameters are defined using a resource that is – unsurprisingly – called SearchParameter. The idea is that you create one of these SearchParameter resources and give it a code (the name of the URL parameter), a type (the search parameter type), and an expression (the FHIRPath expression which will actually be indexed).

Custom Parameters in HAPI FHIR JPA

In HAPI FHIR’s JPA server, custom search parameters are indexed just like any other search parameter. A new mechanism has been introduced in HAPI FHIR 2.3 (to be released soon) that parses the expression, adds any new or updated search parameters to an internal registry of indexed paths, and marks any existing resources that are potential candidates for this new search parameter as requiring reindexing.

This means that any newly added search parameters will cover resources added after the search parameter was added, and it will also cover older resources after the server has had a chance to reindex them.

This also means that you definitely want to make sure you have properly secured the /SearchParameter endpoint since it can potentially cause your server to do a lot of extra work if there are a lot of resources present.

Taking it for a Spin!

To show how this works, here is an example of a search parameter on an extension. We’ll suppose that in our system we’ve defined an extension for patients’ eye colour. Patient resources stored in our database will have the eye colour extension set, and we want to be able to search on this extension, too.

1. Create the Search Parameter

First, define a search parameter and upload it to your server. In Java, this looks as follows:

// Create a search parameter definition
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);

// Upload it to the server
client
	.create()
	.resource(eyeColourSp)
	.execute();

The resulting SearchParameter resource looks as follows:

{
	"resourceType": "SearchParameter",
	"title": "Eye Colour",
	"base": [ "Patient" ],
	"status": "active",
	"code": "eyecolour",
	"type": "token",
	"expression": "Patient.extension('http://acme.org/eyecolour')",
	"xpathUsage": "normal"
}

2. Upload Some Resources

Let’s upload two Patient resources with different eye colours.

Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
client
	.create()
	.resource(p1)
	.execute();

Patient p2 = new Patient();
p2.setActive(true);
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
client
	.create()
	.resource(p2)
	.execute();

Here’s how one of these resources will look when encoded.

{
  "resourceType": "Patient",
  "extension": [
    {
      "url": "http://acme.org/eyecolour",
      "valueCode": "blue"
    }
  ],
  "active": true
}

3. Search!

Finally, let’s try searching:

Bundle bundle = ourClient
	.search()
	.forResource(Patient.class)
	.where(new TokenClientParam("eyecolour").exactly().code("blue"))
	.returnBundle(Bundle.class)
	.execute();

System.out.println(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));

This produces a search result that contains only the matching resource:

{
  "resourceType": "Bundle",
  "id": "bc89e883-b9f7-4745-8c2f-24bf9277664d",
  "meta": {
    "lastUpdated": "2017-02-07T20:30:05.445-05:00"
  },
  "type": "searchset",
  "total": 1,
  "link": [
    {
      "relation": "self",
      "url": "http://localhost:45481/fhir/context/Patient?eyecolour=blue"
    }
  ],
  "entry": [
    {
      "fullUrl": "http://localhost:45481/fhir/context/Patient/2",
      "resource": {
        "resourceType": "Patient",
        "id": "2",
        "meta": {
          "versionId": "1",
          "lastUpdated": "2017-02-07T20:30:05.317-05:00"
        },
        "text": {
          "status": "generated",
          "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><table class=\"hapiPropertyTable\"><tbody/></table></div>"
        },
        "extension": [
          {
            "url": "http://acme.org/eyecolour",
            "valueCode": "blue"
          }
        ],
        "active": true
      },
      "search": {
        "mode": "match"
      }
    }
  ]
}

Custom Search Parameters in Smile Digital Health

Naturally, this feature will soon be available in Smile. Previous versions of Smile had a less elegant solution to this problem; however, now that we have a nice elegant approach to custom parameters that is based on FHIR’s own way of handling this, Smile users will see the benefits quickly.

avatar

Clement Ng

Clement is Smile CDR's Chief Revenue Officer. For over 25 years Clem has been helping organizations, companies and investors reach new heights through expanded audience outreach. With an MA in Economics and a strong command of over a number of languages Clem’s had a rich and decorated work history. In his career, he held key roles in planning, development, sales, marketing, finance, corporate development, client services and federal government. Clem’s work experience combined with his global perspective, strong problem-solving abilities and superb people skills has been instrumental to Smile CDR becoming the globally recognized brand that it is today.

RELATED ARTICLES