Add support for JPA searches with _tag:not=foo

This commit is contained in:
jamesagnew 2016-04-07 09:50:05 -04:00
parent ad34e38dee
commit b81a343f5d
3 changed files with 80 additions and 40 deletions

View File

@ -28,6 +28,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -795,14 +796,9 @@ public class SearchBuilder {
} }
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTag> from = cq.from(ResourceTag.class);
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName));
List<Predicate> orPredicates = new ArrayList<Predicate>();
boolean paramInverted = false; boolean paramInverted = false;
List<Pair<String, String>> tokens = Lists.newArrayList();
for (IQueryParameterType nextOrParams : nextAndParams) { for (IQueryParameterType nextOrParams : nextAndParams) {
String code; String code;
String system; String system;
@ -818,57 +814,66 @@ public class SearchBuilder {
code = nextParam.getValue(); code = nextParam.getValue();
system = null; system = null;
} }
From<ResourceTag, TagDefinition> defJoin = from.join("myTag");
Predicate typePrediate = builder.equal(defJoin.get("myTagType"), tagType);
Predicate codePrediate = builder.equal(defJoin.get("myCode"), code);
if (isBlank(code)) {
continue;
}
if (isNotBlank(system)) {
Predicate systemPrediate = builder.equal(defJoin.get("mySystem"), system);
orPredicates.add(builder.and(typePrediate, systemPrediate, codePrediate));
} else {
orPredicates.add(builder.and(typePrediate, codePrediate));
}
if (isNotBlank(code)) {
tokens.add(Pair.of(system, code));
}
} }
if (orPredicates.isEmpty() == false) {
Predicate tagOptions = builder.or(toArray(orPredicates)); if (tokens.isEmpty()) {
andPredicates.add(tagOptions);
} else {
continue; continue;
} }
From<?, ResourceTable> defJoin;
if (paramInverted) { if (paramInverted) {
Subquery<Long> subQ = cq.subquery(Long.class); ourLog.debug("Searching for _tag:not");
Root<ResourceTag> subQfrom = cq.from(ResourceTag.class);
subQ.select(subQfrom.get("myResourceId").as(Long.class)); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
subQ.where(builder.and(toArray(andPredicates)));
Root<ResourceTable> newFrom = cq.from(ResourceTable.class); Root<ResourceTable> newFrom = cq.from(ResourceTable.class);
cq.select(newFrom.get("myId").as(Long.class)).where(builder.not(builder.in(newFrom.get("myId")).value(subQ)));
Subquery<Long> subQ = cq.subquery(Long.class);
Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
subQ.select(subQfrom.get("myResourceId").as(Long.class));
cq.select(newFrom.get("myId").as(Long.class));
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates = new ArrayList<Predicate>(); andPredicates = new ArrayList<Predicate>();
andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName)); andPredicates.add(builder.equal(newFrom.get("myResourceType"), myResourceName));
defJoin = newFrom; andPredicates.add(builder.not(builder.in(newFrom.get("myId")).value(subQ)));
Subquery<Long> defJoin = subQ.subquery(Long.class);
Root<TagDefinition> defJoinFrom = defJoin.from(TagDefinition.class);
defJoin.select(defJoinFrom.get("myId").as(Long.class));
subQ.where(subQfrom.get("myTagId").as(Long.class).in(defJoin));
List<Predicate> orPredicates = createPredicateTagList(defJoinFrom, builder, tagType, tokens);
defJoin.where(toArray(orPredicates));
cq.where(toArray(andPredicates));
TypedQuery<Long> q = myEntityManager.createQuery(cq); TypedQuery<Long> q = myEntityManager.createQuery(cq);
Set<Long> pids = new HashSet<Long>(q.getResultList()); Set<Long> pids = new HashSet<Long>(q.getResultList());
doSetPids(pids); doSetPids(pids);
continue; continue;
}
} else {
defJoin = from.join("myResource");
}
Predicate notDeletedPredicatePrediate = builder.isNull(defJoin.get("myDeleted")); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTag> from = cq.from(ResourceTag.class);
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates.add(builder.equal(from.get("myResourceType"), myResourceName));
From<ResourceTag, TagDefinition> defJoin = from.join("myTag");
Join<?, ResourceTable> defJoin2 = from.join("myResource");
Predicate notDeletedPredicatePrediate = builder.isNull(defJoin2.get("myDeleted"));
andPredicates.add(notDeletedPredicatePrediate); andPredicates.add(notDeletedPredicatePrediate);
List<Predicate> orPredicates = createPredicateTagList(defJoin, builder, tagType, tokens);
andPredicates.add(builder.or(toArray(orPredicates)));
if (theLastUpdated != null) { if (theLastUpdated != null) {
andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin)); andPredicates.addAll(createLastUpdatedPredicates(theLastUpdated, builder, defJoin2));
} }
createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class)); createPredicateResourceId(builder, cq, andPredicates, from.get("myResourceId").as(Long.class));
@ -884,6 +889,22 @@ public class SearchBuilder {
} }
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
Predicate typePrediate = theBuilder.equal(theDefJoin.get("myTagType"), theTagType);
List<Predicate> orPredicates = Lists.newArrayList();
for (Pair<String, String> next : theTokens) {
Predicate codePrediate = theBuilder.equal(theDefJoin.get("myCode"), next.getRight());
if (isNotBlank(next.getLeft())) {
Predicate systemPrediate = theBuilder.equal(theDefJoin.get("mySystem"), next.getLeft());
orPredicates.add(theBuilder.and(typePrediate, systemPrediate, codePrediate));
} else {
orPredicates.add(theBuilder.and(typePrediate, codePrediate));
}
}
return orPredicates;
}
private void addPredicateToken(String theParamName, List<? extends IQueryParameterType> theList) { private void addPredicateToken(String theParamName, List<? extends IQueryParameterType> theList) {
if (Boolean.TRUE.equals(theList.get(0).getMissing())) { if (Boolean.TRUE.equals(theList.get(0).getMissing())) {

View File

@ -2117,6 +2117,20 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertThat(patients, containsInAnyOrder(tag2id)); assertThat(patients, containsInAnyOrder(tag2id));
assertThat(patients, not(containsInAnyOrder(tag1id))); assertThat(patients, not(containsInAnyOrder(tag1id)));
} }
{
// Non existant tag
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam("urn:taglist", methodName + "FOO").setModifier(TokenParamModifier.NOT));
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id, tag2id));
}
{
// Common tag
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam("urn:taglist", methodName + "1b").setModifier(TokenParamModifier.NOT));
List<IIdType> patients = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
assertThat(patients, empty());
}
} }
@Test @Test

View File

@ -398,6 +398,11 @@
Thanks to GitHub user @ipropper for reporting and providing Thanks to GitHub user @ipropper for reporting and providing
a test case! a test case!
</action> </action>
<action type="add">
JPA server now supports searching for <![CDATA[<code>_tag:not=[tag]</code>]]>
which enables finding resources that to not have a given tag/profile/security tag.
Thanks to Lars Kristian Roland for the suggestion!
</action>
</release> </release>
<release version="1.4" date="2016-02-04"> <release version="1.4" date="2016-02-04">
<action type="add"> <action type="add">