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.MathContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@ -795,14 +796,9 @@ public class SearchBuilder {
}
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;
List<Pair<String, String>> tokens = Lists.newArrayList();
for (IQueryParameterType nextOrParams : nextAndParams) {
String code;
String system;
@ -818,57 +814,66 @@ public class SearchBuilder {
code = nextParam.getValue();
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(code)) {
tokens.add(Pair.of(system, code));
}
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 (orPredicates.isEmpty() == false) {
Predicate tagOptions = builder.or(toArray(orPredicates));
andPredicates.add(tagOptions);
} else {
if (tokens.isEmpty()) {
continue;
}
From<?, ResourceTable> defJoin;
if (paramInverted) {
Subquery<Long> subQ = cq.subquery(Long.class);
Root<ResourceTag> subQfrom = cq.from(ResourceTag.class);
subQ.select(subQfrom.get("myResourceId").as(Long.class));
subQ.where(builder.and(toArray(andPredicates)));
ourLog.debug("Searching for _tag:not");
CriteriaQuery<Long> cq = builder.createQuery(Long.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.add(builder.equal(from.get("myResourceType"), myResourceName));
defJoin = newFrom;
andPredicates.add(builder.equal(newFrom.get("myResourceType"), myResourceName));
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);
Set<Long> pids = new HashSet<Long>(q.getResultList());
doSetPids(pids);
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);
List<Predicate> orPredicates = createPredicateTagList(defJoin, builder, tagType, tokens);
andPredicates.add(builder.or(toArray(orPredicates)));
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));
@ -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) {
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, 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

View File

@ -398,6 +398,11 @@
Thanks to GitHub user @ipropper for reporting and providing
a test case!
</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 version="1.4" date="2016-02-04">
<action type="add">