Allow chained searches in Bundles where the fullUrl is fully qualified (#5529)

* Allow chained searches in Bundles where the fullUrl is fully qualified

* Add changelog

* Spotless
This commit is contained in:
James Agnew 2023-12-01 15:19:51 -05:00 committed by GitHub
parent ec5402abd4
commit d597cf2763
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 26 deletions

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 5529
title: "When using a chained SearchParameter to search within a Bundle as [described here](https://smilecdr.com/docs/fhir_storage_relational/chained_searches_and_sorts.html#document-and-message-search-parameters), if the `Bundle.entry.fullUrl` was fully qualified but the reference was not, the search did not work. This has been corrected."

View File

@ -63,6 +63,7 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -2010,17 +2011,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
* references within a Bundle * references within a Bundle
*/ */
if (theAppContext instanceof IBaseBundle && isNotBlank(theUrl) && !theUrl.startsWith("#")) { if (theAppContext instanceof IBaseBundle && isNotBlank(theUrl) && !theUrl.startsWith("#")) {
String unqualifiedVersionlessReference;
boolean isPlaceholderReference;
if (theUrl.startsWith("urn:")) {
isPlaceholderReference = true;
unqualifiedVersionlessReference = null;
} else {
isPlaceholderReference = false;
unqualifiedVersionlessReference =
new IdType(theUrl).toUnqualifiedVersionless().getValue();
}
List<BundleEntryParts> entries = BundleUtil.toListOfEntries(getContext(), (IBaseBundle) theAppContext); List<BundleEntryParts> entries = BundleUtil.toListOfEntries(getContext(), (IBaseBundle) theAppContext);
for (BundleEntryParts next : entries) { for (BundleEntryParts next : entries) {
if (next.getResource() != null) { if (next.getResource() != null) {
if (theUrl.startsWith("urn:uuid:")) { if (isPlaceholderReference) {
if (theUrl.equals(next.getUrl()) if (theUrl.equals(next.getUrl())
|| theUrl.equals( || theUrl.equals(
next.getResource().getIdElement().getValue())) { next.getResource().getIdElement().getValue())) {
return (T) next.getResource(); return (T) next.getResource();
} }
} else { } else {
if (theUrl.equals(next.getResource().getIdElement().getValue())) { if (unqualifiedVersionlessReference.equals(next.getResource()
.getIdElement()
.toUnqualifiedVersionless()
.getValue())) {
return (T) next.getResource(); return (T) next.getResource();
} }
} }

View File

@ -5793,8 +5793,15 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
* [base]/Bundle?composition.patient.identifier=foo * [base]/Bundle?composition.patient.identifier=foo
*/ */
@ParameterizedTest @ParameterizedTest
@CsvSource({"urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", "Patient/ABC"}) @CsvSource({
public void testCreateAndSearchForFullyChainedSearchParameter(String thePatientId) { "true , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b",
"false, urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b",
"true , Patient/ABC , Patient/ABC ",
"false, Patient/ABC , Patient/ABC ",
"true , Patient/ABC , http://example.com/fhir/Patient/ABC ",
"false, Patient/ABC , http://example.com/fhir/Patient/ABC ",
})
public void testCreateAndSearchForFullyChainedSearchParameter(boolean theUseFullChainInName, String thePatientId, String theFullUrl) {
// Setup 1 // Setup 1
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
@ -5819,13 +5826,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
composition.setSubject(new Reference(thePatientId)); composition.setSubject(new Reference(thePatientId));
Patient patient = new Patient(); Patient patient = new Patient();
patient.setId(new IdType(thePatientId)); patient.setId(new IdType(theFullUrl));
patient.addIdentifier().setSystem("http://foo").setValue("bar"); patient.addIdentifier().setSystem("http://foo").setValue("bar");
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.DOCUMENT); bundle.setType(Bundle.BundleType.DOCUMENT);
bundle.addEntry().setResource(composition); bundle
bundle.addEntry().setResource(patient); .addEntry()
.setResource(composition);
bundle
.addEntry()
.setFullUrl(theFullUrl)
.setResource(patient);
myBundleDao.create(bundle, mySrd); myBundleDao.create(bundle, mySrd);
@ -5833,35 +5845,40 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
bundle2.setType(Bundle.BundleType.DOCUMENT); bundle2.setType(Bundle.BundleType.DOCUMENT);
myBundleDao.create(bundle2, mySrd); myBundleDao.create(bundle2, mySrd);
// Verify 1 // Test
runInTransaction(() -> {
SearchParameterMap map;
if (theUseFullChainInName) {
map = SearchParameterMap.newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar"));
} else {
map = SearchParameterMap.newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar"));
}
IBundleProvider outcome = myBundleDao.search(map, mySrd);
// Verify
List<String> params = extractAllTokenIndexes();
assertThat(params.toString(), params, containsInAnyOrder(
"composition.patient.identifier http://foo|bar"
));
assertEquals(1, outcome.size());
}
private List<String> extractAllTokenIndexes() {
List<String> params = runInTransaction(() -> {
logAllTokenIndexes(); logAllTokenIndexes();
List<String> params = myResourceIndexedSearchParamTokenDao return myResourceIndexedSearchParamTokenDao
.findAll() .findAll()
.stream() .stream()
.filter(t -> t.getParamName().contains(".")) .filter(t -> t.getParamName().contains("."))
.map(t -> t.getParamName() + " " + t.getSystem() + "|" + t.getValue()) .map(t -> t.getParamName() + " " + t.getSystem() + "|" + t.getValue())
.toList(); .toList();
assertThat(params.toString(), params, containsInAnyOrder(
"composition.patient.identifier http://foo|bar"
));
}); });
return params;
// Test 2
IBundleProvider outcome;
SearchParameterMap map = SearchParameterMap
.newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar"));
outcome = myBundleDao.search(map, mySrd);
assertEquals(1, outcome.size());
map = SearchParameterMap
.newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar"));
outcome = myBundleDao.search(map, mySrd);
assertEquals(1, outcome.size());
} }
@Nested @Nested
public class TagBelowTests { public class TagBelowTests {