# FHIR searches and bundles

Query FHIR servers with parameters, operators and sorting - the client pages through the result bundles for you.

You can query FHIR servers with search parameters, operators, sorting and paging - the client builds the query URL and unwraps the results for you. A server answers every search with a Bundle, a container resource that holds the matches, the total number of them and links to further pages.

## Build a search {#build-a-search}

The `resources` method of a [connection](https://zato.io/docs/dev/healthcare/hl7/fhir/connections/index.html) client returns a lazy search object. Each call below refines the query and the server receives nothing until you fetch the results.

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class FindPatients(Service):
    name = 'demo.fhir.find-patients'

    def handle(self) -> 'None':

        client = self.fhir['FHIR.Sample']

        # Everything about patients starts here
        patients = client.resources('Patient')

        # Active patients named Chalmers, at most 10 of them, youngest first
        result = patients.search(name='Chalmers', active=True).sort('-birthdate').limit(10)

        # Only now is the server invoked
        for patient in result.fetch():
            self.logger.info('Found %s', patient['name'])
```

Invoking the service writes each match to the server log:

```text
INFO - Found [{'use': 'official', 'family': 'Chalmers', 'given': ['Peter', 'James']}]
```

## Search operators {#search-operators}

Search parameters accept operators appended with a double underscore, corresponding to the operators the FHIR specification allows in URLs:

```python
# Born in the year 2000 or later - birthdate=ge2000-01-01 on the wire
patients.search(birthdate__ge='2000-01-01')

# Name containing a string
patients.search(name__contains='alm')

# Status being none of the listed values
observations = client.resources('Observation')
observations.search(status__not=['cancelled', 'entered-in-error'])
```

Python-style underscores in parameter names are translated to FHIR-style dashes automatically, e.g. `general_practitioner` becomes `general-practitioner`.

## Fetch the results {#fetch-the-results}

There are several ways to obtain the results, depending on how much data you expect:

```python
# One page of results, as configured with .limit
found = result.fetch()

# All the results - the Bundle's next links are followed until there are no more pages
found = result.fetch_all()

# The same, but lazily - each page is fetched only when the iteration reaches it
for patient in result:
    ...

# A single match - raises an error if there are none or more than one
patient = patients.search(_id='511a6231-361e-4b8e-8f9c-b183b7813f4d').get()

# The first match or None
patient = patients.search(name='Chalmers').first()

# Only the number of matches, without any resources
how_many = patients.search(active=True).count()
```

## Read the Bundle itself {#read-the-bundle-itself}

To read the Bundle's own fields, e.g. its total or its links, use `fetch_raw`:

```python
bundle = result.fetch_raw()

self.logger.info('Total matches: %s', bundle.total)

for entry in bundle.entry:
    self.logger.info('Entry: %s', entry.resource)
```

## Include related resources {#include-related-resources}

A single search can return related resources in the same Bundle, e.g. each Encounter together with the Patient it refers to, saving you the extra round trips:

```python
# Encounters plus the patients they point at ..
encounters = client.resources('Encounter').include('Encounter', 'subject')

# .. or, the reverse, patients plus the observations that point back at them.
patients = client.resources('Patient').revinclude('Observation', 'subject')
```

## Select fields to transfer {#select-fields-to-transfer}

To reduce the amount of data transferred, ask the server for specific elements of each resource:

```python
# Each returned Patient will contain only its name, plus the id and resourceType fields
patients.elements('name').fetch()
```

## Conditional operations {#conditional-operations}

Searches can also drive writes - the search parameters then act as the condition:

```python
# Create the patient only if no resource matches the search
patient = client.resource('Patient', identifier=[{'system': 'urn:mrn', 'value': '12345'}])
search = client.resources('Patient').search(identifier='urn:mrn|12345')
patient, created = search.get_or_create(patient)

self.logger.info('Newly created? %s', created)
```

## See also {#see-also}

- [Resources](https://zato.io/docs/dev/healthcare/hl7/fhir/resources/index.html) - Create, read, update and delete any FHIR resource
- [Path access](https://zato.io/docs/dev/healthcare/hl7/fhir/path-access/index.html) - Values extracted from search results in single calls
- [Connections](https://zato.io/docs/dev/healthcare/hl7/fhir/connections/index.html) - The Dashboard form and everything self.fhir accepts

## Learn more {#learn-more}

- [Healthcare interface engine](https://zato.io/docs/dev/healthcare/) - Clinical messages, FHIR, conversion and operations
- [HL7 v2 parsing](https://zato.io/docs/dev/healthcare/hl7/v2/parsing/) - Messages as typed objects, with field access and validation
- [MLLP channels](https://zato.io/docs/dev/healthcare/hl7v2/mllp/) - Receiving and sending HL7 v2 over MLLP sockets
- [FHIR integrations](https://zato.io/docs/dev/healthcare/hl7/fhir/) - Read and write FHIR resources, with paths, bundles and extensions
- [HL7 v2 to FHIR](https://zato.io/docs/dev/healthcare/hl7/to-fhir/) - Converting v2 messages into FHIR bundles, codes and references included
- [EDIFACT](https://zato.io/docs/dev/healthcare/edifact/) - Parsing interchanges, dialects and the transports they arrive on
- [Transformation](https://zato.io/docs/dev/healthcare/transformation/) - One message in, another standard out, in ordinary Python
- [Audit log](https://zato.io/docs/dev/healthcare/audit-log.html) - Every message with its acknowledgment, searchable by patient identifier
- [AI in clinical interfaces](https://zato.io/docs/dev/healthcare/ai/) - Services calling LLMs and AI agents calling clinical services as tools
