From ad34e38dee6efd76bb5837cfbca2265f378408c8 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 6 Apr 2016 07:55:42 -0400 Subject: [PATCH] Work on _tag:not=foo --- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 102 +++++++++++++++++- .../FhirResourceDaoDstu3SearchNoFtTest.java | 32 ++++++ 2 files changed, 130 insertions(+), 4 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 9af5a3c38b8..1ceb779abfb 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 @@ -58,6 +58,7 @@ import javax.persistence.criteria.Subquery; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -66,6 +67,8 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import com.google.common.collect.Lists; + import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; @@ -118,6 +121,7 @@ import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; 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.param.UriParam; import ca.uhn.fhir.rest.param.UriParamQualifierEnum; import ca.uhn.fhir.rest.server.Constants; @@ -711,6 +715,64 @@ public class SearchBuilder { throw new IllegalArgumentException("Param name: " + theParamName); // shouldn't happen } + /* + CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = builder.createQuery(Long.class); + Root from = cq.from(ResourceTable.class); + cq.select(from.get("myId").as(Long.class)); + + Subquery subQ = cq.subquery(Long.class); + Root subQfrom = subQ.from(theParamTable); + subQ.select(subQfrom.get("myResourcePid").as(Long.class)); + Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName); + Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName); + subQ.where(builder.and(subQtype, subQname)); + + List predicates = new ArrayList(); + predicates.add(builder.not(builder.in(from.get("myId")).value(subQ))); + predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); + predicates.add(builder.isNull(from.get("myDeleted"))); + createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class)); + */ + + List> notTags = Lists.newArrayList(); + for (List nextAndParams : theList) { + for (IQueryParameterType nextOrParams : nextAndParams) { + if (nextOrParams instanceof TokenParam) { + TokenParam param = (TokenParam)nextOrParams; + if (param.getModifier() == TokenParamModifier.NOT) { + if (isNotBlank(param.getSystem()) || isNotBlank(param.getValue())) { + notTags.add(Pair.of(param.getSystem(), param.getValue())); + } + } + } + } + } + + /* + * We have a parameter of ResourceType?_tag:not=foo + * This means match resources that don't have the given tag(s) + */ + if (notTags.isEmpty() == false) { +// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); +// CriteriaQuery cq = builder.createQuery(Long.class); +// Root from = cq.from(ResourceTable.class); +// cq.select(from.get("myId").as(Long.class)); +// +// Subquery subQ = cq.subquery(Long.class); +// Root subQfrom = subQ.from(ResourceTag.class); +// subQ.select(subQfrom.get("myResourceId").as(Long.class)); +// Predicate subQname = builder.equal(subQfrom.get("myParamName"), theParamName); +// Predicate subQtype = builder.equal(subQfrom.get("myResourceType"), myResourceName); +// subQ.where(builder.and(subQtype, subQname)); +// +// List predicates = new ArrayList(); +// predicates.add(builder.not(builder.in(from.get("myId")).value(subQ))); +// predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); +// predicates.add(builder.isNull(from.get("myDeleted"))); +// createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class)); + } + for (List nextAndParams : theList) { boolean haveTags = false; for (IQueryParameterType nextParamUncasted : nextAndParams) { @@ -735,12 +797,12 @@ public class SearchBuilder { CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery(Long.class); Root from = cq.from(ResourceTag.class); - cq.select(from.get("myResourceId").as(Long.class)); List andPredicates = new ArrayList(); andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName)); - + List orPredicates = new ArrayList(); + boolean paramInverted = false; for (IQueryParameterType nextOrParams : nextAndParams) { String code; String system; @@ -748,6 +810,9 @@ public class SearchBuilder { TokenParam nextParam = (TokenParam) nextOrParams; code = nextParam.getValue(); system = nextParam.getSystem(); + if (nextParam.getModifier() == TokenParamModifier.NOT) { + paramInverted = true; + } } else { UriParam nextParam = (UriParam) nextOrParams; code = nextParam.getValue(); @@ -768,12 +833,40 @@ public class SearchBuilder { } if (orPredicates.isEmpty() == false) { - andPredicates.add(builder.or(toArray(orPredicates))); + Predicate tagOptions = builder.or(toArray(orPredicates)); + andPredicates.add(tagOptions); + } else { + continue; } - From defJoin = from.join("myResource"); + From defJoin; + if (paramInverted) { + Subquery subQ = cq.subquery(Long.class); + Root subQfrom = cq.from(ResourceTag.class); + subQ.select(subQfrom.get("myResourceId").as(Long.class)); + subQ.where(builder.and(toArray(andPredicates))); + + Root newFrom = cq.from(ResourceTable.class); + cq.select(newFrom.get("myId").as(Long.class)).where(builder.not(builder.in(newFrom.get("myId")).value(subQ))); + + andPredicates = new ArrayList(); + andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName)); + defJoin = newFrom; + + TypedQuery q = myEntityManager.createQuery(cq); + Set pids = new HashSet(q.getResultList()); + doSetPids(pids); + continue; + + + + } else { + defJoin = from.join("myResource"); + } + Predicate notDeletedPredicatePrediate = builder.isNull(defJoin.get("myDeleted")); andPredicates.add(notDeletedPredicatePrediate); + if (theLastUpdated != null) { andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin)); } @@ -781,6 +874,7 @@ public class SearchBuilder { createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class)); Predicate masterCodePredicate = builder.and(toArray(andPredicates)); + cq.select(from.get("myResourceId").as(Long.class)); cq.where(masterCodePredicate); TypedQuery q = myEntityManager.createQuery(cq); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index be24971a28f..cd683b76cb8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -84,6 +84,7 @@ import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.param.UriParamQualifierEnum; import ca.uhn.fhir.rest.server.Constants; @@ -2087,6 +2088,37 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { } + @Test + public void testSearchWithTagParameterMissing() { + String methodName = "testSearchWithTagParameterMissing"; + + IIdType tag1id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "1a", null); + org.getMeta().addTag("urn:taglist", methodName + "1b", null); + tag1id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + IIdType tag2id; + { + Organization org = new Organization(); + org.getNameElement().setValue("FOO"); + org.getMeta().addTag("urn:taglist", methodName + "1b", null); + tag2id = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); + } + + { + // One tag + SearchParameterMap params = new SearchParameterMap(); + params.add("_tag", new TokenParam("urn:taglist", methodName + "1a").setModifier(TokenParamModifier.NOT)); + List patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); + assertThat(patients, containsInAnyOrder(tag2id)); + assertThat(patients, not(containsInAnyOrder(tag1id))); + } + } + @Test public void testSearchWithToken() { IIdType notMissing;