0
# Search and Query
1
2
Advanced FHIR search capabilities with chaining, modifiers, includes, pagination, and complex query building. SearchSets provide a fluent API for building and executing FHIR queries.
3
4
## Capabilities
5
6
### Basic Search Operations
7
8
Core search functionality for building and executing FHIR queries.
9
10
```python { .api }
11
def search(self, **params) -> FHIRSearchSet:
12
"""
13
Add search parameters to the query.
14
15
Parameters:
16
- **params: Search parameters as keyword arguments
17
18
Returns:
19
New SearchSet instance with parameters added
20
"""
21
22
def limit(self, count: int) -> FHIRSearchSet:
23
"""
24
Set maximum number of results to return.
25
26
Parameters:
27
- count: Maximum result count
28
29
Returns:
30
New SearchSet instance with limit applied
31
"""
32
33
def sort(self, *args) -> FHIRSearchSet:
34
"""
35
Set sort parameters for results.
36
37
Parameters:
38
- *args: Sort fields, prefix with '-' for descending order
39
40
Returns:
41
New SearchSet instance with sorting applied
42
"""
43
44
def elements(self, *args, exclude=False) -> FHIRSearchSet:
45
"""
46
Specify which elements to include or exclude in results.
47
48
Parameters:
49
- *args: Element names to include/exclude
50
- exclude: If True, exclude specified elements; if False, include only specified elements
51
52
Returns:
53
New SearchSet instance with element selection
54
"""
55
```
56
57
### Advanced Search Features
58
59
Include, revinclude, and _has parameter support for complex queries.
60
61
```python { .api }
62
def include(
63
self,
64
resource_type: str,
65
attr: str = None,
66
*,
67
iterate: bool = False
68
) -> FHIRSearchSet:
69
"""
70
Add _include parameter to include referenced resources.
71
72
Parameters:
73
- resource_type: Resource type to include
74
- attr: Reference parameter name (required unless resource_type is '*')
75
- iterate: Whether to apply :iterate modifier
76
77
Returns:
78
New SearchSet instance with include added
79
"""
80
81
def revinclude(
82
self,
83
resource_type: str,
84
attr: str = None,
85
*,
86
iterate: bool = False
87
) -> FHIRSearchSet:
88
"""
89
Add _revinclude parameter to include referencing resources.
90
91
Parameters:
92
- resource_type: Resource type that references this type
93
- attr: Reference parameter name (required unless resource_type is '*')
94
- iterate: Whether to apply :iterate modifier
95
96
Returns:
97
New SearchSet instance with revinclude added
98
"""
99
100
def has(self, *args, **kwargs) -> FHIRSearchSet:
101
"""
102
Add _has parameter for reverse chaining.
103
104
Parameters:
105
- *args: Positional _has parameters
106
- **kwargs: Named _has parameters
107
108
Returns:
109
New SearchSet instance with _has parameter added
110
"""
111
```
112
113
### Result Fetching
114
115
Methods for executing queries and retrieving results.
116
117
```python { .api }
118
async def count(self) -> int:
119
"""
120
Get total count of matching resources.
121
122
Returns:
123
Total number of resources matching the query
124
"""
125
126
async def first(self) -> FHIRResource:
127
"""
128
Get the first result from the query.
129
130
Returns:
131
First matching resource or None if no results
132
"""
133
134
async def get(self) -> FHIRResource:
135
"""
136
Get exactly one result from the query.
137
138
Returns:
139
Single matching resource
140
141
Raises:
142
- ResourceNotFound: If no resources match
143
- MultipleResourcesFound: If multiple resources match
144
"""
145
146
async def fetch(self) -> list:
147
"""
148
Fetch all results from current page.
149
150
Returns:
151
List of resources from current page
152
"""
153
154
async def fetch_all(self) -> list:
155
"""
156
Fetch all results from all pages.
157
158
Returns:
159
List of all matching resources across all pages
160
"""
161
162
async def fetch_raw(self) -> dict:
163
"""
164
Fetch raw Bundle response from server.
165
166
Returns:
167
Raw FHIR Bundle dict
168
"""
169
```
170
171
### SearchSet Utilities
172
173
Helper methods for working with search sets.
174
175
```python { .api }
176
def clone(self) -> FHIRSearchSet:
177
"""
178
Create a copy of the SearchSet.
179
180
Returns:
181
New SearchSet instance with same parameters
182
"""
183
184
async def __aiter__(self):
185
"""
186
Async iteration support for AsyncFHIRSearchSet.
187
188
Allows using 'async for' to iterate through all search results,
189
automatically handling pagination.
190
191
Yields:
192
Individual resources from search results
193
"""
194
```
195
196
### Search Query Builder
197
198
Advanced query building utilities for complex search parameters.
199
200
```python { .api }
201
def SQ(*args, **kwargs) -> dict:
202
"""
203
Build search query parameters with advanced syntax support.
204
205
Supports:
206
- Chained parameters: patient__Patient__name='John'
207
- Search modifiers: name__contains='test'
208
- Date comparisons: date__ge='2020-01-01'
209
- Multiple values: status__in=['active', 'inactive']
210
211
Returns:
212
Dict of properly formatted search parameters
213
"""
214
215
class Raw:
216
def __init__(self, **kwargs):
217
"""
218
Wrapper for raw parameter values that should not be transformed.
219
220
Parameters:
221
- **kwargs: Raw parameter key-value pairs
222
"""
223
```
224
225
## Usage Examples
226
227
### Basic Search Operations
228
229
```python
230
import asyncio
231
from fhirpy import AsyncFHIRClient
232
233
async def basic_search():
234
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
235
236
# Simple search
237
patients = client.resources('Patient').search(family='Smith')
238
results = await patients.fetch()
239
240
# Search with multiple parameters
241
patients = client.resources('Patient').search(
242
family='Smith',
243
given='John',
244
active=True
245
)
246
247
# Limit results and sort
248
recent_patients = (client.resources('Patient')
249
.search(active=True)
250
.sort('-_lastUpdated')
251
.limit(10))
252
253
results = await recent_patients.fetch()
254
255
# Get total count
256
total = await patients.count()
257
print(f"Found {total} patients")
258
```
259
260
### Advanced Query Building with SQ
261
262
```python
263
from fhirpy.base.searchset import SQ
264
265
async def advanced_queries():
266
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
267
268
# Chained search - find patients with observations
269
query = SQ(patient__Patient__name='John')
270
observations = client.resources('Observation').search(**query)
271
272
# Search modifiers
273
query = SQ(
274
name__contains='John', # :contains modifier
275
birthdate__ge='1990-01-01', # greater than or equal
276
gender__not='unknown' # :not modifier
277
)
278
patients = client.resources('Patient').search(**query)
279
280
# Multiple values
281
query = SQ(status__in=['active', 'inactive'])
282
patients = client.resources('Patient').search(**query)
283
284
# Complex chained parameters
285
query = SQ(
286
patient__Patient__general_practitioner__Organization__name='Hospital'
287
)
288
observations = client.resources('Observation').search(**query)
289
290
results = await observations.fetch()
291
```
292
293
### Includes and References
294
295
```python
296
async def include_examples():
297
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
298
299
# Include patient data with observations
300
observations = (client.resources('Observation')
301
.search(code='55284-4') # Blood pressure
302
.include('Patient', 'subject'))
303
304
bundle = await observations.fetch_raw()
305
# Bundle contains both observations and referenced patients
306
307
# Reverse include - get patients with their observations
308
patients = (client.resources('Patient')
309
.search(active=True)
310
.revinclude('Observation', 'subject')
311
.limit(5))
312
313
results = await patients.fetch_all()
314
315
# Include with iteration
316
encounters = (client.resources('Encounter')
317
.include('Patient', 'subject', iterate=True)
318
.include('Organization', 'serviceProvider'))
319
320
# Wildcard includes
321
encounters = client.resources('Encounter').include('*')
322
```
323
324
### Pagination and Result Handling
325
326
```python
327
async def pagination_example():
328
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
329
330
# Page through results
331
patients = client.resources('Patient').limit(50)
332
333
page1 = await patients.fetch()
334
print(f"Page 1: {len(page1)} patients")
335
336
# Get all results across all pages
337
all_patients = await patients.fetch_all()
338
print(f"Total patients: {len(all_patients)}")
339
340
# Get count without fetching data
341
total_count = await patients.count()
342
print(f"Total count: {total_count}")
343
344
# Get single result safely
345
try:
346
specific_patient = await (client.resources('Patient')
347
.search(identifier='12345')
348
.get())
349
except ResourceNotFound:
350
print("Patient not found")
351
except MultipleResourcesFound:
352
print("Multiple patients found with same identifier")
353
```
354
355
### Complex Search Scenarios
356
357
```python
358
async def complex_search():
359
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
360
361
# Find observations for patients in specific organization
362
query = SQ(
363
subject__Patient__organization='Organization/123',
364
code__text__contains='blood',
365
date__ge='2023-01-01',
366
date__lt='2024-01-01'
367
)
368
369
observations = (client.resources('Observation')
370
.search(**query)
371
.include('Patient', 'subject')
372
.include('Practitioner', 'performer')
373
.sort('-date')
374
.limit(100))
375
376
# Use _has for reverse chaining
377
patients_with_conditions = (client.resources('Patient')
378
.has('Condition', 'subject', code='diabetes'))
379
380
# Element selection for performance
381
patients_summary = (client.resources('Patient')
382
.search(active=True)
383
.elements('id', 'name', 'birthDate')
384
.limit(1000))
385
386
# Combine multiple techniques
387
comprehensive_search = (client.resources('Encounter')
388
.search(status='finished')
389
.include('Patient', 'subject')
390
.revinclude('Observation', 'encounter')
391
.has('Condition', 'encounter',
392
category='problem-list-item')
393
.sort('-date')
394
.limit(20))
395
396
results = await comprehensive_search.fetch_raw()
397
398
# Process bundle entries
399
encounters = [entry['resource'] for entry in results['entry']
400
if entry['resource']['resourceType'] == 'Encounter']
401
patients = [entry['resource'] for entry in results['entry']
402
if entry['resource']['resourceType'] == 'Patient']
403
observations = [entry['resource'] for entry in results['entry']
404
if entry['resource']['resourceType'] == 'Observation']
405
```
406
407
### SearchSet Iteration
408
409
```python
410
async def iteration_example():
411
client = AsyncFHIRClient('https://hapi.fhir.org/baseR4')
412
413
# Async iteration over search results
414
patients = client.resources('Patient').search(active=True)
415
416
async for patient in patients:
417
print(f"Patient: {patient.get_by_path('name.0.family')}")
418
419
# Process each patient individually
420
if patient.get('gender') == 'female':
421
# Do something with female patients
422
pass
423
424
# Clone and modify search sets
425
base_search = client.resources('Patient').search(active=True)
426
427
male_patients = base_search.clone().search(gender='male')
428
female_patients = base_search.clone().search(gender='female')
429
430
male_count = await male_patients.count()
431
female_count = await female_patients.count()
432
433
print(f"Male: {male_count}, Female: {female_count}")
434
```