Jr 20211018 chained references 2 (#3099)
* Create index entries for outbound references of contained resources * build query for chained reference * fix case where the contained reference is an explicit id rather than a continued chain * fix contained index to use path names not search param names * make qualified search work * cleanup and changelog * recurse while creating indexes on contained resources * double link both contained * longer contained subchains * adding some failing test cases to illustrate the limitations of qualified searches * clean up merge cruft * changelog
This commit is contained in:
parent
d94284b2e8
commit
b267fdb752
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 3100
|
||||
jira: SMILE-3151
|
||||
title: "Previously, only contained resources that are referenced directly by the containing resource were being indexed.
|
||||
This enhancement indexes the fields of contained resources that are referenced by other contained resources and
|
||||
uses these new indices in chained searches. Note: in order to make use of this new capability, it must be enabled via
|
||||
a configuration parameter and the repository must be re-indexed."
|
|
@ -685,7 +685,7 @@ public class QueryStack {
|
|||
}
|
||||
|
||||
public Condition createPredicateReferenceForContainedResource(@Nullable DbColumn theSourceJoinColumn,
|
||||
String theResourceName, String theParamName, RuntimeSearchParam theSearchParam,
|
||||
String theResourceName, String theParamName, List<String> theQualifiers, RuntimeSearchParam theSearchParam,
|
||||
List<? extends IQueryParameterType> theList, SearchFilterParser.CompareOperation theOperation,
|
||||
RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
|
@ -760,7 +760,7 @@ public class QueryStack {
|
|||
throw new InvalidRequestException("Unknown search parameter name: " + theSearchParam.getName() + ".");
|
||||
}
|
||||
|
||||
List<String> qualifiers= Collections.singletonList(headQualifier);
|
||||
theQualifiers.add(headQualifier);
|
||||
|
||||
// 3. create the query
|
||||
Condition containedCondition = null;
|
||||
|
@ -796,7 +796,11 @@ public class QueryStack {
|
|||
break;
|
||||
case REFERENCE:
|
||||
String chainedParamName = theParamName + "." + targetParamName;
|
||||
containedCondition = createPredicateReference(theSourceJoinColumn, theResourceName, chainedParamName, qualifiers, trimmedParameters, theOperation, theRequest, theRequestPartitionId);
|
||||
containedCondition = createPredicateReference(theSourceJoinColumn, theResourceName, chainedParamName, theQualifiers, trimmedParameters, theOperation, theRequest, theRequestPartitionId);
|
||||
if (myModelConfig.isIndexOnContainedResourcesRecursively()) {
|
||||
containedCondition = toOrPredicate(containedCondition,
|
||||
createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, chainedParamName, theQualifiers, theSearchParam, trimmedParameters, theOperation, theRequest, theRequestPartitionId));
|
||||
}
|
||||
break;
|
||||
case HAS:
|
||||
case SPECIAL:
|
||||
|
@ -1140,11 +1144,11 @@ public class QueryStack {
|
|||
// See SMILE-2898 for details.
|
||||
// For now, leave the incorrect implementation alone, just in case someone is relying on it,
|
||||
// until the complete fix is available.
|
||||
andPredicates.add(createPredicateReferenceForContainedResource(null, theResourceName, theParamName, nextParamDef, nextAnd, null, theRequest, theRequestPartitionId));
|
||||
andPredicates.add(createPredicateReferenceForContainedResource(null, theResourceName, theParamName, new ArrayList<>(), nextParamDef, nextAnd, null, theRequest, theRequestPartitionId));
|
||||
} else if (isEligibleForContainedResourceSearch(nextAnd)) {
|
||||
andPredicates.add(toOrPredicate(
|
||||
createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, new ArrayList<>(), nextAnd, null, theRequest, theRequestPartitionId),
|
||||
createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, theParamName, nextParamDef, nextAnd, null, theRequest, theRequestPartitionId)
|
||||
createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, theParamName, new ArrayList<>(), nextParamDef, nextAnd, null, theRequest, theRequestPartitionId)
|
||||
));
|
||||
} else {
|
||||
andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, new ArrayList<>(), nextAnd, null, theRequest, theRequestPartitionId));
|
||||
|
|
|
@ -569,6 +569,10 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
|
|||
String qualifier = theParamQualifiers.get(0);
|
||||
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, paramNameHead);
|
||||
if (param == null) {
|
||||
// This can happen during recursion, if not all the possible target types of one link in the chain support the next link
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<String> tailPaths = param.getTargets().stream()
|
||||
.filter(t -> isBlank(qualifier) || qualifier.equals(t))
|
||||
.map(t -> createResourceLinkPaths(t, paramNameTail, theParamQualifiers.subList(1, theParamQualifiers.size())))
|
||||
|
|
|
@ -10,12 +10,14 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Device;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Location;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.Organization;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
|
@ -46,6 +48,7 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
|
||||
myModelConfig.setIndexOnContainedResources(false);
|
||||
myModelConfig.setIndexOnContainedResources(new ModelConfig().isIndexOnContainedResources());
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(new ModelConfig().isIndexOnContainedResourcesRecursively());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -116,6 +119,50 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testShouldResolveATwoLinkChainWithQualifiersWithAContainedResource() throws Exception {
|
||||
// TODO: This test fails because of a known limitation in qualified searches over contained resources.
|
||||
// Type information for intermediate resources in the chain is not being retained in the indexes.
|
||||
// setup
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getContained().add(p);
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.setValue(new StringType("Test"));
|
||||
obs.getSubject().setReference("#pat");
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Location loc = new Location();
|
||||
loc.setId("loc");
|
||||
loc.setName("Smith");
|
||||
|
||||
Observation obs2 = new Observation();
|
||||
obs2.getContained().add(loc);
|
||||
obs2.getCode().setText("Observation 2");
|
||||
obs2.setValue(new StringType("Test"));
|
||||
obs2.getSubject().setReference("#loc");
|
||||
|
||||
myObservationDao.create(obs2, mySrd);
|
||||
}
|
||||
|
||||
String url = "/Observation?subject:Patient.name=Smith";
|
||||
|
||||
// execute
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveATwoLinkChainToAContainedReference() throws Exception {
|
||||
// Adding support for this case in SMILE-3151
|
||||
|
@ -260,6 +307,45 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAThreeLinkChainWithAllContainedResources() throws Exception {
|
||||
|
||||
// setup
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Organization org = new Organization();
|
||||
org.setId("org");
|
||||
org.setName("HealthCo");
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.getManagingOrganization().setReference("#org");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getContained().add(p);
|
||||
obs.getContained().add(org);
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.getSubject().setReference("#pat");
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
String url = "/Observation?subject.organization.name=HealthCo";
|
||||
|
||||
// execute
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAThreeLinkChainWithQualifiersWhereAllResourcesStandAlone() throws Exception {
|
||||
|
||||
|
@ -405,6 +491,117 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testShouldResolveAThreeLinkChainWithQualifiersWithAContainedResourceAtTheBeginning_NotDistinctSourcePaths() throws Exception {
|
||||
// TODO: This test fails because of a known limitation in qualified searches over contained resources.
|
||||
// Type information for intermediate resources in the chain is not being retained in the indexes.
|
||||
|
||||
// Adding support for this case in SMILE-3151
|
||||
|
||||
// setup
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Organization org = new Organization();
|
||||
org.setId(IdType.newRandomUuid());
|
||||
org.setName("HealthCo");
|
||||
myOrganizationDao.create(org, mySrd);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.getManagingOrganization().setReference(org.getId());
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getContained().add(p);
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.getSubject().setReference("#pat");
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Location loc = new Location();
|
||||
loc.setId("loc");
|
||||
loc.getManagingOrganization().setReference(org.getId());
|
||||
|
||||
Observation obs2 = new Observation();
|
||||
obs2.getContained().add(loc);
|
||||
obs2.getCode().setText("Observation 2");
|
||||
obs2.getSubject().setReference("#loc");
|
||||
|
||||
myObservationDao.create(obs2, mySrd);
|
||||
}
|
||||
|
||||
String url = "/Observation?subject:Patient.organization:Organization.name=HealthCo";
|
||||
|
||||
// execute
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testShouldResolveAThreeLinkChainWithQualifiersWithAllContainedResources() throws Exception {
|
||||
// TODO: This test fails because of a known limitation in qualified searches over contained resources.
|
||||
// Type information for intermediate resources in the chain is not being retained in the indexes.
|
||||
|
||||
// setup
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Organization org = new Organization();
|
||||
org.setId("org");
|
||||
org.setName("HealthCo");
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.getManagingOrganization().setReference("#org");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getContained().add(p);
|
||||
obs.getContained().add(org);
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.getSubject().setReference("#pat");
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Organization org2 = new Organization();
|
||||
org2.setId("org");
|
||||
org2.setName("HealthCo");
|
||||
|
||||
Device d = new Device();
|
||||
d.setId("dev");
|
||||
d.getOwner().setReference("#org");
|
||||
|
||||
Observation obs2 = new Observation();
|
||||
obs2.getContained().add(d);
|
||||
obs2.getContained().add(org2);
|
||||
obs2.getCode().setText("Observation 2");
|
||||
obs2.getSubject().setReference("#dev");
|
||||
|
||||
myObservationDao.create(obs2, mySrd);
|
||||
}
|
||||
|
||||
String url = "/Observation?subject:Patient.organization:Organization.name=HealthCo";
|
||||
|
||||
// execute
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
myCaptureQueriesListener.logSelectQueries();
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAFourLinkChainWhereAllResourcesStandAlone() throws Exception {
|
||||
|
||||
|
@ -485,6 +682,47 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAFourLinkChainWhereTheLastTwoReferencesAreContained() throws Exception {
|
||||
|
||||
// setup
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Organization org = new Organization();
|
||||
org.setId("parent");
|
||||
org.setName("HealthCo");
|
||||
|
||||
Organization partOfOrg = new Organization();
|
||||
partOfOrg.setId("child");
|
||||
partOfOrg.getPartOf().setReference("#parent");
|
||||
|
||||
Patient p = new Patient();
|
||||
p.getContained().add(org);
|
||||
p.getContained().add(partOfOrg);
|
||||
p.setId(IdType.newRandomUuid());
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.getManagingOrganization().setReference("#child");
|
||||
myPatientDao.create(p, mySrd);
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.getSubject().setReference(p.getId());
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
String url = "/Observation?subject.organization.partof.name=HealthCo";
|
||||
|
||||
// execute
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAFourLinkChainWithAContainedResourceInTheMiddle() throws Exception {
|
||||
|
||||
|
@ -531,6 +769,47 @@ public class ChainingR4SearchTest extends BaseJpaR4Test {
|
|||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldResolveAFourLinkChainWhereAllReferencesAreContained() throws Exception {
|
||||
|
||||
// setup
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
IIdType oid1;
|
||||
|
||||
{
|
||||
Organization org = new Organization();
|
||||
org.setId("parent");
|
||||
org.setName("HealthCo");
|
||||
|
||||
Organization partOfOrg = new Organization();
|
||||
partOfOrg.setId("child");
|
||||
partOfOrg.getPartOf().setReference("#parent");
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.addName().setFamily("Smith").addGiven("John");
|
||||
p.getManagingOrganization().setReference("#child");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getContained().add(org);
|
||||
obs.getContained().add(partOfOrg);
|
||||
obs.getContained().add(p);
|
||||
obs.getCode().setText("Observation 1");
|
||||
obs.getSubject().setReference("#pat");
|
||||
|
||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
String url = "/Observation?subject.organization.partof.name=HealthCo";
|
||||
|
||||
// execute
|
||||
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
|
||||
|
||||
// validate
|
||||
assertEquals(1L, oids.size());
|
||||
assertThat(oids, contains(oid1.getIdPart()));
|
||||
}
|
||||
|
||||
private List<String> searchAndReturnUnqualifiedVersionlessIdValues(String theUrl) throws IOException {
|
||||
List<String> ids = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
|||
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
|
||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||
|
@ -67,6 +68,7 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
|||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
|
||||
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
|
||||
myModelConfig.setIndexOnContainedResources(new ModelConfig().isIndexOnContainedResources());
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(new ModelConfig().isIndexOnContainedResourcesRecursively());
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,6 +124,87 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateLinkCreatesAppropriatePaths_ContainedResourceRecursive() {
|
||||
myModelConfig.setIndexOnContainedResources(true);
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setId("pat");
|
||||
p.setActive(true);
|
||||
p.getNameFirstRep().setFamily("Smith");
|
||||
|
||||
Observation containedObs = new Observation();
|
||||
containedObs.setId("#obs");
|
||||
containedObs.setSubject(new Reference("#pat"));
|
||||
|
||||
Encounter enc = new Encounter();
|
||||
enc.getContained().add(containedObs);
|
||||
enc.getContained().add(p);
|
||||
enc.addReasonReference(new Reference("#obs"));
|
||||
myEncounterDao.create(enc, mySrd);
|
||||
|
||||
runInTransaction(() ->{
|
||||
List<ResourceIndexedSearchParamString> allParams = myResourceIndexedSearchParamStringDao.findAll();
|
||||
Optional<ResourceIndexedSearchParamString> link = allParams
|
||||
.stream()
|
||||
.filter(t -> "reason-reference.subject.family".equals(t.getParamName()))
|
||||
.findFirst();
|
||||
assertTrue(link.isPresent());
|
||||
assertEquals("Smith", link.get().getValueExact());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateLinkCreatesAppropriatePaths_ContainedResourceRecursive_DoesNotLoop() {
|
||||
myModelConfig.setIndexOnContainedResources(true);
|
||||
myModelConfig.setIndexOnContainedResourcesRecursively(true);
|
||||
|
||||
Organization org1 = new Organization();
|
||||
org1.setId("org1");
|
||||
org1.setName("EscherCorp");
|
||||
org1.setPartOf(new Reference("#org2"));
|
||||
|
||||
Organization org2 = new Organization();
|
||||
org2.setId("org2");
|
||||
org2.setName("M.C.Escher Unlimited");
|
||||
org2.setPartOf(new Reference("#org1"));
|
||||
|
||||
Observation containedObs = new Observation();
|
||||
containedObs.setId("#obs");
|
||||
containedObs.addPerformer(new Reference("#org1"));
|
||||
|
||||
Encounter enc = new Encounter();
|
||||
enc.getContained().add(containedObs);
|
||||
enc.getContained().add(org1);
|
||||
enc.getContained().add(org2);
|
||||
enc.addReasonReference(new Reference("#obs"));
|
||||
myEncounterDao.create(enc, mySrd);
|
||||
|
||||
runInTransaction(() ->{
|
||||
List<ResourceIndexedSearchParamString> allParams = myResourceIndexedSearchParamStringDao.findAll();
|
||||
Optional<ResourceIndexedSearchParamString> firstOrg = allParams
|
||||
.stream()
|
||||
.filter(t -> "reason-reference.performer.name".equals(t.getParamName()))
|
||||
.findFirst();
|
||||
assertTrue(firstOrg.isPresent());
|
||||
assertEquals("EscherCorp", firstOrg.get().getValueExact());
|
||||
|
||||
Optional<ResourceIndexedSearchParamString> secondOrg = allParams
|
||||
.stream()
|
||||
.filter(t -> "reason-reference.performer.partof.name".equals(t.getParamName()))
|
||||
.findFirst();
|
||||
assertTrue(secondOrg.isPresent());
|
||||
assertEquals("M.C.Escher Unlimited", secondOrg.get().getValueExact());
|
||||
|
||||
Optional<ResourceIndexedSearchParamString> thirdOrg = allParams
|
||||
.stream()
|
||||
.filter(t -> "reason-reference.performer.partof.partof.name".equals(t.getParamName()))
|
||||
.findFirst();
|
||||
assertFalse(thirdOrg.isPresent());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConditionalCreateWithPlusInUrl() {
|
||||
|
|
|
@ -100,6 +100,7 @@ public class ModelConfig {
|
|||
private Map<String, Set<String>> myTypeToAutoVersionReferenceAtPaths = Collections.emptyMap();
|
||||
private boolean myRespectVersionsForSearchIncludes;
|
||||
private boolean myIndexOnContainedResources = false;
|
||||
private boolean myIndexOnContainedResourcesRecursively = false;
|
||||
private boolean myAllowMdmExpansion = false;
|
||||
|
||||
/**
|
||||
|
@ -785,6 +786,26 @@ public class ModelConfig {
|
|||
myIndexOnContainedResources = theIndexOnContainedResources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should recursive indexing and searching on contained resources be enabled on this server.
|
||||
* This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*/
|
||||
public boolean isIndexOnContainedResourcesRecursively() {
|
||||
return myIndexOnContainedResourcesRecursively;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should indexing and searching on contained resources be enabled on this server.
|
||||
* This may have performance impacts, and should be enabled only if it is needed. Default is <code>false</code>.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*/
|
||||
public void setIndexOnContainedResourcesRecursively(boolean theIndexOnContainedResourcesRecursively) {
|
||||
myIndexOnContainedResourcesRecursively = theIndexOnContainedResourcesRecursively;
|
||||
}
|
||||
|
||||
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
|
||||
Validate.notBlank(theUrl, "Base URL must not be null or empty");
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ import javax.annotation.Nonnull;
|
|||
import javax.validation.constraints.NotNull;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
@ -133,6 +134,10 @@ public class SearchParamExtractorService {
|
|||
// 1. get all contained resources
|
||||
Collection<IBaseResource> containedResources = terser.getAllEmbeddedResources(theResource, false);
|
||||
|
||||
extractSearchIndexParametersForContainedResources(theRequestDetails, theParams, theResource, theEntity, containedResources, new HashSet<>());
|
||||
}
|
||||
|
||||
private void extractSearchIndexParametersForContainedResources(RequestDetails theRequestDetails, ResourceIndexedSearchParams theParams, IBaseResource theResource, ResourceTable theEntity, Collection<IBaseResource> containedResources, Collection<IBaseResource> theAlreadySeenResources) {
|
||||
// 2. Find referenced search parameters
|
||||
ISearchParamExtractor.SearchParamSet<PathAndRef> referencedSearchParamSet = mySearchParamExtractor.extractResourceLinks(theResource, true);
|
||||
|
||||
|
@ -152,17 +157,29 @@ public class SearchParamExtractorService {
|
|||
if (containedResource == null)
|
||||
continue;
|
||||
|
||||
// 3.2.1 if we've already processed this resource upstream, do not process it again, to prevent infinite loops
|
||||
if (theAlreadySeenResources.contains(containedResource)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currParams = new ResourceIndexedSearchParams();
|
||||
|
||||
// 3.3 create indexes for the current contained resource
|
||||
extractSearchIndexParameters(theRequestDetails, currParams, containedResource, theEntity);
|
||||
|
||||
// 3.4 added reference name as a prefix for the contained resource if any
|
||||
// 3.4 recurse to process any other contained resources referenced by this one
|
||||
if (myModelConfig.isIndexOnContainedResourcesRecursively()) {
|
||||
HashSet<IBaseResource> nextAlreadySeenResources = new HashSet<>(theAlreadySeenResources);
|
||||
nextAlreadySeenResources.add(containedResource);
|
||||
extractSearchIndexParametersForContainedResources(theRequestDetails, currParams, containedResource, theEntity, containedResources, nextAlreadySeenResources);
|
||||
}
|
||||
|
||||
// 3.5 added reference name as a prefix for the contained resource if any
|
||||
// e.g. for Observation.subject contained reference
|
||||
// the SP_NAME = subject.family
|
||||
currParams.updateSpnamePrefixForIndexedOnContainedResource(spnamePrefix);
|
||||
|
||||
// 3.5 merge to the mainParams
|
||||
// 3.6 merge to the mainParams
|
||||
// NOTE: the spname prefix is different
|
||||
mergeParams(currParams, theParams);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue