Skip to content

Commit db1fc14

Browse files
authored
Handle multiple target types for ReferenceSearchParam (#5285)
Update SQL generator for better usage of index on ReferenceSearchParam table Refs AB#177046
1 parent 4493ae1 commit db1fc14

File tree

9 files changed

+548
-25
lines changed

9 files changed

+548
-25
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
{
2+
"resourceType": "Bundle",
3+
"type": "transaction",
4+
"entry": [
5+
{
6+
"resource": {
7+
"resourceType": "Patient",
8+
"id": "ref-test-patient1",
9+
"name": [{ "family": "RefTestPatient", "given": ["Alice"] }],
10+
"gender": "female",
11+
"birthDate": "1990-01-15"
12+
},
13+
"request": { "method": "PUT", "url": "Patient/ref-test-patient1" }
14+
},
15+
{
16+
"resource": {
17+
"resourceType": "Patient",
18+
"id": "ref-test-patient2",
19+
"name": [{ "family": "RefTestPatient", "given": ["Bob"] }],
20+
"gender": "male",
21+
"birthDate": "1985-06-20"
22+
},
23+
"request": { "method": "PUT", "url": "Patient/ref-test-patient2" }
24+
},
25+
{
26+
"resource": {
27+
"resourceType": "Group",
28+
"id": "ref-test-group1",
29+
"type": "person",
30+
"actual": true,
31+
"name": "Test Group Alpha",
32+
"member": [
33+
{ "entity": { "reference": "Patient/ref-test-patient1" } }
34+
]
35+
},
36+
"request": { "method": "PUT", "url": "Group/ref-test-group1" }
37+
},
38+
{
39+
"resource": {
40+
"resourceType": "Practitioner",
41+
"id": "ref-test-practitioner1",
42+
"name": [{ "family": "RefTestDoc", "given": ["Carol"] }]
43+
},
44+
"request": { "method": "PUT", "url": "Practitioner/ref-test-practitioner1" }
45+
},
46+
{
47+
"resource": {
48+
"resourceType": "Device",
49+
"id": "ref-test-device1",
50+
"status": "active",
51+
"deviceName": [{ "name": "Test Thermometer", "type": "user-friendly-name" }]
52+
},
53+
"request": { "method": "PUT", "url": "Device/ref-test-device1" }
54+
},
55+
{
56+
"resource": {
57+
"resourceType": "Location",
58+
"id": "ref-test-location1",
59+
"name": "Test Clinic Room 1",
60+
"status": "active"
61+
},
62+
"request": { "method": "PUT", "url": "Location/ref-test-location1" }
63+
},
64+
{
65+
"resource": {
66+
"resourceType": "Encounter",
67+
"id": "ref-test-encounter-patient",
68+
"status": "finished",
69+
"class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB" },
70+
"subject": { "reference": "Patient/ref-test-patient1" }
71+
},
72+
"request": { "method": "PUT", "url": "Encounter/ref-test-encounter-patient" }
73+
},
74+
{
75+
"resource": {
76+
"resourceType": "Encounter",
77+
"id": "ref-test-encounter-group",
78+
"status": "finished",
79+
"class": { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "AMB" },
80+
"subject": { "reference": "Group/ref-test-group1" }
81+
},
82+
"request": { "method": "PUT", "url": "Encounter/ref-test-encounter-group" }
83+
},
84+
{
85+
"resource": {
86+
"resourceType": "Observation",
87+
"id": "ref-test-obs-patient",
88+
"status": "final",
89+
"code": { "coding": [{ "system": "http://loinc.org", "code": "8867-4", "display": "Heart rate" }] },
90+
"subject": { "reference": "Patient/ref-test-patient1" },
91+
"valueQuantity": { "value": 72, "unit": "beats/minute" }
92+
},
93+
"request": { "method": "PUT", "url": "Observation/ref-test-obs-patient" }
94+
},
95+
{
96+
"resource": {
97+
"resourceType": "Observation",
98+
"id": "ref-test-obs-device",
99+
"status": "final",
100+
"code": { "coding": [{ "system": "http://loinc.org", "code": "8310-5", "display": "Body temperature" }] },
101+
"subject": { "reference": "Device/ref-test-device1" },
102+
"valueQuantity": { "value": 36.6, "unit": "C" }
103+
},
104+
"request": { "method": "PUT", "url": "Observation/ref-test-obs-device" }
105+
},
106+
{
107+
"resource": {
108+
"resourceType": "Observation",
109+
"id": "ref-test-obs-location",
110+
"status": "final",
111+
"code": { "coding": [{ "system": "http://loinc.org", "code": "8867-4", "display": "Heart rate" }] },
112+
"subject": { "reference": "Location/ref-test-location1" },
113+
"valueQuantity": { "value": 0, "unit": "beats/minute" }
114+
},
115+
"request": { "method": "PUT", "url": "Observation/ref-test-obs-location" }
116+
},
117+
{
118+
"resource": {
119+
"resourceType": "Condition",
120+
"id": "ref-test-condition1",
121+
"clinicalStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active" }] },
122+
"verificationStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", "code": "confirmed" }] },
123+
"code": { "coding": [{ "system": "http://snomed.info/sct", "code": "386661006", "display": "Fever" }] },
124+
"subject": { "reference": "Patient/ref-test-patient1" },
125+
"asserter": { "reference": "Practitioner/ref-test-practitioner1" }
126+
},
127+
"request": { "method": "PUT", "url": "Condition/ref-test-condition1" }
128+
},
129+
{
130+
"resource": {
131+
"resourceType": "Condition",
132+
"id": "ref-test-condition2",
133+
"clinicalStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active" }] },
134+
"verificationStatus": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", "code": "confirmed" }] },
135+
"code": { "coding": [{ "system": "http://snomed.info/sct", "code": "25064002", "display": "Headache" }] },
136+
"subject": { "reference": "Patient/ref-test-patient2" },
137+
"asserter": { "reference": "Patient/ref-test-patient1" }
138+
},
139+
"request": { "method": "PUT", "url": "Condition/ref-test-condition2" }
140+
}
141+
]
142+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Tests for PR 5285: Handle multiple target types for ReferenceSearchParam
2+
#
3+
# The UntypedReferenceRewriter now handles reference search parameters with
4+
# multiple target types. Previously, only single-target-type references were
5+
# rewritten. Now, multi-target references get an OR expression over all
6+
# valid target types (plus IS NULL for untyped string references).
7+
#
8+
# Key search parameters exercised:
9+
# - Encounter.subject targets: Patient, Group
10+
# - Observation.subject targets: Patient, Group, Device, Location
11+
# - Condition.asserter targets: Patient, Practitioner, PractitionerRole, RelatedPerson
12+
13+
@hostname = localhost:44348
14+
15+
### Get the bearer token, if authentication is enabled
16+
# @name bearer
17+
POST https://{{hostname}}/connect/token
18+
content-type: application/x-www-form-urlencoded
19+
20+
grant_type=client_credentials
21+
&client_id=globalAdminServicePrincipal
22+
&client_secret=globalAdminServicePrincipal
23+
&scope=fhir-api
24+
25+
### Setup test data (transaction bundle)
26+
POST https://{{hostname}}
27+
Content-Type: application/json
28+
Authorization: Bearer {{bearer.response.body.access_token}}
29+
30+
< ./Data/ReferenceMultipleTargetTypesBundle.json
31+
32+
###############################################################################
33+
# TEST 1: Untyped reference search with multiple target types
34+
# Encounter.subject targets Patient and Group.
35+
# BEFORE this PR: type filter was NOT added for multi-target params.
36+
# AFTER this PR: an OR filter over (Patient, Group, NULL) is added.
37+
###############################################################################
38+
39+
### 1a. Untyped - should find encounter with Patient subject
40+
GET {{hostname}}/Encounter?subject=ref-test-patient1
41+
Authorization: Bearer {{bearer.response.body.access_token}}
42+
43+
### 1b. Untyped - should find encounter with Group subject
44+
GET {{hostname}}/Encounter?subject=ref-test-group1
45+
Authorization: Bearer {{bearer.response.body.access_token}}
46+
47+
###############################################################################
48+
# TEST 2: Typed reference search (regression - should work same as before)
49+
###############################################################################
50+
51+
### 2a. Typed - Patient subject
52+
GET {{hostname}}/Encounter?subject=Patient/ref-test-patient1
53+
Authorization: Bearer {{bearer.response.body.access_token}}
54+
55+
### 2b. Typed - Group subject
56+
GET {{hostname}}/Encounter?subject=Group/ref-test-group1
57+
Authorization: Bearer {{bearer.response.body.access_token}}
58+
59+
### 2c. Typed - wrong type, should return empty
60+
GET {{hostname}}/Encounter?subject=Device/ref-test-device1
61+
Authorization: Bearer {{bearer.response.body.access_token}}
62+
63+
###############################################################################
64+
# TEST 3: Observation.subject (4 target types: Patient, Group, Device, Location)
65+
# Exercises wider OR expressions.
66+
###############################################################################
67+
68+
### 3a. Untyped - Patient subject
69+
GET {{hostname}}/Observation?subject=ref-test-patient1
70+
Authorization: Bearer {{bearer.response.body.access_token}}
71+
72+
### 3b. Untyped - Device subject
73+
GET {{hostname}}/Observation?subject=ref-test-device1
74+
Authorization: Bearer {{bearer.response.body.access_token}}
75+
76+
### 3c. Untyped - Location subject
77+
GET {{hostname}}/Observation?subject=ref-test-location1
78+
Authorization: Bearer {{bearer.response.body.access_token}}
79+
80+
### 3d. Typed - only Device observations
81+
GET {{hostname}}/Observation?subject=Device/ref-test-device1
82+
Authorization: Bearer {{bearer.response.body.access_token}}
83+
84+
###############################################################################
85+
# TEST 4: Comma-separated (OR) references mixing typed and untyped
86+
# Exercises top-level OR handling + per-operand rewriting.
87+
###############################################################################
88+
89+
### 4a. Two untyped - should find both encounters
90+
GET {{hostname}}/Encounter?subject=ref-test-patient1,ref-test-group1
91+
Authorization: Bearer {{bearer.response.body.access_token}}
92+
93+
### 4b. Mixed typed and untyped - should find both encounters
94+
GET {{hostname}}/Encounter?subject=Patient/ref-test-patient1,ref-test-group1
95+
Authorization: Bearer {{bearer.response.body.access_token}}
96+
97+
### 4c. Multiple untyped across 4 target types
98+
GET {{hostname}}/Observation?subject=ref-test-patient1,ref-test-device1,ref-test-location1
99+
Authorization: Bearer {{bearer.response.body.access_token}}
100+
101+
###############################################################################
102+
# TEST 5: Chained search through multi-target reference
103+
###############################################################################
104+
105+
### 5a. Encounter where subject is a Patient named "RefTestPatient"
106+
GET {{hostname}}/Encounter?subject:Patient.family=RefTestPatient
107+
Authorization: Bearer {{bearer.response.body.access_token}}
108+
109+
### 5b. Encounter where subject is a Group named "Test Group Alpha"
110+
GET {{hostname}}/Encounter?subject:Group.name=Test Group Alpha
111+
Authorization: Bearer {{bearer.response.body.access_token}}
112+
113+
###############################################################################
114+
# TEST 6: _include on multi-target reference
115+
###############################################################################
116+
117+
### 6a. Include subject on Encounter (should pull in Patient and Group)
118+
GET {{hostname}}/Encounter?_id=ref-test-encounter-patient,ref-test-encounter-group&_include=Encounter:subject
119+
Authorization: Bearer {{bearer.response.body.access_token}}
120+
121+
### 6b. Include subject on Observation (should pull in Patient, Device, Location)
122+
GET {{hostname}}/Observation?_id=ref-test-obs-patient,ref-test-obs-device,ref-test-obs-location&_include=Observation:subject
123+
Authorization: Bearer {{bearer.response.body.access_token}}
124+
125+
###############################################################################
126+
# TEST 7: Condition.asserter (4 target types)
127+
###############################################################################
128+
129+
### 7a. Untyped - matches Practitioner asserter
130+
GET {{hostname}}/Condition?asserter=ref-test-practitioner1
131+
Authorization: Bearer {{bearer.response.body.access_token}}
132+
133+
### 7b. Untyped - matches Patient as asserter
134+
GET {{hostname}}/Condition?asserter=ref-test-patient1
135+
Authorization: Bearer {{bearer.response.body.access_token}}
136+
137+
### 7c. Typed asserter
138+
GET {{hostname}}/Condition?asserter=Practitioner/ref-test-practitioner1
139+
Authorization: Bearer {{bearer.response.body.access_token}}
140+
141+
### 7d. Comma-separated asserter
142+
GET {{hostname}}/Condition?asserter=ref-test-practitioner1,ref-test-patient1
143+
Authorization: Bearer {{bearer.response.body.access_token}}

0 commit comments

Comments
 (0)