Basic Search | Medplum

Intro

One of the most basic operations when working with the Medplum FHIR API is to query resources that fulfill certain criteria.

The FHIR specification defines rich search semantics to support these use cases, and this guide will cover some of the basic search operations to get you started. If you're new to FHIR, we'd recommend checking out our FHIR Basics page first.

Search Parameters

To maintain performance, FHIR doesn't allow Resources to be queried by arbitrary elements. Instead, it defines a set of search parameters for each Resource Type.

Let's look at a few examples with the Patient resource. The Patient reference docs have a table that list out all the available search parameters.

Search Parameter Type Description Expression
birthdate date The patient's date of birth Patient.birthDate

Some search parameters, such as birthdate, map directly to a top-level element, Patient.birthDate. (Note that the search parameter is all lowercase, even though the element is camel case)

Search Parameter Type Description Expression
address-city string A city specified in an address Patient.address.city

Some search parameters map to nested elements, such as address-city, which maps to Patient.address.city. Since Patient.address is an array element, this search parameter will search all addresses saved to the Patient.

Search Parameter Type Description Expression
death-date date The date of death has been provided and satisfies this search value Patient.deceased

Lastly, some search parameters rename/alias the target element. For example, the death-date maps to the Patient.deceased element.

Basic Search

To search for resources, you can simply add search parameters and values as query parameters in your GET request.

The Medplum Client SDK also provides the search helper method, which accepts a string or object.

await medplum.search('Patient', { birthdate: '1940-03-29' });

// OR
await medplum.search('Patient', 'birthdate=1940-03-29');
curl https://api.medplum.com/fhir/R4/Patient?birthdate=1940-03-29

This request will return a FHIR Bundle resource, which contains the query results as well as some metadata. The Bundle.entry element will contain an array with each Bundle.entry[i].resource being a search result.

// returns
{
  resourceType: 'Bundle',
  type: 'searchset',
  entry: [
    {
      fullUrl: 'http://api.medplum.com/Patient/1',
      resource: {
        resourceType: 'Patient',
        id: '1',
        name: [
          {
            given: ['John'],
            family: 'Doe',
          },
        ],
        birthDate: '1940-03-29',
      },
    },
    {
      fullUrl: 'http://api.medplum.com/Patient/2',
      resource: {
        resourceType: 'Patient',
        id: '2',
        name: [
          {
            given: ['Homer'],
            family: 'Simpson',
          },
        ],
        birthDate: '1940-03-29',
      },
    },
  ],
  link: [
    {
      relation: 'self',
      url: 'http://api.medplum.com/Patient?birthdate=1940-03-29',
    },
  ],
}

Because iterating over the Bundle.entry array is such a common pattern, the Medplum SDK provides the searchResources convenience method that unwraps the bundle and returns an array of resources.

await medplum.searchResources('Patient', { name: 'Simpson', birthdate: '1940-03-29' });

// OR
await medplum.searchResources('Patient', 'name=Simpson&birthdate=1940-03-29');
// returns
// [
//   {
//     resourceType: 'Patient',
//     id: '2',
//     name: [
//       {
//         given: ['Homer'],
//         family: 'Simpson',
//       },
//     ],
//     birthDate: '1940-03-29',
//   },
// ]

The array returned by searchResources also includes a bundle property that contains the original Bundle resource. You can use this to access bundle metadata such as Bundle.total and Bundle.link.

Searching Multiple Criteria

You can perform an AND search by specifying multiple query parameters.

await medplum.searchResources('Patient', { name: 'Simpson', birthdate: '1940-03-29' });

// OR
await medplum.searchResources('Patient', 'name=Simpson&birthdate=1940-03-29');
curl https://api.medplum.com/fhir/R4/Patient?name=Simpson&birthdate=1940-03-29

Specifying comma-separated values performs an OR operation for that search parameter.

await medplum.searchResources('Task', { status: 'completed,cancelled' });

// OR
await medplum.searchResources('Task', 'status=completed,cancelled');
curl https://api.medplum.com/fhir/R4/Task?status=completed,cancelled

Searching by Reference

You can use reference search parameters to search for resources based on the resources they refer to.

The syntax for this kind of search is [parameter]=[resourceType]/[id]. You can use the getReferenceString() utility method to help with construct your query.

For example, to search for all Observation resources that reference a Patient with the ID "1234":

/*

curl https://api.medplum.com/fhir/R4/Observation?subject=Patient/1234

*/
const patient: Patient = { resourceType: 'Patient', id: '1234' };
await medplum.searchResources('Observation', { subject: getReferenceString(patient) });

// OR
await medplum.searchResources('Observation', { subject: 'Patient/1234' });
curl https://api.medplum.com/fhir/R4/Observation?subject=Patient/1234

Strings vs. Tokens

FHIR defines two different types of "string" search parameters: string and token. Their search behaviors are quite different, and it's important to understand the difference.

string token
Case insensitive
Match strings that start with query
No system URL
Case sensitive
Exact string match
Optional system URL

string

A string search parameter is used when searching for a specific word or phrase within a resource. This type of search is more general and allows for partial matches, such as searching for patients whose names contain the word "Smith". Searches are case insensitive, and any result that starts with the query string will be returned.

Example

For example, the following search will return patients with the names "eve", "Eve", "Evelyn", but not"Steve"

medplum.search('Patient', 'name=eve')

You can use the :contains modifier to search anywhere inside the target string, and the :exact modifier to perform a case-sensitive, exact string match (see below)

token

A token search parameter is used when searching for exact matches of a specific code or identifier, such as a medical terminology code ( CodeableConcept) or a unique patient identifier ( Identifier ). By default, searching a token performs a case-sensitive, exact string match.

Additionally, many token elements are namespaced by a system string. This is because FHIR resources often contain codes or identifiers that come from different code systems, such as LOINC or SNOMED CT, which may use the same code or identifier for different concepts.

You can restrict your token search to a specific system by using the syntax <parameter>=<system>|<value>

Example

The following search would find all patients with any identifier that equals "12345"

medplum.searchResources('Patient', 'identifier=12345');

If we only wanted to search for patients whose social security number was "12345", we could use the system string "http://hl7.org/fhir/sid/us-ssn" as follows:

medplum.searchResources('Patient', 'identifier=http://hl7.org/fhir/sid/us-ssn|12345');

We can also check for the presence of a particular identifier by dropping the <value> and using the syntax <parameter>=<system>|.

Example

To find all Patients that have a social security number:

medplum.searchResources('Patient', 'identifier=http://hl7.org/fhir/sid/us-ssn|');

Search Modifiers

The FHIR spec includes a set of modifiers to change the way a specific search parameters behave. They are used by appending the string :<modifier-name> to the search parameter. While the FHIR search specification details all the available modifiers, we'll describe some of the most common modifiers here.

:not

:not excludes the specified values from results. For example, search for all Tasks where status is notcompleted:

await medplum.searchResources('Task', { 'status:not': 'completed' });

//OR
await medplum.searchResources('Task', 'status:not=completed');
curl https://api.medplum.com/fhir/R4/Patient?status:not=completed

:missing

:missing specifies whether or not to include values where the specified search parameter is present/absent.

For example, searching for all Patients with missing birthDates.

await medplum.searchResources('Patient', { 'birthdate:missing': 'true' });

// OR
await medplum.searchResources('Patient', 'birthdate:missing=true');
curl https://api.medplum.com/fhir/R4/Patient?birthdate:missing=true

:contains

:contains allows you to perform a partial match on string search parameters.

For example, searching for Patients whose name includes the substring "stein"

await medplum.searchResources('Patient', { 'name:contains': 'eve' });

// OR
await medplum.searchResources('Patient', 'name:contains=eve');
// Return patients with the names `"eve"`, `"Eve"`, `"Evelyn`",  AND `"Steve"`
curl https://api.medplum.com/fhir/R4/Patient?name:contains=eve

Searching by Comparison

FHIR provides a mechanism to search for resources that have a value greater or less than a certain threshold, or that have a value within a specific range, by adding a prefix to your query value. The table below lists the common prefixes used for quantity, number, and date search parameters:

Prefix Description
eq equal
ne not equal
gt greater than
lt less than
ge greater than or equal to
le less than or equal to
sa starts after
eb ends before

This example shows how to find all RiskAssessments with a probability greater than 0.8.

await medplum.searchResources('RiskAssessment', { probability: 'gt0.8' });

// OR
await medplum.searchResources('RiskAssessment', 'probability=gt0.8');
curl https://api.medplum.com/fhir/R4/RiskAssessment?probability=gt0.8

You can search an inclusive range using an AND search.

await medplum.searchResources('Observation', [
  ['value-quantity', 'gt40'],
  ['value-quantity', 'lt60'],
]);

// OR
await medplum.searchResources('Observation', 'value-quantity=gt40&value-quantity=lt60');
curl https://api.medplum.com/fhir/R4/Observation?value-quantity=gt40&value-quantity=lt60

Note

Because we are specifying the value-quantity parameter twice in this query, we must pass a string[][] as the second argument to searchResources().

You can search an exclusive range using an OR search.

await medplum.searchResources('Observation', { 'value-quantity': 'lt40,gt60' });

// OR
await medplum.searchResources('Observation', 'value-quantity=lt40,gt60');
https://api.medplum.com/fhir/R4/Observation?value-quantity=lt40,gt60

Sorting the Results

You can sort your search results by using the special search parameter _sort.

The _sort parameter allows you specify a list of search parameters to sort by, in order.

The example below searches for all RiskAssessments, sorted by their probability, then by date.

await medplum.searchResources('RiskAssessment', { _sort: 'probability,date' });

// OR
await medplum.searchResources('RiskAssessment', '_sort=probability,date');
https://api.medplum.com/fhir/R4/RiskAssessment?_sort=probability,date

Reversing the sort order

To sort in descending order, prepend the search parameter with a minus sign -. The following example returns RiskAssessments, in descending order of probability:

await medplum.searchResources('RiskAssessment', { _sort: '-probability' });

// OR
await medplum.searchResources('RiskAssessment', '_sort=-probability');
https://api.medplum.com/fhir/R4/RiskAssessment?_sort=-probability

Sorting by updated time

FHIR also provides the special search parameter, _lastUpdated, to search by the last updated time for a resource. The following example searches for the most recently updated RiskAssessments resources:

await medplum.searchResources('RiskAssessment', { _sort: '-_lastUpdated' });

// OR
await medplum.searchResources('RiskAssessment', '_sort=-_lastUpdated');
https://api.medplum.com/fhir/R4/RiskAssessment?_sort=-_lastUpdated

Getting the total number of results

You can use the special search parameter, _total, to include the total number of matching resources in your search results. This information is particularly useful for pagination and understanding the scope of the data you are dealing with.

Conclusion

This article covers the basic FHIR search functionality needed to build a healthcare application. The next guides will cover more advanced topics such as paginated search and using GraphQL for retrieving linked Resources.