force the resource table to be the root of the query if we might be traversing a contained reference

This commit is contained in:
Jason Roberts 2021-09-03 11:26:46 -04:00
parent 5604cabbe8
commit 8a9e4e4328
4 changed files with 18 additions and 9 deletions

View File

@ -1139,8 +1139,14 @@ public class QueryStack {
// 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));
} else if (nextAnd.stream().filter(t -> t instanceof ReferenceParam).map(t -> (ReferenceParam) t).anyMatch(t -> t.getChain().contains("."))) {
// FIXME for now, restrict contained reference traversal to the last reference in the chain
andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId));
} else {
andPredicates.add(createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId));
andPredicates.add(toOrPredicate(
createPredicateReference(theSourceJoinColumn, theResourceName, theParamName, nextAnd, null, theRequest, theRequestPartitionId),
createPredicateReferenceForContainedResource(theSourceJoinColumn, theResourceName, theParamName, nextParamDef, nextAnd, null, theRequest, theRequestPartitionId)
));
}
}
break;

View File

@ -373,7 +373,7 @@ public class SearchBuilder implements ISearchBuilder {
SearchQueryBuilder sqlBuilder = new SearchQueryBuilder(myContext, myDaoConfig.getModelConfig(), myPartitionSettings, myRequestPartitionId, sqlBuilderResourceName, mySqlBuilderFactory, myDialectProvider, theCount);
QueryStack queryStack3 = new QueryStack(theParams, myDaoConfig, myDaoConfig.getModelConfig(), myContext, sqlBuilder, mySearchParamRegistry, myPartitionSettings);
if (theParams.keySet().size() > 1 || theParams.getSort() != null || theParams.keySet().contains(Constants.PARAM_HAS)) {
if (theParams.keySet().size() > 1 || theParams.getSort() != null || theParams.keySet().contains(Constants.PARAM_HAS) || isTraverseContainedReferenceAtRoot(theParams)) {
List<RuntimeSearchParam> activeComboParams = mySearchParamRegistry.getActiveComboSearchParams(myResourceName, theParams.keySet());
if (activeComboParams.isEmpty()) {
sqlBuilder.setNeedResourceTableRoot(true);
@ -483,6 +483,13 @@ public class SearchBuilder implements ISearchBuilder {
return Optional.of(executor);
}
private boolean isTraverseContainedReferenceAtRoot(SearchParameterMap theParams) {
return myModelConfig.isIndexOnContainedResources() && theParams.values().stream()
.flatMap(Collection::stream)
.flatMap(Collection::stream)
.anyMatch(t -> t instanceof ReferenceParam);
}
private List<Long> normalizeIdListForLastNInClause(List<Long> lastnResourceIds) {
/*
The following is a workaround to a known issue involving Hibernate. If queries are used with "in" clauses with large and varying

View File

@ -402,10 +402,6 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder {
andPredicates.add(childQueryFactory.searchForIdsWithAndOr(myColumnTargetResourceId, subResourceName, chain, chainParamValues, theRequest, theRequestPartitionId, SearchContainedModeEnum.FALSE));
orPredicates.add(toAndPredicate(andPredicates));
if (getModelConfig().isIndexOnContainedResources() && theReferenceParam.getChain().contains(".")) {
orPredicates.add(childQueryFactory.createPredicateReferenceForContainedResource(myColumnTargetResourceId, subResourceName, chain, param, orValues, null, theRequest, theRequestPartitionId));
}
}
if (candidateTargetTypes.isEmpty()) {

View File

@ -12,9 +12,9 @@ import org.hl7.fhir.r4.model.IdType;
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;
@ -93,7 +93,6 @@ public class ChainedContainedR4SearchTest extends BaseJpaR4Test {
}
@Test
@Disabled
public void testShouldResolveATwoLinkChainWithAContainedResource() throws Exception {
IIdType oid1;
@ -105,6 +104,7 @@ public class ChainedContainedR4SearchTest extends BaseJpaR4Test {
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();
@ -113,6 +113,7 @@ public class ChainedContainedR4SearchTest extends BaseJpaR4Test {
}
String url = "/Observation?subject.name=Smith";
// String url = "/Observation?subject.name=Smith&value-string=Test";
myCaptureQueriesListener.clear();
List<String> oids = searchAndReturnUnqualifiedVersionlessIdValues(url);
ourLog.info(">>> " + myCaptureQueriesListener.getSelectQueriesForCurrentThread());
@ -198,7 +199,6 @@ public class ChainedContainedR4SearchTest extends BaseJpaR4Test {
}
@Test
@Disabled
public void testShouldResolveAThreeLinkChainWithAContainedResourceAtTheBeginningOfTheChain() throws Exception {
// This case seems like it would be less frequent in production, but we don't want to
// paint ourselves into a corner where we require the contained link to be the last