Skip to main content

Basic Search

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 ParameterTypeDescriptionExpression
birthdatedateThe patient's date of birthPatient.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 ParameterTypeDescriptionExpression
address-citystringA city specified in an addressPatient.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 ParameterTypeDescriptionExpression
death-datedateThe date of death has been provided and satisfies this search valuePatient.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');

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',
},
},
],
total: 2,
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',
// },
// ]

Searching Multiple Criteria

You can perform and 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');

// returns
// [
// {
// resourceType: 'Patient',
// id: '2',
// name: [
// {
// given: ['Homer'],
// family: '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');

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' });

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.

stringtoken

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.search('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.search('Patient', 'identifier=http://hl7.org/fhir/sid/us-ssn|12345')

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 not completed:

await medplum.searchResources('Task', { 'status:not': 'completed' });
//OR
await medplum.searchResources('Task', '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');

: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"`

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:

PrefixDescription
eqequal
nenot equal
gtgreater than
ltless than
gegreater than or equal to
leless than or equal to
sastarts after
ebends 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');

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');
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');

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.