From ea1f35beaac46d2c00537e6f8471b2f90819e9d9 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 31 Mar 2020 10:33:47 -0400 Subject: [PATCH] Work on multitenancy --- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 5 +- .../RequestPartitionHelperService.java | 8 + .../dao/predicate/BasePredicateBuilder.java | 26 +-- .../dao/predicate/PredicateBuilderCoords.java | 3 +- .../dao/predicate/PredicateBuilderDate.java | 3 +- .../dao/predicate/PredicateBuilderNumber.java | 3 +- .../predicate/PredicateBuilderQuantity.java | 3 +- .../predicate/PredicateBuilderReference.java | 3 +- .../dao/predicate/PredicateBuilderString.java | 5 +- .../dao/predicate/PredicateBuilderTag.java | 2 +- .../dao/predicate/PredicateBuilderToken.java | 5 +- .../dao/predicate/PredicateBuilderUri.java | 4 +- .../uhn/fhir/jpa/dao/predicate/QueryRoot.java | 2 +- .../fhir/jpa/dao/r4/PartitioningR4Test.java | 218 ++++++++++++++++-- .../jpa/model/entity/SearchParamPresent.java | 14 +- 15 files changed, 247 insertions(+), 57 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 3599a7310d2..d26608c71f6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -351,8 +351,7 @@ public class SearchBuilder implements ISearchBuilder { * If we have any joins to index tables, we get this behaviour already guaranteed so we don't * need an explicit predicate for it. */ - boolean haveNoIndexSearchParams = myParams.size() == 0 || myParams.keySet().stream().allMatch(t -> t.startsWith("_")); - if (haveNoIndexSearchParams) { + if (!myQueryRoot.hasIndexJoins()) { if (myParams.getEverythingMode() == null) { myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myResourceType"), myResourceName)); } @@ -895,7 +894,7 @@ public class SearchBuilder implements ISearchBuilder { myQueryRoot.addPredicate(predicate); } - myQueryRoot.setHasIndexJoins(true); + myQueryRoot.setHasIndexJoins(); Predicate predicate = myCriteriaBuilder.equal(join.get("myIndexString"), theIndexedString); myQueryRoot.addPredicate(predicate); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/partition/RequestPartitionHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/partition/RequestPartitionHelperService.java index b74f438ebf5..fbdfa51cc6b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/partition/RequestPartitionHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/partition/RequestPartitionHelperService.java @@ -53,6 +53,10 @@ public class RequestPartitionHelperService { */ @Nullable public PartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType) { + if (myPartitioningBlacklist.contains(theResourceType)) { + return null; + } + PartitionId partitionId = null; if (myDaoConfig.isPartitioningEnabled()) { @@ -73,6 +77,10 @@ public class RequestPartitionHelperService { */ @Nullable public PartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource) { + String resourceType = myFhirContext.getResourceDefinition(theResource).getName(); + if (myPartitioningBlacklist.contains(resourceType)) { + return null; + } PartitionId partitionId = null; if (myDaoConfig.isPartitioningEnabled()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/BasePredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/BasePredicateBuilder.java index ba2079b50c6..c3b0d75b933 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/BasePredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/BasePredicateBuilder.java @@ -25,11 +25,9 @@ import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IDao; import ca.uhn.fhir.jpa.dao.SearchBuilder; import ca.uhn.fhir.jpa.model.entity.BasePartitionable; -import ca.uhn.fhir.jpa.model.entity.BaseResourceIndex; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.PartitionId; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.SearchParamPresent; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @@ -45,6 +43,7 @@ import javax.annotation.PostConstruct; import javax.persistence.criteria.*; import java.math.BigDecimal; import java.math.MathContext; +import java.util.ArrayList; import java.util.List; abstract class BasePredicateBuilder { @@ -112,30 +111,29 @@ abstract class BasePredicateBuilder { return (Join) join; } - void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing) { -// if (myDontUseHashesForSearch) { -// Join paramPresentJoin = myQueryRoot.join("mySearchParamPresents", JoinType.LEFT); -// Join paramJoin = paramPresentJoin.join("mySearchParam", JoinType.LEFT); -// -// myQueryRoot.addPredicate(myBuilder.equal(paramJoin.get("myResourceName"), theResourceName)); -// myQueryRoot.addPredicate(myBuilder.equal(paramJoin.get("myParamName"), theParamName)); -// myQueryRoot.addPredicate(myBuilder.equal(paramPresentJoin.get("myPresent"), !theMissing)); -// } - + void addPredicateParamMissingForReference(String theResourceName, String theParamName, boolean theMissing, PartitionId thePartitionId) { Join paramPresentJoin = myQueryRoot.join("mySearchParamPresents", JoinType.LEFT); Expression hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class); Long hash = SearchParamPresent.calculateHashPresence(theResourceName, theParamName, !theMissing); - myQueryRoot.addPredicate(myCriteriaBuilder.equal(hashPresence, hash)); + + List predicates = new ArrayList<>(); + predicates.add(myCriteriaBuilder.equal(hashPresence, hash)); + + addPartitionIdPredicate(thePartitionId, paramPresentJoin, predicates); + + myQueryRoot.setHasIndexJoins(); + myQueryRoot.addPredicates(predicates); } - void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing, Join theJoin, PartitionId thePartitionId) { + void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, Join theJoin, PartitionId thePartitionId) { if (thePartitionId != null) { myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), thePartitionId.getPartitionId())); } myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName)); myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myParamName"), theParamName)); myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myMissing"), theMissing)); + myQueryRoot.setHasIndexJoins(); } Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From theFrom, Predicate thePredicate) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java index b00335fabe3..50e575ebba8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderCoords.java @@ -153,7 +153,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre Join join = createJoin(SearchBuilderJoinEnum.COORDS, theParamName); if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -173,6 +173,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates)); myQueryRoot.addPredicate(retVal); + myQueryRoot.setHasIndexJoins(); return retVal; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java index ec97488396c..34bd8e71503 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderDate.java @@ -77,7 +77,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi if (theList.get(0).getMissing() != null) { Boolean missing = theList.get(0).getMissing(); - addPredicateParamMissing(theResourceName, theParamName, missing, join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, missing, join, thePartitionId); return null; } @@ -97,6 +97,7 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi Predicate orPredicates = myCriteriaBuilder.or(toArray(codePredicates)); + myQueryRoot.setHasIndexJoins(); if (newJoin) { Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates); myQueryRoot.addPredicate(identityAndValuePredicate); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderNumber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderNumber.java index e2078552568..736243dea9f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderNumber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderNumber.java @@ -60,7 +60,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB Join join = createJoin(SearchBuilderJoinEnum.NUMBER, theParamName); if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -109,6 +109,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB } Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates)); + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(predicate); return predicate; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderQuantity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderQuantity.java index 11747869515..b868dba4349 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderQuantity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderQuantity.java @@ -58,7 +58,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat Join join = createJoin(SearchBuilderJoinEnum.QUANTITY, theParamName); if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -77,6 +77,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat } Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates)); + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(retVal); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java index 73708fabbaf..d641885a7c0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java @@ -137,7 +137,7 @@ class PredicateBuilderReference extends BasePredicateBuilder { } if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing()); + addPredicateParamMissingForReference(theResourceName, theParamName, theList.get(0).getMissing(), thePartitionId); return null; } @@ -227,6 +227,7 @@ class PredicateBuilderReference extends BasePredicateBuilder { codePredicates.add(myCriteriaBuilder.and(pathPredicate, pidPredicate)); } + myQueryRoot.setHasIndexJoins(); if (codePredicates.size() > 0) { Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates)); myQueryRoot.addPredicate(predicate); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderString.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderString.java index 349d84e5be7..4c49ada29a5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderString.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderString.java @@ -63,7 +63,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB Join join = createJoin(SearchBuilderJoinEnum.STRING, theParamName); if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -82,7 +82,10 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB } Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates)); + + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(retVal); + return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderTag.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderTag.java index b741702321c..e1e7e324312 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderTag.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderTag.java @@ -134,7 +134,6 @@ class PredicateBuilderTag extends BasePredicateBuilder { continue; } - // FIXME: add test for tag:not // FIXME: add test for :missing if (paramInverted) { ourLog.debug("Searching for _tag:not"); @@ -173,6 +172,7 @@ class PredicateBuilderTag extends BasePredicateBuilder { addPartitionIdPredicate(thePartitionId, tagJoin, predicates); } + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicates(predicates); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java index 0a167efd735..3d22b256e0c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderToken.java @@ -76,7 +76,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu if (theList.get(0).getMissing() != null) { Join join = createJoin(SearchBuilderJoinEnum.TOKEN, theParamName); - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -108,7 +108,10 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu codePredicates.addAll(singleCode); Predicate spPredicate = myCriteriaBuilder.or(toArray(codePredicates)); + + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(spPredicate); + return spPredicate; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderUri.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderUri.java index 18de2fe86d1..586b77d7998 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderUri.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderUri.java @@ -62,7 +62,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil Join join = createJoin(SearchBuilderJoinEnum.URI, theParamName); if (theList.get(0).getMissing() != null) { - addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); + addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, thePartitionId); return null; } @@ -169,6 +169,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil */ if (codePredicates.isEmpty()) { Predicate predicate = myCriteriaBuilder.isNull(join.get("myMissing").as(String.class)); + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(predicate); return null; } @@ -179,6 +180,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil theParamName, join, orPredicate); + myQueryRoot.setHasIndexJoins(); myQueryRoot.addPredicate(outerPredicate); return outerPredicate; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/QueryRoot.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/QueryRoot.java index 3eeeed71efe..320a763ed59 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/QueryRoot.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/QueryRoot.java @@ -96,7 +96,7 @@ public class QueryRoot { return myHasIndexJoins; } - public void setHasIndexJoins(boolean theHasIndexJoins) { + public void setHasIndexJoins() { myHasIndexJoins = true; } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningR4Test.java index f8c45582de5..8cdab1619a5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningR4Test.java @@ -12,11 +12,11 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -79,11 +79,14 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { myPartitionId = 3; myPartitionInterceptor = new MyInterceptor(); + myInterceptorRegistry.registerInterceptor(myPartitionInterceptor); } @Test public void testCreateResourceNoPartition() { + addCreatePartition(null, null); + Patient p = new Patient(); p.addIdentifier().setSystem("system").setValue("value"); p.setBirthDate(new Date()); @@ -343,6 +346,142 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { } } + @Test + public void testSearch_MissingParamString_SearchAllPartitions() { + IIdType patientIdNull = createPatient(null, withFamily("FAMILY")); + IIdType patientId1 = createPatient(1, withFamily("FAMILY")); + IIdType patientId2 = createPatient(2, withFamily("FAMILY")); + + // :missing=true + { + addReadPartition(null); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_ACTIVE, new StringParam().setMissing(true)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'")); + } + + // :missing=false + { + addReadPartition(null); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_FAMILY, new StringParam().setMissing(false)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'")); + } + } + + @Test + public void testSearch_MissingParamReference_SearchAllPartitions() { + IIdType patientIdNull = createPatient(null, withFamily("FAMILY")); + IIdType patientId1 = createPatient(1, withFamily("FAMILY")); + IIdType patientId2 = createPatient(2, withFamily("FAMILY")); + + // :missing=true + { + addReadPartition(null); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_GENERAL_PRACTITIONER, new StringParam().setMissing(true)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT")); + assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'")); + } + } + + + @Test + public void testSearch_MissingParamString_SearchOnePartition() { + createPatient(null, withFamily("FAMILY")); + IIdType patientId1 = createPatient(1, withFamily("FAMILY")); + createPatient(2, withFamily("FAMILY")); + + // :missing=true + { + addReadPartition(1); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_ACTIVE, new StringParam().setMissing(true)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientId1)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "myparamsto1_.PARTITION_ID='1'")); + assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='true'")); + } + + // :missing=false + { + addReadPartition(1); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_FAMILY, new StringParam().setMissing(false)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientId1)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "myparamsst1_.PARTITION_ID='1'")); + assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING='false'")); + } + } + + @Test + public void testSearch_MissingParamReference_SearchOnePartition() { + createPatient(null, withFamily("FAMILY")); + IIdType patientId1 = createPatient(1, withFamily("FAMILY")); + createPatient(2, withFamily("FAMILY")); + + // :missing=true + { + addReadPartition(1); + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Patient.SP_GENERAL_PRACTITIONER, new StringParam().setMissing(true)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientId1)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "mysearchpa1_.PARTITION_ID='1'")); + assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT")); + assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE='1919227773735728687'")); + } + } + + + @Test public void testSearch_NoParams_SearchAllPartitions() { IIdType patientIdNull = createPatient(null, withActiveTrue()); @@ -452,9 +591,9 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { @Test public void testSearch_TagParam_SearchOnePartition() { - IIdType patientIdNull = createPatient(null, withActiveTrue(), withTag("http://system", "code")); + createPatient(null, withActiveTrue(), withTag("http://system", "code")); IIdType patientId1 = createPatient(1, withActiveTrue(), withTag("http://system", "code")); - IIdType patientId2 = createPatient(2, withActiveTrue(), withTag("http://system", "code")); + createPatient(2, withActiveTrue(), withTag("http://system", "code")); addReadPartition(1); @@ -472,6 +611,56 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'")); } + @Test + public void testSearch_TagParamNot_SearchAllPartitions() { + IIdType patientIdNull = createPatient(null, withActiveTrue(), withTag("http://system", "code")); + IIdType patientId1 = createPatient(1, withActiveTrue(), withTag("http://system", "code")); + IIdType patientId2 = createPatient(2, withActiveTrue(), withTag("http://system", "code")); + createPatient(null, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + createPatient(1, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + createPatient(2, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + + addReadPartition(null); + + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Constants.PARAM_TAG, new TokenParam("http://system", "code2").setModifier(TokenParamModifier.NOT)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(0, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'")); + } + + @Test + public void testSearch_TagParamNot_SearchOnePartition() { + createPatient(null, withActiveTrue(), withTag("http://system", "code")); + IIdType patientId1 = createPatient(1, withActiveTrue(), withTag("http://system", "code")); + createPatient(2, withActiveTrue(), withTag("http://system", "code")); + createPatient(null, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + createPatient(1, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + createPatient(2, withActiveTrue(), withTag("http://system", "code"), withTag("http://system", "code2")); + + addReadPartition(1); + + myCaptureQueriesListener.clear(); + SearchParameterMap map = new SearchParameterMap(); + map.add(Constants.PARAM_TAG, new TokenParam("http://system", "code2").setModifier(TokenParamModifier.NOT)); + map.setLoadSynchronous(true); + IBundleProvider results = myPatientDao.search(map); + List ids = toUnqualifiedVersionlessIds(results); + assertThat(ids, Matchers.contains(patientId1)); + + String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); + ourLog.info("Search SQL:\n{}", searchSql); + assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID")); + assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM='http://system'")); + } + @Test public void testSearch_UniqueParam_SearchAllPartitions() { createUniqueCompositeSp(); @@ -557,13 +746,15 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { } - private void addCreatePartition(int thePartitionId, LocalDate thePartitionDate) { - registerInterceptorIfNeeded(); - myPartitionInterceptor.addCreatePartition(new PartitionId(thePartitionId, thePartitionDate)); + private void addCreatePartition(Integer thePartitionId, LocalDate thePartitionDate) { + PartitionId partitionId = null; + if (thePartitionId != null) { + partitionId = new PartitionId(thePartitionId, thePartitionDate); + } + myPartitionInterceptor.addCreatePartition(partitionId); } private void addReadPartition(Integer thePartitionId) { - registerInterceptorIfNeeded(); PartitionId partitionId = null; if (thePartitionId != null) { partitionId = new PartitionId(thePartitionId, null); @@ -571,16 +762,8 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { myPartitionInterceptor.addReadPartition(partitionId); } - private void registerInterceptorIfNeeded() { - if (!myInterceptorRegistry.getAllRegisteredInterceptors().contains(myPartitionInterceptor)) { - myInterceptorRegistry.registerInterceptor(myPartitionInterceptor); - } - } - public IIdType createPatient(Integer thePartitionId, Consumer... theModifiers) { - if (thePartitionId != null) { - addCreatePartition(thePartitionId, null); - } + addCreatePartition(thePartitionId, null); Patient p = new Patient(); for (Consumer next : theModifiers) { @@ -614,7 +797,7 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { } private Consumer withTag(String theSystem, String theCode) { - return t->t.getMeta().addTag(theSystem, theCode, theCode); + return t -> t.getMeta().addTag(theSystem, theCode, theCode); } @@ -626,7 +809,6 @@ public class PartitioningR4Test extends BaseJpaR4SystemTest { private final List myReadPartitionIds = new ArrayList<>(); public void addCreatePartition(PartitionId thePartitionId) { - Validate.notNull(thePartitionId); myCreatePartitionIds.add(thePartitionId); } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresent.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresent.java index 8a1c24a8091..1e956f92b0e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresent.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresent.java @@ -32,7 +32,7 @@ import java.io.Serializable; @Index(name = "IDX_RESPARMPRESENT_RESID", columnList = "RES_ID"), @Index(name = "IDX_RESPARMPRESENT_HASHPRES", columnList = "HASH_PRESENCE") }) -public class SearchParamPresent implements Serializable { +public class SearchParamPresent extends BasePartitionable implements Serializable { private static final long serialVersionUID = 1L; @@ -52,8 +52,6 @@ public class SearchParamPresent implements Serializable { private transient String myParamName; @Column(name = "HASH_PRESENCE") private Long myHashPresence; - @Embedded - private PartitionId myPartitionId; /** * Constructor @@ -112,18 +110,10 @@ public class SearchParamPresent implements Serializable { b.append("resPid", myResource.getIdDt().toUnqualifiedVersionless().getValue()); b.append("paramName", myParamName); b.append("present", myPresent); - b.append("tenant", myPartitionId); + b.append("partition", getPartitionId()); return b.build(); } - public PartitionId getPartitionId() { - return myPartitionId; - } - - public void setPartitionId(PartitionId thePartitionId) { - myPartitionId = thePartitionId; - } - public static long calculateHashPresence(String theResourceType, String theParamName, Boolean thePresent) { String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false); return BaseResourceIndexedSearchParam.hash(theResourceType, theParamName, string);