Skip to content

Commit

Permalink
Add chained search without specifying resource type Close #92
Browse files Browse the repository at this point in the history
  • Loading branch information
ruscoder committed Dec 13, 2022
1 parent 1820f4d commit e841065
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Current
* Add ability to provide additional params to Aiohttp (AsyncFHIRClient) and Requests (SyncFHIRClient) request
* Make `authorization` param truly optional
* Support chained search without specifying resource #92

## 1.3.0
* Fix resource fetching for /fhir base urls #81
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ patients.search(general_practitioner__Organization__name='Hospital')
# /Patient?general-practitioner:Organization.name=Hospital
```

```Python
patients.search(general_practitioner__name='Hospital')
# /Patient?general-practitioner.name=Hospital
```

### Reference
```Python
practitioner = client.resources('Practitioner').search(_id='john-smith').first()
Expand Down
4 changes: 2 additions & 2 deletions fhirpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from .lib import SyncFHIRClient, AsyncFHIRClient

__title__ = "fhir-py"
__version__ = "1.3.0"
__version__ = "1.3.1"
__author__ = "beda.software"
__license__ = "None"
__copyright__ = "Copyright 2021 beda.software"
__copyright__ = "Copyright 2022 beda.software"

# Version synonym
VERSION = __version__
49 changes: 30 additions & 19 deletions fhirpy/base/searchset.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ def SQ(*args, **kwargs):
>>> dict(SQ(patient__Patient__general_practitioner__Organization__name='Name'))
{'patient:Patient.general-practitioner:Organization.name': ['Name']}
>>> dict(SQ(patient__Patient__general_practitioner__name='Name'))
{'patient:Patient.general-practitioner.name': ['Name']}
>>> dict(SQ(based_on__instantiates_canonical='PlanDefinition/id'))
{'based-on.instantiates-canonical': ['PlanDefinition/id']}
>>> dict(SQ(period__ge='2018', period__lt='2019'))
{'period': ['ge2018', 'lt2019']}
Expand Down Expand Up @@ -109,6 +115,20 @@ def SQ(*args, **kwargs):
{'_has:Person:link:id': ['id']}
"""
param_ops = [
"contains",
"exact",
"missing",
"not",
"below",
"above",
"in",
"not_in",
"text",
"of_type",
]
value_ops = ["eq", "ne", "gt", "ge", "lt", "le", "sa", "eb", "ap"]

res = defaultdict(list)
for key, value in kwargs.items():
value = value if isinstance(value, list) else [value]
Expand All @@ -117,33 +137,24 @@ def SQ(*args, **kwargs):
key_parts = key.split("__")

op = None
if len(key_parts) % 2 == 0:
if key_parts[-1] in value_ops or key_parts[-1] in param_ops:
# The operator is always the last part,
# e.g., birth_date__ge or patient__Patient__birth_date__ge
op = key_parts[-1]
key_parts = key_parts[:-1]

base_param, *chained_params = key_parts
param_parts = [base_param]
if chained_params:
param_parts.extend([".".join(pair) for pair in chunks(chained_params, 2)])
param = ":".join(param_parts)
param = key_parts[0]
for part in key_parts[1:]:
# Resource type always starts with upper first letter
is_resource_type = part[0].isupper()

param += ":" if is_resource_type else "."
param += part

if op:
if op in [
"contains",
"exact",
"missing",
"not",
"below",
"above",
"in",
"not_in",
"text",
"of_type",
]:
if op in param_ops:
param = "{0}:{1}".format(param, transform_param(op))
elif op in ["eq", "ne", "gt", "ge", "lt", "le", "sa", "eb", "ap"]:
elif op in value_ops:
value = ["{0}{1}".format(op, sub_value) for sub_value in value]
res[transform_param(param)].extend(value)

Expand Down

0 comments on commit e841065

Please sign in to comment.