Smile CDR v2024.08.PRE
On this page:

18.5.1Pagination

 

FHIR Gateway performs pagination using default pagination size, or the _count parameter if it is provided.

For the purposes of pagination, uncategorized resources are considered as a match and sorted among them.

To return a bundle to a FHIR Gateway client user, the Gateway considers results from all targets, which is necessary to fulfill the requested sort order. In case no _sort parameter is specified in the request, results will still be sorted by target id and resource id, to guarantee deterministic order across targets.

Result pages will contain _count number of match and uncategorized resources, except last page which could have fewer, when exhausting the results of all target servers. Intermediate pages will include a next and a previous link to provide access to following or previous pages.

Gateway pagination requires target servers replying next and previous links in responses when extra resources are available in the corresponding pagination direction.

To sort, the Gateway requires resources from all targets which have resources matching the request. For this reason, performance will be improved by configuring parallel: true in the GatewaySearchRoute model, unless a compelling reason to change it exists (e.g. target requests depending on a previous target responses).

It is important to note that regardless of whether the search routes are defined to execute in parallel: true mode, the evaluation of pagination will occur in a sequential manner.

18.5.1.1Sorting

Current implementation supports standard FHIR context parameters.

Properties of type token are sorted by the token system, then the token value with null sorting last.

Properties of type quantity are sorted considering their un-normalized value, to match HAPI-FHIR target servers.

For an explanation on match and uncategorized search modes, see (#SearchModes)[Search Modes] below.

18.5.1.2Search Modes

When a downstream target returns a bundle, it contains various pieces of data. The ones that FHIR Gateway critically relies on are the total element, as well as the entries section. The entries section contains all the resources that the downstream target returned.

18.5.1.2.1Entries

The following is an example bundle we will use to explain the various search modes in the entries.

{
	"resourceType": "Bundle",
	"id": "461666c1-2a1b-453a-9765-be272bdb48e9",
	"type": "searchset",
	"total": 1,
	"entry": [
		{
			"fullUrl": "http://localhost:8000/Patient/1",
			"resource": {
				"resourceType": "Patient",
				"id": "1",
				"identifier": [
					{
						"system": "urn:hapitest:mrns",
						"value": "00002"
					}
				]
			},
			"search": {
				"mode": "match"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Observation/2",
			"resource": {
				"resourceType": "Observation",
				"id": "2",
				"subject": {
					"reference": "Patient/1"
				}
			},
			"search": {
				"mode": "include"
			}
		},
		{
			"fullUrl": "http://localhost:8000/OperationOutcome/3",
			"resource": {
				"resourceType": "OperationOutcome",
				"id": "3",
				"issue": [
					{
						"code": "unknown"
					}
				]
			},
			"search": {
				"mode": "outcome"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Patient/4",
			"resource": {
				"resourceType": "Patient",
				"id": "4"
			}
		}
	]
}

This bundle contains 4 entries, each bearing a unique Search Mode. Here's what they mean:

  • match: This resource was a match to the requested query.
  • include: This resource is returned because it is referred to from another resource in the search set. For example, in /Patient?_revinclude=Observation:subject any Observation resources would have search mode include.
  • outcome: An OperationOutcome that provides additional information about the processing of a search.

Finally, it is permitted by the FHIR Specification to omit the search section entirely from a searchset bundle, we call this internally an uncategorized entry, and this is the fourth and final possible value of search.mode.

18.5.1.2.2total

The total field represents the total number of resources that have search mode of match across all pages of a query, not how many resources are returned in a particular response.

18.5.1.3Paging rules

The _count parameter determines the number of match and uncategorized resources that should be returned per page, from downstream targets. However, what happens to downstream entries that are not of match type?

Search ModeLogic for inclusion in Gateway Bundle
matchIncludes up to _count entries from target servers, returning remainders in subsequent page requests.
includeIncludes a resource if its related target resources are in the included match entries. e.g. only include an observation if its related patient was included in the gateway bundle.
outcomeIncluded in pages containing match resources retrieved from target in same response. Can be returned multiple times, when resources in same target response are split in different pages.
uncategorizedTreated as match for pagination purposes.

18.5.1.4Example

Let's take the following example bundle from a single downstream server. For brevity, the resource bodies have been truncated.

{
	"resourceType": "Bundle",
	"id": "461666c1-2a1b-453a-9765-be272bdb48e9",
	"type": "searchset",
	"total": 2,
	"entry": [
		{
			"fullUrl": "http://localhost:8000/Patient/1",
			"resource": {...},
			"search": {
				"mode": "match"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Patient/2",
			"resource": {...},
			"search": {
				"mode": "match"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Observation/3",
			"resource": {
				"subject": {
					"reference": "Patient/1"
				}
			},
			"search": {
				"mode": "include"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Observation/4",
			"resource": {
				"subject": {
					"reference": "Patient/2"
				}
			},
			"search": {
				"mode": "include"
			}
		},
		{
			"fullUrl": "http://localhost:8000/OperationOutcome/3",
			"resource": {...},
			"search": {
				"mode": "outcome"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Patient/4",
			"resource": {...}
		}
	]
}

You'll note that there are two resources that match, two which are included, one outcome and one uncategorized.

Consider that the query which was executed against the gateway was the following:

GET localhost:8008/Patient?_revinclude=Observation:subject&_count=1

The important part of this is that the client has requested _count=1, meaning there should be no more than one match entry. Let's have a look at what the Gateway returns here, remembering what our one downstream server returned.

{
	"resourceType": "Bundle",
	"id": "461666c1-2a1b-453a-9765-be272bdb48e9",
	"type": "searchset",
	"total": 2,
	"link":{
		"relation": "next",
		"url": "..."
	}
	"entry": [
		{
			"fullUrl": "http://localhost:8000/Patient/1",
			"resource": {...},
			"search": {
				"mode": "match"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Observation/3",
			"resource": {
				"subject": {
					"reference": "Patient/1"
				}
			},
			"search": {
				"mode": "include"
			}
		},
		{
			"fullUrl": "http://localhost:8000/OperationOutcome/3",
			"resource": {...},
			"search": {
				"mode": "outcome"
			}
		}
	]
}

You will see that since _count=1 and no _sort was requested, the gateway only took the first-in-id-order match or uncategorized result from the downstream target. Furthermore, it included only the observation which was related to the chosen patient, and omitted the Observation which refers to the patient which was not included for this page. You will also note that the outcome results were included. Here is what the second page looks like, when the next url is visited:

{
	"resourceType": "Bundle",
	"id": "461666c1-2a1b-453a-9765-be272bdb48e9",
	"type": "searchset",
	"total": 2,
	"entry": [
		{
			"fullUrl": "http://localhost:8000/Patient/2",
			"resource": {...},
			"search": {
				"mode": "match"
			}
		},
		{
			"fullUrl": "http://localhost:8000/Observation/4",
			"resource": {
				"subject": {
					"reference": "Patient/2"
				}
			},
			"search": {
				"mode": "include"
			}
		}
	]
}

In this second page, the second match patient is returned, along with its included Observation. Finally, let's see what the final page of data would look like:


	"resourceType": "Bundle",
	"id": "461666c1-2a1b-453a-9765-be272bdb48e9",
	"type": "searchset",
	"total": 1,
	"entry": [
		{
			"fullUrl": "http://localhost:8000/Patient/4",
			"resource": {
				"resourceType": "Patient",
				"id": "4"
			},
		}
	]
}

In this final page, we found the uncategorized entry with the last-in-id-order.