Merge pull request #1760 from jamesagnew/bugfix/1752-support-chained-parameters-inside-reverse-chained-parameter
Bugfix/1752 support chained parameters inside reverse chained parameter
This commit is contained in:
commit
4583cb9939
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1760
|
||||
title: "Adds support for chained parameters in a _has query. For example
|
||||
`GET /Patient?_has:Observation:subject:device.identifier=1234-5`. Adds a performance warning on any queries that use
|
||||
an unqualified resource in a chain which ends up resolving to 2 or more candidate target types."
|
||||
|
||||
|
|
@ -81,7 +81,6 @@ public class MatchResourceUrlService {
|
|||
.add(StorageProcessingMessage.class, message);
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INFO, params);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
@Autowired
|
||||
private PredicateBuilderFactory myPredicateBuilderFactory;
|
||||
private List<ResourcePersistentId> myAlsoIncludePids;
|
||||
private CriteriaBuilder myBuilder;
|
||||
private CriteriaBuilder myCriteriaBuilder;
|
||||
private IDao myCallingDao;
|
||||
private SearchParameterMap myParams;
|
||||
private String mySearchUuid;
|
||||
|
@ -195,15 +195,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
Dstu3DistanceHelper.setNearDistance(myResourceType, theParams);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if there is a unique key associated with the set
|
||||
* of parameters passed in
|
||||
*/
|
||||
boolean couldBeEligibleForCompositeUniqueSpProcessing =
|
||||
myDaoConfig.isUniqueIndexesEnabled() &&
|
||||
myParams.getEverythingMode() == null &&
|
||||
myParams.isAllParametersHaveNoModifier();
|
||||
if (couldBeEligibleForCompositeUniqueSpProcessing) {
|
||||
// Attempt to lookup via composite unique key.
|
||||
if (isCompositeUniqueSpCandidate()) {
|
||||
attemptCompositeUniqueSpProcessing(theParams, theRequest);
|
||||
}
|
||||
|
||||
|
@ -213,7 +206,16 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
List<List<IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
searchForIdsWithAndOr(myResourceName, nextParamName, andOrParams, theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A search is a candidate for Composite Unique SP if unique indexes are enabled, there is no EverythingMode, and the
|
||||
* parameters all have no modifiers.
|
||||
*/
|
||||
private boolean isCompositeUniqueSpCandidate() {
|
||||
return myDaoConfig.isUniqueIndexesEnabled() &&
|
||||
myParams.getEverythingMode() == null &&
|
||||
myParams.isAllParametersHaveNoModifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -243,10 +245,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return new QueryIterator(theSearchRuntimeDetails, theRequest);
|
||||
}
|
||||
|
||||
private void init(SearchParameterMap theParams, String theTheSearchUuid) {
|
||||
private void init(SearchParameterMap theParams, String theSearchUuid) {
|
||||
myParams = theParams;
|
||||
myBuilder = myEntityManager.getCriteriaBuilder();
|
||||
mySearchUuid = theTheSearchUuid;
|
||||
myCriteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||
mySearchUuid = theSearchUuid;
|
||||
myPredicateBuilder = new PredicateBuilder(this, myPredicateBuilderFactory);
|
||||
}
|
||||
|
||||
|
@ -261,50 +263,49 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (sort != null) {
|
||||
assert !theCount;
|
||||
|
||||
outerQuery = myBuilder.createQuery(Long.class);
|
||||
outerQuery = myCriteriaBuilder.createQuery(Long.class);
|
||||
myQueryRoot.push(outerQuery);
|
||||
if (theCount) {
|
||||
outerQuery.multiselect(myBuilder.countDistinct(myQueryRoot.getRoot()));
|
||||
outerQuery.multiselect(myCriteriaBuilder.countDistinct(myQueryRoot.getRoot()));
|
||||
} else {
|
||||
outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class));
|
||||
}
|
||||
|
||||
List<Order> orders = Lists.newArrayList();
|
||||
|
||||
createSort(myBuilder, myQueryRoot, sort, orders);
|
||||
createSort(myCriteriaBuilder, myQueryRoot, sort, orders);
|
||||
if (orders.size() > 0) {
|
||||
outerQuery.orderBy(orders);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
outerQuery = myBuilder.createQuery(Long.class);
|
||||
outerQuery = myCriteriaBuilder.createQuery(Long.class);
|
||||
myQueryRoot.push(outerQuery);
|
||||
if (theCount) {
|
||||
outerQuery.multiselect(myBuilder.countDistinct(myQueryRoot.getRoot()));
|
||||
outerQuery.multiselect(myCriteriaBuilder.countDistinct(myQueryRoot.getRoot()));
|
||||
} else {
|
||||
outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class));
|
||||
// KHS This distinct call is causing performance issues in large installations
|
||||
// outerQuery.distinct(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (myParams.getEverythingMode() != null) {
|
||||
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
|
||||
if (myParams.get(IAnyResource.SP_RES_ID) != null) {
|
||||
StringParam idParm = (StringParam) myParams.get(IAnyResource.SP_RES_ID).get(0).get(0);
|
||||
ResourcePersistentId pid = myIdHelperService.resolveResourcePersistentIds(myResourceName, idParm.getValue());
|
||||
StringParam idParam = (StringParam) myParams.get(IAnyResource.SP_RES_ID).get(0).get(0);
|
||||
ResourcePersistentId pid = myIdHelperService.resolveResourcePersistentIds(myResourceName, idParam.getValue());
|
||||
if (myAlsoIncludePids == null) {
|
||||
myAlsoIncludePids = new ArrayList<>(1);
|
||||
}
|
||||
myAlsoIncludePids.add(pid);
|
||||
myQueryRoot.addPredicate(myBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid.getIdAsLong()));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid.getIdAsLong()));
|
||||
} else {
|
||||
Predicate targetTypePredicate = myBuilder.equal(join.get("myTargetResourceType").as(String.class), myResourceName);
|
||||
Predicate sourceTypePredicate = myBuilder.equal(myQueryRoot.get("myResourceType").as(String.class), myResourceName);
|
||||
myQueryRoot.addPredicate(myBuilder.or(sourceTypePredicate, targetTypePredicate));
|
||||
Predicate targetTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType").as(String.class), myResourceName);
|
||||
Predicate sourceTypePredicate = myCriteriaBuilder.equal(myQueryRoot.get("myResourceType").as(String.class), myResourceName);
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.or(sourceTypePredicate, targetTypePredicate));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -348,17 +349,17 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
boolean haveNoIndexSearchParams = myParams.size() == 0 || myParams.keySet().stream().allMatch(t -> t.startsWith("_"));
|
||||
if (haveNoIndexSearchParams) {
|
||||
if (myParams.getEverythingMode() == null) {
|
||||
myQueryRoot.addPredicate(myBuilder.equal(myQueryRoot.get("myResourceType"), myResourceName));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myResourceType"), myResourceName));
|
||||
}
|
||||
myQueryRoot.addPredicate(myBuilder.isNull(myQueryRoot.get("myDeleted")));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(myQueryRoot.get("myDeleted")));
|
||||
}
|
||||
|
||||
// Last updated
|
||||
DateRangeParam lu = myParams.getLastUpdated();
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myBuilder, myQueryRoot.getRoot());
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myCriteriaBuilder, myQueryRoot.getRoot());
|
||||
myQueryRoot.addPredicates(lastUpdatedPredicates);
|
||||
|
||||
myQueryRoot.where(myBuilder.and(myQueryRoot.getPredicateArray()));
|
||||
myQueryRoot.where(myCriteriaBuilder.and(myQueryRoot.getPredicateArray()));
|
||||
|
||||
/*
|
||||
* Now perform the search
|
||||
|
@ -880,7 +881,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexedString) {
|
||||
myQueryRoot.setHasIndexJoins(true);
|
||||
Join<ResourceTable, ResourceIndexedCompositeStringUnique> join = myQueryRoot.join("myParamsCompositeStringUnique", JoinType.LEFT);
|
||||
Predicate predicate = myBuilder.equal(join.get("myIndexString"), theIndexedString);
|
||||
Predicate predicate = myCriteriaBuilder.equal(join.get("myIndexString"), theIndexedString);
|
||||
myQueryRoot.addPredicate(predicate);
|
||||
|
||||
// Remove any empty parameters remaining after this
|
||||
|
@ -907,7 +908,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
public CriteriaBuilder getBuilder() {
|
||||
return myBuilder;
|
||||
return myCriteriaBuilder;
|
||||
}
|
||||
|
||||
public QueryRoot getQueryRoot() {
|
||||
|
@ -984,7 +985,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private final SearchRuntimeDetails mySearchRuntimeDetails;
|
||||
private final RequestDetails myRequest;
|
||||
private final boolean myHaveRawSqlHooks;
|
||||
private final boolean myHavePerftraceFoundIdHook;
|
||||
private final boolean myHavePerfTraceFoundIdHook;
|
||||
private boolean myFirst = true;
|
||||
private IncludesIterator myIncludesIterator;
|
||||
private ResourcePersistentId myNext;
|
||||
|
@ -1005,7 +1006,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myStillNeedToFetchIncludes = true;
|
||||
}
|
||||
|
||||
myHavePerftraceFoundIdHook = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
|
||||
myHavePerfTraceFoundIdHook = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_SEARCH_FOUND_ID, myInterceptorBroadcaster, myRequest);
|
||||
myHaveRawSqlHooks = JpaInterceptorBroadcaster.hasHooks(Pointcut.JPA_PERFTRACE_RAW_SQL, myInterceptorBroadcaster, myRequest);
|
||||
|
||||
}
|
||||
|
@ -1047,7 +1048,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (myNext == null) {
|
||||
while (myResultsIterator.hasNext()) {
|
||||
Long nextLong = myResultsIterator.next();
|
||||
if (myHavePerftraceFoundIdHook) {
|
||||
if (myHavePerfTraceFoundIdHook) {
|
||||
HookParams params = new HookParams()
|
||||
.add(Integer.class, System.identityHashCode(this))
|
||||
.add(Object.class, nextLong);
|
||||
|
|
|
@ -109,10 +109,8 @@ public class IdHelperService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Given a resource type and ID, looks up the resource and returns a {@link IResourceLookup}. This
|
||||
* object contains the internal PID for the resource and the resource deletion status, making it sufficient
|
||||
* for persisting resource links between resources without adding any further database calls after the
|
||||
* single one performed by this call.
|
||||
* Given a forced ID, convert it to it's Long value. Since you are allowed to use string IDs for resources, we need to
|
||||
* convert those to the underlying Long values that are stored, for lookup and comparison purposes.
|
||||
*
|
||||
* @throws ResourceNotFoundException If the ID can not be found
|
||||
*/
|
||||
|
@ -167,10 +165,10 @@ public class IdHelperService {
|
|||
if (myDaoConfig.getResourceClientIdStrategy() != DaoConfig.ClientIdStrategyEnum.ANY) {
|
||||
theIds
|
||||
.stream()
|
||||
.filter(t -> isValidPid(t))
|
||||
.map(t -> t.getIdPartAsLong())
|
||||
.map(t -> new ResourcePersistentId(t))
|
||||
.forEach(t -> retVal.add(t));
|
||||
.filter(IdHelperService::isValidPid)
|
||||
.map(IIdType::getIdPartAsLong)
|
||||
.map(ResourcePersistentId::new)
|
||||
.forEach(retVal::add);
|
||||
}
|
||||
|
||||
ListMultimap<String, String> typeToIds = organizeIdsByResourceType(theIds);
|
||||
|
@ -274,15 +272,10 @@ public class IdHelperService {
|
|||
.map(t -> t.getIdPartAsLong())
|
||||
.collect(Collectors.toList());
|
||||
if (!pids.isEmpty()) {
|
||||
|
||||
Collection<Object[]> lookups = myResourceTableDao.findLookupFieldsByResourcePid(pids);
|
||||
for (Object[] next : lookups) {
|
||||
String resourceType = (String) next[0];
|
||||
Long resourcePid = (Long) next[1];
|
||||
Date deletedAt = (Date) next[2];
|
||||
retVal.add(new ResourceLookup(resourceType, resourcePid, deletedAt));
|
||||
}
|
||||
|
||||
myResourceTableDao.findLookupFieldsByResourcePid(pids)
|
||||
.stream()
|
||||
.map(lookup -> new ResourceLookup((String)lookup[0], (Long)lookup[1], (Date)lookup[2]))
|
||||
.forEach(retVal::add);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
boolean myDontUseHashesForSearch;
|
||||
final IDao myCallingDao;
|
||||
final CriteriaBuilder myBuilder;
|
||||
final CriteriaBuilder myCriteriaBuilder;
|
||||
final QueryRoot myQueryRoot;
|
||||
final Class<? extends IBaseResource> myResourceType;
|
||||
final String myResourceName;
|
||||
|
@ -60,7 +60,7 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
BasePredicateBuilder(SearchBuilder theSearchBuilder) {
|
||||
myCallingDao = theSearchBuilder.getCallingDao();
|
||||
myBuilder = theSearchBuilder.getBuilder();
|
||||
myCriteriaBuilder = theSearchBuilder.getBuilder();
|
||||
myQueryRoot = theSearchBuilder.getQueryRoot();
|
||||
myResourceType = theSearchBuilder.getResourceType();
|
||||
myResourceName = theSearchBuilder.getResourceName();
|
||||
|
@ -122,27 +122,27 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
Expression<Long> hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(theResourceName, theParamName, !theMissing);
|
||||
myQueryRoot.addPredicate(myBuilder.equal(hashPresence, hash));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(hashPresence, hash));
|
||||
}
|
||||
|
||||
void addPredicateParamMissing(String theResourceName, String theParamName, boolean theMissing, Join<ResourceTable, ? extends BaseResourceIndexedSearchParam> theJoin) {
|
||||
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myParamName"), theParamName));
|
||||
myQueryRoot.addPredicate(myBuilder.equal(theJoin.get("myMissing"), theMissing));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myParamName"), theParamName));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myMissing"), theMissing));
|
||||
}
|
||||
|
||||
Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, Predicate thePredicate) {
|
||||
if (myDontUseHashesForSearch) {
|
||||
Predicate resourceTypePredicate = myBuilder.equal(theFrom.get("myResourceType"), theResourceName);
|
||||
Predicate paramNamePredicate = myBuilder.equal(theFrom.get("myParamName"), theParamName);
|
||||
Predicate outerPredicate = myBuilder.and(resourceTypePredicate, paramNamePredicate, thePredicate);
|
||||
Predicate resourceTypePredicate = myCriteriaBuilder.equal(theFrom.get("myResourceType"), theResourceName);
|
||||
Predicate paramNamePredicate = myCriteriaBuilder.equal(theFrom.get("myParamName"), theParamName);
|
||||
Predicate outerPredicate = myCriteriaBuilder.and(resourceTypePredicate, paramNamePredicate, thePredicate);
|
||||
return outerPredicate;
|
||||
}
|
||||
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
Predicate hashIdentityPredicate = myBuilder.equal(theFrom.get("myHashIdentity"), hashIdentity);
|
||||
return myBuilder.and(hashIdentityPredicate, thePredicate);
|
||||
Predicate hashIdentityPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentity"), hashIdentity);
|
||||
return myCriteriaBuilder.and(hashIdentityPredicate, thePredicate);
|
||||
}
|
||||
|
||||
Predicate createPredicateNumeric(String theResourceName,
|
||||
|
|
|
@ -161,13 +161,13 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
|||
Predicate singleCode = createPredicateCoords(nextOr,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myBuilder,
|
||||
myCriteriaBuilder,
|
||||
join
|
||||
);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
Predicate retVal = myBuilder.or(toArray(codePredicates));
|
||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -85,13 +85,13 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
|||
Predicate p = createPredicateDate(params,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myBuilder,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
operation);
|
||||
codePredicates.add(p);
|
||||
}
|
||||
|
||||
Predicate orPredicates = myBuilder.or(toArray(codePredicates));
|
||||
Predicate orPredicates = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
|
||||
if (newJoin) {
|
||||
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates);
|
||||
|
|
|
@ -94,7 +94,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
|
|||
|
||||
String invalidMessageName = "invalidNumberPrefix";
|
||||
|
||||
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theParamName, join, myBuilder, nextOr, prefix, value, fromObj, invalidMessageName);
|
||||
Predicate predicateNumeric = createPredicateNumeric(theResourceName, theParamName, join, myCriteriaBuilder, nextOr, prefix, value, fromObj, invalidMessageName);
|
||||
Predicate predicateOuter = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, predicateNumeric);
|
||||
codePredicates.add(predicateOuter);
|
||||
|
||||
|
@ -104,7 +104,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
|
|||
|
||||
}
|
||||
|
||||
Predicate predicate = myBuilder.or(toArray(codePredicates));
|
||||
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(predicate);
|
||||
return predicate;
|
||||
}
|
||||
|
|
|
@ -66,13 +66,13 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
Predicate singleCode = createPredicateQuantity(nextOr,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myBuilder,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
operation);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
Predicate retVal = myBuilder.or(toArray(codePredicates));
|
||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
@ -171,13 +171,13 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
|||
Predicate hashPredicate;
|
||||
if (!isBlank(systemValue) && !isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(theResourceName, theParamName, systemValue, unitsValue);
|
||||
hashPredicate = myBuilder.equal(theFrom.get("myHashIdentitySystemAndUnits"), hash);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentitySystemAndUnits"), hash);
|
||||
} else if (!isBlank(unitsValue)) {
|
||||
long hash = ResourceIndexedSearchParamQuantity.calculateHashUnits(theResourceName, theParamName, unitsValue);
|
||||
hashPredicate = myBuilder.equal(theFrom.get("myHashIdentityAndUnits"), hash);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentityAndUnits"), hash);
|
||||
} else {
|
||||
long hash = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
hashPredicate = myBuilder.equal(theFrom.get("myHashIdentity"), hash);
|
||||
hashPredicate = myCriteriaBuilder.equal(theFrom.get("myHashIdentity"), hash);
|
||||
}
|
||||
|
||||
cmpValue = defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
|
||||
|
|
|
@ -21,14 +21,19 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.dao.*;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
|
||||
import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
|
@ -41,7 +46,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -53,6 +60,7 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
import javax.persistence.criteria.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
|
@ -69,6 +77,8 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
MatchUrlService myMatchUrlService;
|
||||
@Autowired
|
||||
DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
private final PredicateBuilder myPredicateBuilder;
|
||||
|
||||
|
@ -87,6 +97,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
SearchFilterParser.CompareOperation operation,
|
||||
RequestDetails theRequest) {
|
||||
|
||||
//Is this just to ensure the chain has been split correctly???
|
||||
assert theParamName.contains(".") == false;
|
||||
|
||||
if ((operation != null) &&
|
||||
|
@ -164,7 +175,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
} else {
|
||||
pidPredicate = join.get("myTargetResourcePid").in(ResourcePersistentId.toLongList(targetPids)).not();
|
||||
}
|
||||
codePredicates.add(myBuilder.and(pathPredicate, pidPredicate));
|
||||
codePredicates.add(myCriteriaBuilder.and(pathPredicate, pidPredicate));
|
||||
}
|
||||
|
||||
// Resources by fully qualified URL
|
||||
|
@ -182,11 +193,11 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
} else {
|
||||
pidPredicate = join.get("myTargetResourceUrl").in(targetQualifiedUrls).not();
|
||||
}
|
||||
codePredicates.add(myBuilder.and(pathPredicate, pidPredicate));
|
||||
codePredicates.add(myCriteriaBuilder.and(pathPredicate, pidPredicate));
|
||||
}
|
||||
|
||||
if (codePredicates.size() > 0) {
|
||||
Predicate predicate = myBuilder.or(toArray(codePredicates));
|
||||
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(predicate);
|
||||
return predicate;
|
||||
} else {
|
||||
|
@ -198,9 +209,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private Predicate addPredicateReferenceWithChain(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList, Join<ResourceTable, ResourceLink> theJoin, List<Predicate> theCodePredicates, ReferenceParam theRef, RequestDetails theRequest) {
|
||||
/**
|
||||
* This is for handling queries like the following: /Observation?device.identifier=urn:system|foo in which we use a chain
|
||||
* on the device.
|
||||
*/
|
||||
private Predicate addPredicateReferenceWithChain(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList, Join<ResourceTable, ResourceLink> theJoin, List<Predicate> theCodePredicates, ReferenceParam theReferenceParam, RequestDetails theRequest) {
|
||||
final List<Class<? extends IBaseResource>> resourceTypes;
|
||||
if (!theRef.hasResourceType()) {
|
||||
if (!theReferenceParam.hasResourceType()) {
|
||||
|
||||
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
|
||||
resourceTypes = new ArrayList<>();
|
||||
|
@ -259,19 +274,19 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
|
||||
} else {
|
||||
try {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theRef.getResourceType());
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theReferenceParam.getResourceType());
|
||||
resourceTypes = new ArrayList<>(1);
|
||||
resourceTypes.add(resDef.getImplementingClass());
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException("Invalid resource type: " + theRef.getResourceType());
|
||||
throw new InvalidRequestException("Invalid resource type: " + theReferenceParam.getResourceType());
|
||||
}
|
||||
}
|
||||
|
||||
boolean foundChainMatch = false;
|
||||
|
||||
List<Class<? extends IBaseResource>> candidateTargetTypes = new ArrayList<>();
|
||||
for (Class<? extends IBaseResource> nextType : resourceTypes) {
|
||||
|
||||
String chain = theRef.getChain();
|
||||
String chain = theReferenceParam.getChain();
|
||||
String remainingChain = null;
|
||||
int chainDotIndex = chain.indexOf('.');
|
||||
if (chainDotIndex != -1) {
|
||||
|
@ -317,24 +332,46 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
orValues.add(chainValue);
|
||||
}
|
||||
|
||||
|
||||
Subquery<Long> subQ = createLinkSubquery(foundChainMatch, chain, subResourceName, orValues, theRequest);
|
||||
|
||||
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, theJoin);
|
||||
Predicate pidPredicate = theJoin.get("myTargetResourcePid").in(subQ);
|
||||
Predicate andPredicate = myBuilder.and(pathPredicate, pidPredicate);
|
||||
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, pidPredicate);
|
||||
theCodePredicates.add(andPredicate);
|
||||
|
||||
candidateTargetTypes.add(nextType);
|
||||
}
|
||||
|
||||
if (!foundChainMatch) {
|
||||
throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theRef.getChain()));
|
||||
throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theReferenceParam.getChain()));
|
||||
}
|
||||
|
||||
Predicate predicate = myBuilder.or(toArray(theCodePredicates));
|
||||
if (candidateTargetTypes.size() > 1) {
|
||||
warnAboutPerformanceOnUnqualifiedResources(theParamName, theRequest, candidateTargetTypes);
|
||||
}
|
||||
|
||||
Predicate predicate = myCriteriaBuilder.or(toArray(theCodePredicates));
|
||||
myQueryRoot.addPredicate(predicate);
|
||||
return predicate;
|
||||
}
|
||||
|
||||
private void warnAboutPerformanceOnUnqualifiedResources(String theParamName, RequestDetails theRequest, List<Class<? extends IBaseResource>> theCandidateTargetTypes) {
|
||||
String message = new StringBuilder()
|
||||
.append("This search uses an unqualified resource(a parameter in a chain without a resource type). ")
|
||||
.append("This is less efficient than using a qualified type. ")
|
||||
.append("[" + theParamName + "] resolves to ["+ theCandidateTargetTypes.stream().map(Class::getSimpleName).collect(Collectors.joining(",")) +"].")
|
||||
.append("If you know what you're looking for, try qualifying it like this: ")
|
||||
.append(theCandidateTargetTypes.stream().map(cls -> "[" +cls.getSimpleName() +":"+theParamName+"]").collect(Collectors.joining(" or ")))
|
||||
.toString();
|
||||
StorageProcessingMessage msg = new StorageProcessingMessage()
|
||||
.setMessage(message);
|
||||
HookParams params = new HookParams()
|
||||
.add(RequestDetails.class, theRequest)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||
.add(StorageProcessingMessage.class, msg);
|
||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_WARNING, params);
|
||||
}
|
||||
|
||||
Predicate createResourceLinkPathPredicate(String theResourceName, String theParamName, From<?, ? extends ResourceLink> from) {
|
||||
return createResourceLinkPathPredicate(myContext, theParamName, from, theResourceName);
|
||||
}
|
||||
|
@ -398,8 +435,8 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
andOrParams.add(theOrValues);
|
||||
|
||||
// Create the subquery predicates
|
||||
myQueryRoot.addPredicate(myBuilder.equal(myQueryRoot.get("myResourceType"), theSubResourceName));
|
||||
myQueryRoot.addPredicate(myBuilder.isNull(myQueryRoot.get("myDeleted")));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myResourceType"), theSubResourceName));
|
||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(myQueryRoot.get("myDeleted")));
|
||||
|
||||
if (theFoundChainMatch) {
|
||||
searchForIdsWithAndOr(theSubResourceName, theChain, andOrParams, theRequest);
|
||||
|
@ -557,9 +594,9 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
theResourceName, theRequest);
|
||||
|
||||
if (((SearchFilterParser.FilterLogical) theFilter).getOperation() == SearchFilterParser.FilterLogicalOperation.and) {
|
||||
return myBuilder.and(xPredicate, yPredicate);
|
||||
return myCriteriaBuilder.and(xPredicate, yPredicate);
|
||||
} else if (((SearchFilterParser.FilterLogical) theFilter).getOperation() == SearchFilterParser.FilterLogicalOperation.or) {
|
||||
return myBuilder.or(xPredicate, yPredicate);
|
||||
return myCriteriaBuilder.or(xPredicate, yPredicate);
|
||||
}
|
||||
} else if (theFilter instanceof SearchFilterParser.FilterParameterGroup) {
|
||||
return processFilter(((SearchFilterParser.FilterParameterGroup) theFilter).getContained(),
|
||||
|
@ -744,10 +781,10 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
SourceParam sourceParameter = new SourceParam(nextParameter.getValueAsQueryToken(myContext));
|
||||
String sourceUri = sourceParameter.getSourceUri();
|
||||
String requestId = sourceParameter.getRequestId();
|
||||
Predicate sourceUriPredicate = myBuilder.equal(join.get("mySourceUri"), sourceUri);
|
||||
Predicate requestIdPredicate = myBuilder.equal(join.get("myRequestId"), requestId);
|
||||
Predicate sourceUriPredicate = myCriteriaBuilder.equal(join.get("mySourceUri"), sourceUri);
|
||||
Predicate requestIdPredicate = myCriteriaBuilder.equal(join.get("myRequestId"), requestId);
|
||||
if (isNotBlank(sourceUri) && isNotBlank(requestId)) {
|
||||
codePredicates.add(myBuilder.and(sourceUriPredicate, requestIdPredicate));
|
||||
codePredicates.add(myCriteriaBuilder.and(sourceUriPredicate, requestIdPredicate));
|
||||
} else if (isNotBlank(sourceUri)) {
|
||||
codePredicates.add(sourceUriPredicate);
|
||||
} else if (isNotBlank(requestId)) {
|
||||
|
@ -755,7 +792,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
Predicate retVal = myBuilder.or(toArray(codePredicates));
|
||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
@ -791,11 +828,17 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
}
|
||||
|
||||
assert parameterName != null;
|
||||
|
||||
//Ensure that the name of the search param
|
||||
// (e.g. the `code` in Patient?_has:Observation:subject:code=sys|val)
|
||||
// exists on the target resource type.
|
||||
RuntimeSearchParam owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramName);
|
||||
if (owningParameterDef == null) {
|
||||
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + parameterName);
|
||||
}
|
||||
|
||||
//Ensure that the name of the back-referenced search param on the target (e.g. the `subject` in Patient?_has:Observation:subject:code=sys|val)
|
||||
//exists on the target resource.
|
||||
owningParameterDef = mySearchParamRegistry.getSearchParamByName(targetResourceDefinition, paramReference);
|
||||
if (owningParameterDef == null) {
|
||||
throw new InvalidRequestException("Unknown parameter name: " + targetResourceType + ':' + paramReference);
|
||||
|
@ -810,18 +853,30 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
for (IQueryParameterOr<IQueryParameterType> next : parsedParam.getValuesAsQueryTokens()) {
|
||||
orValues.addAll(next.getValuesAsQueryTokens());
|
||||
}
|
||||
//Handle internal chain inside the has.
|
||||
if (parameterName.contains(".")) {
|
||||
String chainedPartOfParameter = getChainedPart(parameterName);
|
||||
orValues.stream()
|
||||
.filter(qp -> qp instanceof ReferenceParam)
|
||||
.map(qp -> (ReferenceParam)qp)
|
||||
.forEach(rp -> rp.setChain(getChainedPart(chainedPartOfParameter)));
|
||||
}
|
||||
|
||||
Subquery<Long> subQ = myPredicateBuilder.createLinkSubquery(parameterName, targetResourceType, orValues, theRequest);
|
||||
|
||||
Subquery<Long> subQ = myPredicateBuilder.createLinkSubquery(paramName, targetResourceType, orValues, theRequest);
|
||||
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinksAsTarget", JoinType.LEFT);
|
||||
|
||||
Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join);
|
||||
Predicate sourceTypePredicate = myBuilder.equal(join.get("myTargetResourceType"), theResourceType);
|
||||
Predicate sourceTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType"), theResourceType);
|
||||
Predicate sourcePidPredicate = join.get("mySourceResourcePid").in(subQ);
|
||||
Predicate andPredicate = myBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate);
|
||||
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate);
|
||||
myQueryRoot.addPredicate(andPredicate);
|
||||
}
|
||||
}
|
||||
|
||||
private String getChainedPart(String parameter) {
|
||||
return parameter.substring(parameter.indexOf(".") + 1);
|
||||
}
|
||||
|
||||
private void addPredicateComposite(String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd) {
|
||||
// TODO: fail if missing is set for a composite query
|
||||
|
||||
|
@ -846,24 +901,24 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
|||
switch (theParam.getParamType()) {
|
||||
case STRING: {
|
||||
From<ResourceIndexedSearchParamString, ResourceIndexedSearchParamString> stringJoin = theRoot.join("myParamsString", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam.getName(), myBuilder, stringJoin);
|
||||
retVal = myPredicateBuilder.createPredicateString(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, stringJoin);
|
||||
break;
|
||||
}
|
||||
case TOKEN: {
|
||||
From<ResourceIndexedSearchParamToken, ResourceIndexedSearchParamToken> tokenJoin = theRoot.join("myParamsToken", JoinType.INNER);
|
||||
List<IQueryParameterType> tokens = Collections.singletonList(leftValue);
|
||||
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam.getName(), myBuilder, tokenJoin);
|
||||
retVal = myBuilder.and(tokenPredicates.toArray(new Predicate[0]));
|
||||
Collection<Predicate> tokenPredicates = myPredicateBuilder.createPredicateToken(tokens, theResourceName, theParam.getName(), myCriteriaBuilder, tokenJoin);
|
||||
retVal = myCriteriaBuilder.and(tokenPredicates.toArray(new Predicate[0]));
|
||||
break;
|
||||
}
|
||||
case DATE: {
|
||||
From<ResourceIndexedSearchParamDate, ResourceIndexedSearchParamDate> dateJoin = theRoot.join("myParamsDate", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateDate(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
retVal = myPredicateBuilder.createPredicateDate(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case QUANTITY: {
|
||||
From<ResourceIndexedSearchParamQuantity, ResourceIndexedSearchParamQuantity> dateJoin = theRoot.join("myParamsQuantity", JoinType.INNER);
|
||||
retVal = myPredicateBuilder.createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myBuilder, dateJoin);
|
||||
retVal = myPredicateBuilder.createPredicateQuantity(leftValue, theResourceName, theParam.getName(), myCriteriaBuilder, dateJoin);
|
||||
break;
|
||||
}
|
||||
case COMPOSITE:
|
||||
|
|
|
@ -110,7 +110,7 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
|||
if (allOrPids != null && allOrPids.isEmpty()) {
|
||||
|
||||
// This will never match
|
||||
nextPredicate = myBuilder.equal(theRoot.get("myId").as(Long.class), -1);
|
||||
nextPredicate = myCriteriaBuilder.equal(theRoot.get("myId").as(Long.class), -1);
|
||||
|
||||
} else if (allOrPids != null) {
|
||||
|
||||
|
@ -121,11 +121,11 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
|||
default:
|
||||
case eq:
|
||||
codePredicates.add(theRoot.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(allOrPids)));
|
||||
nextPredicate = myBuilder.and(toArray(codePredicates));
|
||||
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
||||
break;
|
||||
case ne:
|
||||
codePredicates.add(theRoot.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(allOrPids)).not());
|
||||
nextPredicate = myBuilder.and(toArray(codePredicates));
|
||||
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,13 +71,13 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
|||
Predicate singleCode = createPredicateString(theParameter,
|
||||
theResourceName,
|
||||
theParamName,
|
||||
myBuilder,
|
||||
myCriteriaBuilder,
|
||||
join,
|
||||
operation);
|
||||
codePredicates.add(singleCode);
|
||||
}
|
||||
|
||||
Predicate retVal = myBuilder.or(toArray(codePredicates));
|
||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -134,8 +134,8 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
|||
subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
||||
|
||||
myQueryRoot.addPredicate(
|
||||
myBuilder.not(
|
||||
myBuilder.in(
|
||||
myCriteriaBuilder.not(
|
||||
myCriteriaBuilder.in(
|
||||
myQueryRoot.get("myId")
|
||||
).value(subQ)
|
||||
)
|
||||
|
@ -147,7 +147,7 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
|||
|
||||
subQ.where(subQfrom.get("myTagId").as(Long.class).in(defJoin));
|
||||
|
||||
Predicate tagListPredicate = createPredicateTagList(defJoinFrom, myBuilder, tagType, tokens);
|
||||
Predicate tagListPredicate = createPredicateTagList(defJoinFrom, myCriteriaBuilder, tagType, tokens);
|
||||
defJoin.where(tagListPredicate);
|
||||
|
||||
continue;
|
||||
|
@ -156,7 +156,7 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
|||
Join<ResourceTable, ResourceTag> tagJoin = myQueryRoot.join("myTags", JoinType.LEFT);
|
||||
From<ResourceTag, TagDefinition> defJoin = tagJoin.join("myTag");
|
||||
|
||||
Predicate tagListPredicate = createPredicateTagList(defJoin, myBuilder, tagType, tokens);
|
||||
Predicate tagListPredicate = createPredicateTagList(defJoin, myCriteriaBuilder, tagType, tokens);
|
||||
myQueryRoot.addPredicate(tagListPredicate);
|
||||
|
||||
}
|
||||
|
|
|
@ -98,11 +98,11 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
}
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
||||
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myBuilder, join, operation);
|
||||
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, operation);
|
||||
assert singleCode != null;
|
||||
codePredicates.addAll(singleCode);
|
||||
|
||||
Predicate spPredicate = myBuilder.or(toArray(codePredicates));
|
||||
Predicate spPredicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
myQueryRoot.addPredicate(spPredicate);
|
||||
return spPredicate;
|
||||
}
|
||||
|
@ -345,8 +345,8 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
|
||||
private <T> Expression<Boolean> toEqualOrIsNullPredicate(Path<T> theExpression, T theCode) {
|
||||
if (theCode == null) {
|
||||
return myBuilder.isNull(theExpression);
|
||||
return myCriteriaBuilder.isNull(theExpression);
|
||||
}
|
||||
return myBuilder.equal(theExpression, theCode);
|
||||
return myCriteriaBuilder.equal(theExpression, theCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,37 +109,37 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
|
||||
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
|
||||
|
||||
Predicate uriPredicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
Predicate uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
Predicate hashAndUriPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, uriPredicate);
|
||||
codePredicates.add(hashAndUriPredicate);
|
||||
|
||||
} else {
|
||||
if (myDontUseHashesForSearch) {
|
||||
Predicate predicate = myBuilder.equal(join.get("myUri").as(String.class), value);
|
||||
Predicate predicate = myCriteriaBuilder.equal(join.get("myUri").as(String.class), value);
|
||||
codePredicates.add(predicate);
|
||||
} else {
|
||||
|
||||
Predicate uriPredicate = null;
|
||||
if (operation == null || operation == SearchFilterParser.CompareOperation.eq) {
|
||||
long hashUri = ResourceIndexedSearchParamUri.calculateHashUri(theResourceName, theParamName, value);
|
||||
Predicate hashPredicate = myBuilder.equal(join.get("myHashUri"), hashUri);
|
||||
Predicate hashPredicate = myCriteriaBuilder.equal(join.get("myHashUri"), hashUri);
|
||||
codePredicates.add(hashPredicate);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||
uriPredicate = myBuilder.notEqual(join.get("myUri").as(String.class), value);
|
||||
uriPredicate = myCriteriaBuilder.notEqual(join.get("myUri").as(String.class), value);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.co) {
|
||||
uriPredicate = myBuilder.like(join.get("myUri").as(String.class), createLeftAndRightMatchLikeExpression(value));
|
||||
uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createLeftAndRightMatchLikeExpression(value));
|
||||
} else if (operation == SearchFilterParser.CompareOperation.gt) {
|
||||
uriPredicate = myBuilder.greaterThan(join.get("myUri").as(String.class), value);
|
||||
uriPredicate = myCriteriaBuilder.greaterThan(join.get("myUri").as(String.class), value);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.lt) {
|
||||
uriPredicate = myBuilder.lessThan(join.get("myUri").as(String.class), value);
|
||||
uriPredicate = myCriteriaBuilder.lessThan(join.get("myUri").as(String.class), value);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ge) {
|
||||
uriPredicate = myBuilder.greaterThanOrEqualTo(join.get("myUri").as(String.class), value);
|
||||
uriPredicate = myCriteriaBuilder.greaterThanOrEqualTo(join.get("myUri").as(String.class), value);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.le) {
|
||||
uriPredicate = myBuilder.lessThanOrEqualTo(join.get("myUri").as(String.class), value);
|
||||
uriPredicate = myCriteriaBuilder.lessThanOrEqualTo(join.get("myUri").as(String.class), value);
|
||||
} else if (operation == SearchFilterParser.CompareOperation.sw) {
|
||||
uriPredicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
|
||||
} else if (operation == SearchFilterParser.CompareOperation.ew) {
|
||||
uriPredicate = myBuilder.like(join.get("myUri").as(String.class), createRightMatchLikeExpression(value));
|
||||
uriPredicate = myCriteriaBuilder.like(join.get("myUri").as(String.class), createRightMatchLikeExpression(value));
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Unsupported operator specified in _filter clause, %s",
|
||||
operation.toString()));
|
||||
|
@ -147,8 +147,8 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
|
||||
if (uriPredicate != null) {
|
||||
long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(theResourceName, theParamName);
|
||||
Predicate hashIdentityPredicate = myBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
||||
codePredicates.add(myBuilder.and(hashIdentityPredicate, uriPredicate));
|
||||
Predicate hashIdentityPredicate = myCriteriaBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
||||
codePredicates.add(myCriteriaBuilder.and(hashIdentityPredicate, uriPredicate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,12 +164,12 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
|||
* just add a predicate that can never match
|
||||
*/
|
||||
if (codePredicates.isEmpty()) {
|
||||
Predicate predicate = myBuilder.isNull(join.get("myMissing").as(String.class));
|
||||
Predicate predicate = myCriteriaBuilder.isNull(join.get("myMissing").as(String.class));
|
||||
myQueryRoot.addPredicate(predicate);
|
||||
return null;
|
||||
}
|
||||
|
||||
Predicate orPredicate = myBuilder.or(toArray(codePredicates));
|
||||
Predicate orPredicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||
|
||||
Predicate outerPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName,
|
||||
theParamName,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.dstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.model.entity.*;
|
|||
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
||||
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||
import ca.uhn.fhir.jpa.util.TestUtil;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
|
@ -15,6 +16,7 @@ import ca.uhn.fhir.rest.api.SortSpec;
|
|||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.method.SearchParameter;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
|
@ -47,6 +49,7 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -362,16 +365,16 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
obs.addIdentifier().setSystem("urn:system").setValue("FOO");
|
||||
obs.setDevice(new Reference(devId));
|
||||
obs.setSubject(new Reference(pid0));
|
||||
myObservationDao.create(obs, mySrd).getId();
|
||||
obs.setCode(new CodeableConcept(new Coding("sys", "val", "disp")));
|
||||
myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
SearchParameterMap params;
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
|
||||
// Not currently working
|
||||
// params = new SearchParameterMap();
|
||||
// params.setLoadSynchronous(true);
|
||||
// params.add("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID"));
|
||||
// assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
// Target exists and is linked
|
||||
params.setLoadSynchronous(true);
|
||||
params.add("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
|
||||
// No targets exist
|
||||
params = new SearchParameterMap();
|
||||
|
|
|
@ -497,12 +497,10 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
|
|||
|
||||
SearchParameterMap params;
|
||||
|
||||
// KHS JA When we switched _has from two queries to a nested subquery, we broke support for chains within _has
|
||||
// We have decided for now to prefer the performance optimization of the subquery over the slower full capability
|
||||
// params = new SearchParameterMap();
|
||||
// params.setLoadSynchronous(true);
|
||||
// params.add("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID"));
|
||||
// assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
params = new SearchParameterMap();
|
||||
params.setLoadSynchronous(true);
|
||||
params.add("_has", new HasParam("Observation", "subject", "device.identifier", "urn:system|DEVICEID"));
|
||||
assertThat(toUnqualifiedVersionlessIdValues(myPatientDao.search(params)), contains(pid0.getValue()));
|
||||
|
||||
// No targets exist
|
||||
params = new SearchParameterMap();
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -455,45 +456,33 @@ public class SearchParameterMap implements Serializable {
|
|||
return b.toString();
|
||||
}
|
||||
|
||||
|
||||
public void clean() {
|
||||
for (Map.Entry<String, List<List<IQueryParameterType>>> nextParamEntry : this.entrySet()) {
|
||||
String nextParamName = nextParamEntry.getKey();
|
||||
List<List<IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
clean(nextParamName, andOrParams);
|
||||
cleanParameter(nextParamName, andOrParams);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Filter out
|
||||
* Given a particular named parameter, e.g. `name`, iterate over AndOrParams and remove any which are empty.
|
||||
*/
|
||||
private void clean(String theParamName, List<List<IQueryParameterType>> theAndOrParams) {
|
||||
for (int andListIdx = 0; andListIdx < theAndOrParams.size(); andListIdx++) {
|
||||
List<? extends IQueryParameterType> nextOrList = theAndOrParams.get(andListIdx);
|
||||
private void cleanParameter(String theParamName, List<List<IQueryParameterType>> theAndOrParams) {
|
||||
theAndOrParams
|
||||
.forEach(
|
||||
orList -> {
|
||||
List<IQueryParameterType> emptyParameters = orList.stream()
|
||||
.filter(nextOr -> nextOr.getMissing() == null)
|
||||
.filter(nextOr -> nextOr instanceof QuantityParam)
|
||||
.filter(nextOr -> isBlank(((QuantityParam) nextOr).getValueAsString()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (int orListIdx = 0; orListIdx < nextOrList.size(); orListIdx++) {
|
||||
IQueryParameterType nextOr = nextOrList.get(orListIdx);
|
||||
boolean hasNoValue = false;
|
||||
if (nextOr.getMissing() != null) {
|
||||
continue;
|
||||
}
|
||||
if (nextOr instanceof QuantityParam) {
|
||||
if (isBlank(((QuantityParam) nextOr).getValueAsString())) {
|
||||
hasNoValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNoValue) {
|
||||
ourLog.debug("Ignoring empty parameter: {}", theParamName);
|
||||
nextOrList.remove(orListIdx);
|
||||
orListIdx--;
|
||||
orList.removeAll(emptyParameters);
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOrList.isEmpty()) {
|
||||
theAndOrParams.remove(andListIdx);
|
||||
andListIdx--;
|
||||
}
|
||||
}
|
||||
);
|
||||
theAndOrParams.removeIf(List::isEmpty);
|
||||
}
|
||||
|
||||
public void setNearDistanceParam(QuantityParam theQuantityParam) {
|
||||
|
|
Loading…
Reference in New Issue