Smile CDR v2024.08.PRE
On this page:

18.5.1Pagination

 

By default, FHIR Gateway performs pagination dependent on the _count parameter of the query, or the default pagination size, if the _count parameter is unset on a given query To return a bundle to a FHIR Gateway client user, the Gateway will first evaluate the first target, and collect matched resources until it fulfills its desired _count. If this value is reached before the downstream bundles have been exhausted of entries, the remainder of that bundle will be used for the subsequent page.

If, however, the first downstream target becomes exhausted before the requested _count is reached, FHIR Gateway will query the second target, and so on and so forth, until all targets become exhausted. 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.

Since Pagination counts are derived from match results, it is important to define what this means.

18.5.1.1Search 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.1.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.1.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.2Paging rules

the _count parameter determines the number of match 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 resource if it is 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.
outcomeAlways included, regardless of pagination. This means that it can be returned multiple times, once per page.
uncategorizedAlways included, regardless of pagination. This means that it can be returned multiple times, once per page.

18.5.1.3Example

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. For the purposes of pagination, uncategorized resources are considered as matchecs. 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 was requested, the gateway only took the first match 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"
			}
		},
		{
			"fullUrl": "http://localhost:8000/OperationOutcome/3",
			"resource": {...},
			"search": {
				"mode": "outcome"
			}
		}
	]
}

In this second page, the second match patient is returned, along with its included Observation. You will note that the outcome results are included again. This is because there is no way to determine which page they should be included with. As a result, they are included in each page derived from that downstream target server. 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"
			},
		},
		{
			"fullUrl": "http://localhost:8000/OperationOutcome/3",
			"resource": {...}
			"search": {
				"mode": "outcome"
			}
		},
	]
}

In this final page, since we have exhausted explicit match entries, the pagination continues onto uncategorized entries. As before, the outcome is included as well.