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:
parent
ec5402abd4
commit
d597cf2763
|
@ -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."
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue