Merge remote-tracking branch 'remotes/origin/master' into im_20200316_lastn_operation_elasticsearch
# Conflicts: # hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java
This commit is contained in:
commit
d2b54d6203
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 1842
|
||||||
|
title: "When performing queries with multiple chained search parameters, such as 'Observation?subject.identifier=FOO&specimen.identifier=BAR',
|
||||||
|
an unnecessary SQL join was introduced into the resulting query. This was inefficient, and made it particularly hard for the RDBMS optimizer
|
||||||
|
to pick an efficient query plan in some cases. This is not fixing a regression (this issue has always existed in HAPI FHIR JPA) but it
|
||||||
|
was deemed sufficiently important to merit a dedicated point release."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 1847
|
||||||
|
title: "Issue #1849 added two new search columns for date SearchParameters that are used to provide searching
|
||||||
|
at the day granularity in a timezone independent way. These new columns require a new database index that
|
||||||
|
was missed in the original merge."
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
release-date: "2020-05-15"
|
||||||
|
codename: "Labrador"
|
|
@ -35,13 +35,12 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder;
|
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderFactory;
|
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderFactory;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.QueryRoot;
|
import ca.uhn.fhir.jpa.dao.predicate.querystack.QueryStack;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||||
|
@ -73,6 +72,7 @@ import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
|
import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
@ -101,7 +101,6 @@ import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.Join;
|
||||||
import javax.persistence.criteria.JoinType;
|
|
||||||
import javax.persistence.criteria.Order;
|
import javax.persistence.criteria.Order;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
@ -113,6 +112,7 @@ import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
@ -137,7 +137,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
private static final List<ResourcePersistentId> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<>());
|
private static final List<ResourcePersistentId> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<>());
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class);
|
||||||
private static final ResourcePersistentId NO_MORE = new ResourcePersistentId(-1L);
|
private static final ResourcePersistentId NO_MORE = new ResourcePersistentId(-1L);
|
||||||
private final QueryRoot myQueryRoot = new QueryRoot();
|
|
||||||
private final String myResourceName;
|
private final String myResourceName;
|
||||||
private final Class<? extends IBaseResource> myResourceType;
|
private final Class<? extends IBaseResource> myResourceType;
|
||||||
private final IDao myCallingDao;
|
private final IDao myCallingDao;
|
||||||
|
@ -147,6 +146,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
protected IResourceTagDao myResourceTagDao;
|
protected IResourceTagDao myResourceTagDao;
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
protected EntityManager myEntityManager;
|
protected EntityManager myEntityManager;
|
||||||
|
private QueryStack myQueryStack;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -275,94 +275,16 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(SearchParameterMap theParams, String theSearchUuid, RequestPartitionId theRequestPartitionId) {
|
private void init(SearchParameterMap theParams, String theSearchUuid, RequestPartitionId theRequestPartitionId) {
|
||||||
myParams = theParams;
|
|
||||||
myCriteriaBuilder = myEntityManager.getCriteriaBuilder();
|
myCriteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||||
|
myQueryStack = new QueryStack(myCriteriaBuilder, myResourceName, theParams, theRequestPartitionId);
|
||||||
|
myParams = theParams;
|
||||||
mySearchUuid = theSearchUuid;
|
mySearchUuid = theSearchUuid;
|
||||||
myPredicateBuilder = new PredicateBuilder(this, myPredicateBuilderFactory);
|
myPredicateBuilder = new PredicateBuilder(this, myPredicateBuilderFactory);
|
||||||
myRequestPartitionId = theRequestPartitionId;
|
myRequestPartitionId = theRequestPartitionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<TypedQuery<Long>> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest,
|
private TypedQuery<Long> createQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest) {
|
||||||
SearchRuntimeDetails theSearchRuntimeDetails) {
|
|
||||||
List<ResourcePersistentId> pids = new ArrayList<>();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fulltext or lastn search
|
|
||||||
*/
|
|
||||||
if (myParams.containsKey(Constants.PARAM_CONTENT) || myParams.containsKey(Constants.PARAM_TEXT) || myParams.isLastN()) {
|
|
||||||
if (myParams.containsKey(Constants.PARAM_CONTENT) || myParams.containsKey(Constants.PARAM_TEXT)) {
|
|
||||||
if (myFulltextSearchSvc == null) {
|
|
||||||
if (myParams.containsKey(Constants.PARAM_TEXT)) {
|
|
||||||
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_TEXT);
|
|
||||||
} else if (myParams.containsKey(Constants.PARAM_CONTENT)) {
|
|
||||||
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (myParams.getEverythingMode() != null) {
|
|
||||||
pids = myFulltextSearchSvc.everything(myResourceName, myParams, theRequest);
|
|
||||||
} else {
|
|
||||||
pids = myFulltextSearchSvc.search(myResourceName, myParams);
|
|
||||||
}
|
|
||||||
} else if (myParams.isLastN()) {
|
|
||||||
if (myIElasticsearchSvc == null) {
|
|
||||||
if (myParams.isLastN()) {
|
|
||||||
throw new InvalidRequestException("LastN operation is not enabled on this service, can not process this request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Integer myMaxObservationsPerCode;
|
|
||||||
if(myParams.getLastNMax() != null) {
|
|
||||||
myMaxObservationsPerCode = myParams.getLastNMax();
|
|
||||||
} else {
|
|
||||||
throw new InvalidRequestException("Max parameter is required for $lastn operation");
|
|
||||||
}
|
|
||||||
List<String> lastnResourceIds = myIElasticsearchSvc.executeLastN(myParams, myContext, theMaximumResults);
|
|
||||||
for (String lastnResourceId : lastnResourceIds) {
|
|
||||||
pids.add(myIdHelperService.resolveResourcePersistentIds(myRequestPartitionId, myResourceName, lastnResourceId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (theSearchRuntimeDetails != null) {
|
|
||||||
theSearchRuntimeDetails.setFoundIndexMatchesCount(pids.size());
|
|
||||||
HookParams params = new HookParams()
|
|
||||||
.add(RequestDetails.class, theRequest)
|
|
||||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
|
||||||
.add(SearchRuntimeDetails.class, theSearchRuntimeDetails);
|
|
||||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.JPA_PERFTRACE_INDEXSEARCH_QUERY_COMPLETE, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pids.isEmpty()) {
|
|
||||||
// Will never match
|
|
||||||
pids = Collections.singletonList(new ResourcePersistentId(-1L));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<TypedQuery<Long>> myQueries = new ArrayList<>();
|
|
||||||
|
|
||||||
if (!pids.isEmpty()) {
|
|
||||||
if (theMaximumResults != null && pids.size() > theMaximumResults) {
|
|
||||||
pids.subList(0,theMaximumResults-1);
|
|
||||||
}
|
|
||||||
new QueryChunker<Long>().chunk(ResourcePersistentId.toLongList(pids), t->{
|
|
||||||
doCreateChunkedQueries(t, sort, theCount, theRequest, myQueries);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
myQueries.add(createChunkedQuery(sort,theMaximumResults, theCount, theRequest, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
return myQueries;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doCreateChunkedQueries(List<Long> thePids, SortSpec sort, boolean theCount, RequestDetails theRequest, ArrayList<TypedQuery<Long>> theQueries) {
|
|
||||||
if(thePids.size() < getMaximumPageSize()) {
|
|
||||||
normalizeIdListForLastNInClause(thePids);
|
|
||||||
}
|
|
||||||
theQueries.add(createChunkedQuery(sort, thePids.size(), theCount, theRequest, thePids));
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypedQuery<Long> createChunkedQuery(SortSpec sort, Integer theMaximumResults, boolean theCount, RequestDetails theRequest, List<Long> thePidList) {
|
|
||||||
CriteriaQuery<Long> outerQuery;
|
|
||||||
/*
|
/*
|
||||||
* Sort
|
* Sort
|
||||||
*
|
*
|
||||||
|
@ -372,36 +294,24 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
if (sort != null) {
|
if (sort != null) {
|
||||||
assert !theCount;
|
assert !theCount;
|
||||||
|
|
||||||
outerQuery = myCriteriaBuilder.createQuery(Long.class);
|
myQueryStack.pushResourceTableQuery();
|
||||||
myQueryRoot.push(outerQuery);
|
|
||||||
if (theCount) {
|
|
||||||
outerQuery.multiselect(myCriteriaBuilder.countDistinct(myQueryRoot.getRoot()));
|
|
||||||
} else {
|
|
||||||
outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Order> orders = Lists.newArrayList();
|
List<Order> orders = createSort(myCriteriaBuilder, myQueryStack, sort);
|
||||||
|
|
||||||
createSort(myCriteriaBuilder, myQueryRoot, sort, orders);
|
|
||||||
if (orders.size() > 0) {
|
if (orders.size() > 0) {
|
||||||
outerQuery.orderBy(orders);
|
myQueryStack.orderBy(orders);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
outerQuery = myCriteriaBuilder.createQuery(Long.class);
|
|
||||||
myQueryRoot.push(outerQuery);
|
|
||||||
if (theCount) {
|
if (theCount) {
|
||||||
outerQuery.multiselect(myCriteriaBuilder.countDistinct(myQueryRoot.getRoot()));
|
myQueryStack.pushResourceTableCountQuery();
|
||||||
} else {
|
} else {
|
||||||
outerQuery.multiselect(myQueryRoot.get("myId").as(Long.class));
|
myQueryStack.pushResourceTableQuery();
|
||||||
// KHS This distinct call is causing performance issues in large installations
|
|
||||||
// outerQuery.distinct(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myParams.getEverythingMode() != null) {
|
if (myParams.getEverythingMode() != null) {
|
||||||
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinks", JoinType.LEFT);
|
From<?, ResourceLink> join = myQueryStack.createJoin(SearchBuilderJoinEnum.REFERENCE, null);
|
||||||
|
|
||||||
if (myParams.get(IAnyResource.SP_RES_ID) != null) {
|
if (myParams.get(IAnyResource.SP_RES_ID) != null) {
|
||||||
StringParam idParam = (StringParam) myParams.get(IAnyResource.SP_RES_ID).get(0).get(0);
|
StringParam idParam = (StringParam) myParams.get(IAnyResource.SP_RES_ID).get(0).get(0);
|
||||||
|
@ -410,11 +320,11 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
myAlsoIncludePids = new ArrayList<>(1);
|
myAlsoIncludePids = new ArrayList<>(1);
|
||||||
}
|
}
|
||||||
myAlsoIncludePids.add(pid);
|
myAlsoIncludePids.add(pid);
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid.getIdAsLong()));
|
myQueryStack.addPredicate(myCriteriaBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid.getIdAsLong()));
|
||||||
} else {
|
} else {
|
||||||
Predicate targetTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType").as(String.class), myResourceName);
|
Predicate targetTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType").as(String.class), myResourceName);
|
||||||
Predicate sourceTypePredicate = myCriteriaBuilder.equal(myQueryRoot.get("myResourceType").as(String.class), myResourceName);
|
Predicate sourceTypePredicate = myCriteriaBuilder.equal(myQueryStack.get("myResourceType").as(String.class), myResourceName);
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.or(sourceTypePredicate, targetTypePredicate));
|
myQueryStack.addPredicate(myCriteriaBuilder.or(sourceTypePredicate, targetTypePredicate));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -422,43 +332,43 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
searchForIdsWithAndOr(myParams, theRequest);
|
searchForIdsWithAndOr(myParams, theRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add PID list predicate for full text search and/or lastn operation
|
/*
|
||||||
if (thePidList != null && thePidList.size() > 0) {
|
* Fulltext search
|
||||||
myQueryRoot.addPredicate(myQueryRoot.get("myId").as(Long.class).in(thePidList));
|
*/
|
||||||
|
if (myParams.containsKey(Constants.PARAM_CONTENT) || myParams.containsKey(Constants.PARAM_TEXT)) {
|
||||||
|
if (myFulltextSearchSvc == null) {
|
||||||
|
if (myParams.containsKey(Constants.PARAM_TEXT)) {
|
||||||
|
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_TEXT);
|
||||||
|
} else if (myParams.containsKey(Constants.PARAM_CONTENT)) {
|
||||||
|
throw new InvalidRequestException("Fulltext search is not enabled on this service, can not process parameter: " + Constants.PARAM_CONTENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
List<ResourcePersistentId> pids;
|
||||||
* Add a predicate to make sure we only include non-deleted resources, and only include
|
if (myParams.getEverythingMode() != null) {
|
||||||
* resources of the right type.
|
pids = myFulltextSearchSvc.everything(myResourceName, myParams, theRequest);
|
||||||
*
|
|
||||||
* If we have any joins to index tables, we get this behaviour already guaranteed so we don't
|
|
||||||
* need an explicit predicate for it.
|
|
||||||
*/
|
|
||||||
if (!myQueryRoot.hasIndexJoins()) {
|
|
||||||
if (myParams.getEverythingMode() == null) {
|
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myResourceType"), myResourceName));
|
|
||||||
}
|
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(myQueryRoot.get("myDeleted")));
|
|
||||||
if (!myRequestPartitionId.isAllPartitions()) {
|
|
||||||
if (myRequestPartitionId.getPartitionId() != null) {
|
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myPartitionIdValue").as(Integer.class), myRequestPartitionId.getPartitionId()));
|
|
||||||
} else {
|
} else {
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(myQueryRoot.get("myPartitionIdValue").as(Integer.class)));
|
pids = myFulltextSearchSvc.search(myResourceName, myParams);
|
||||||
}
|
}
|
||||||
|
if (pids.isEmpty()) {
|
||||||
|
// Will never match
|
||||||
|
pids = Collections.singletonList(new ResourcePersistentId(-1L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myQueryStack.addPredicate(myQueryStack.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(pids)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last updated
|
// Last updated
|
||||||
DateRangeParam lu = myParams.getLastUpdated();
|
DateRangeParam lu = myParams.getLastUpdated();
|
||||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myCriteriaBuilder, myQueryRoot.getRoot());
|
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myCriteriaBuilder);
|
||||||
myQueryRoot.addPredicates(lastUpdatedPredicates);
|
myQueryStack.addPredicates(lastUpdatedPredicates);
|
||||||
|
|
||||||
myQueryRoot.where(myCriteriaBuilder.and(myQueryRoot.getPredicateArray()));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now perform the search
|
* Now perform the search
|
||||||
*/
|
*/
|
||||||
|
CriteriaQuery<Long> outerQuery = (CriteriaQuery<Long>) myQueryStack.pop();
|
||||||
final TypedQuery<Long> query = myEntityManager.createQuery(outerQuery);
|
final TypedQuery<Long> query = myEntityManager.createQuery(outerQuery);
|
||||||
|
assert myQueryStack.isEmpty();
|
||||||
|
|
||||||
if (theMaximumResults != null) {
|
if (theMaximumResults != null) {
|
||||||
query.setMaxResults(theMaximumResults);
|
query.setMaxResults(theMaximumResults);
|
||||||
|
@ -505,32 +415,35 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
* @return Returns {@literal true} if any search parameter sorts were found, or false if
|
* @return Returns {@literal true} if any search parameter sorts were found, or false if
|
||||||
* no sorts were found, or only non-search parameters ones (e.g. _id, _lastUpdated)
|
* no sorts were found, or only non-search parameters ones (e.g. _id, _lastUpdated)
|
||||||
*/
|
*/
|
||||||
private boolean createSort(CriteriaBuilder theBuilder, QueryRoot theQueryRoot, SortSpec theSort, List<Order> theOrders) {
|
private List<Order> createSort(CriteriaBuilder theBuilder, QueryStack theQueryStack, SortSpec theSort) {
|
||||||
if (theSort == null || isBlank(theSort.getParamName())) {
|
if (theSort == null || isBlank(theSort.getParamName())) {
|
||||||
return false;
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Order> orders = new ArrayList<>(1);
|
||||||
if (IAnyResource.SP_RES_ID.equals(theSort.getParamName())) {
|
if (IAnyResource.SP_RES_ID.equals(theSort.getParamName())) {
|
||||||
From<?, ?> forcedIdJoin = theQueryRoot.join("myForcedId", JoinType.LEFT);
|
From<?, ?> forcedIdJoin = theQueryStack.createJoin(SearchBuilderJoinEnum.FORCED_ID, null);
|
||||||
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
||||||
theOrders.add(theBuilder.asc(forcedIdJoin.get("myForcedId")));
|
orders.add(theBuilder.asc(forcedIdJoin.get("myForcedId")));
|
||||||
theOrders.add(theBuilder.asc(theQueryRoot.get("myId")));
|
orders.add(theBuilder.asc(theQueryStack.get("myId")));
|
||||||
} else {
|
} else {
|
||||||
theOrders.add(theBuilder.desc(forcedIdJoin.get("myForcedId")));
|
orders.add(theBuilder.desc(forcedIdJoin.get("myForcedId")));
|
||||||
theOrders.add(theBuilder.desc(theQueryRoot.get("myId")));
|
orders.add(theBuilder.desc(theQueryStack.get("myId")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSort(theBuilder, theQueryRoot, theSort.getChain(), theOrders);
|
orders.addAll(createSort(theBuilder, theQueryStack, theSort.getChain()));
|
||||||
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Constants.PARAM_LASTUPDATED.equals(theSort.getParamName())) {
|
if (Constants.PARAM_LASTUPDATED.equals(theSort.getParamName())) {
|
||||||
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
||||||
theOrders.add(theBuilder.asc(theQueryRoot.get("myUpdated")));
|
orders.add(theBuilder.asc(theQueryStack.get("myUpdated")));
|
||||||
} else {
|
} else {
|
||||||
theOrders.add(theBuilder.desc(theQueryRoot.get("myUpdated")));
|
orders.add(theBuilder.desc(theQueryStack.get("myUpdated")));
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSort(theBuilder, theQueryRoot, theSort.getChain(), theOrders);
|
orders.addAll(createSort(theBuilder, theQueryStack, theSort.getChain()));
|
||||||
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceName);
|
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(myResourceName);
|
||||||
|
@ -539,43 +452,35 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
throw new InvalidRequestException("Unknown sort parameter '" + theSort.getParamName() + "'");
|
throw new InvalidRequestException("Unknown sort parameter '" + theSort.getParamName() + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
String joinAttrName;
|
|
||||||
String[] sortAttrName;
|
String[] sortAttrName;
|
||||||
SearchBuilderJoinEnum joinType;
|
SearchBuilderJoinEnum joinType;
|
||||||
|
|
||||||
switch (param.getParamType()) {
|
switch (param.getParamType()) {
|
||||||
case STRING:
|
case STRING:
|
||||||
joinAttrName = "myParamsString";
|
|
||||||
sortAttrName = new String[]{"myValueExact"};
|
sortAttrName = new String[]{"myValueExact"};
|
||||||
joinType = SearchBuilderJoinEnum.STRING;
|
joinType = SearchBuilderJoinEnum.STRING;
|
||||||
break;
|
break;
|
||||||
case DATE:
|
case DATE:
|
||||||
joinAttrName = "myParamsDate";
|
|
||||||
sortAttrName = new String[]{"myValueLow"};
|
sortAttrName = new String[]{"myValueLow"};
|
||||||
joinType = SearchBuilderJoinEnum.DATE;
|
joinType = SearchBuilderJoinEnum.DATE;
|
||||||
break;
|
break;
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
joinAttrName = "myResourceLinks";
|
|
||||||
sortAttrName = new String[]{"myTargetResourcePid"};
|
sortAttrName = new String[]{"myTargetResourcePid"};
|
||||||
joinType = SearchBuilderJoinEnum.REFERENCE;
|
joinType = SearchBuilderJoinEnum.REFERENCE;
|
||||||
break;
|
break;
|
||||||
case TOKEN:
|
case TOKEN:
|
||||||
joinAttrName = "myParamsToken";
|
|
||||||
sortAttrName = new String[]{"mySystem", "myValue"};
|
sortAttrName = new String[]{"mySystem", "myValue"};
|
||||||
joinType = SearchBuilderJoinEnum.TOKEN;
|
joinType = SearchBuilderJoinEnum.TOKEN;
|
||||||
break;
|
break;
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
joinAttrName = "myParamsNumber";
|
|
||||||
sortAttrName = new String[]{"myValue"};
|
sortAttrName = new String[]{"myValue"};
|
||||||
joinType = SearchBuilderJoinEnum.NUMBER;
|
joinType = SearchBuilderJoinEnum.NUMBER;
|
||||||
break;
|
break;
|
||||||
case URI:
|
case URI:
|
||||||
joinAttrName = "myParamsUri";
|
|
||||||
sortAttrName = new String[]{"myUri"};
|
sortAttrName = new String[]{"myUri"};
|
||||||
joinType = SearchBuilderJoinEnum.URI;
|
joinType = SearchBuilderJoinEnum.URI;
|
||||||
break;
|
break;
|
||||||
case QUANTITY:
|
case QUANTITY:
|
||||||
joinAttrName = "myParamsQuantity";
|
|
||||||
sortAttrName = new String[]{"myValue"};
|
sortAttrName = new String[]{"myValue"};
|
||||||
joinType = SearchBuilderJoinEnum.QUANTITY;
|
joinType = SearchBuilderJoinEnum.QUANTITY;
|
||||||
break;
|
break;
|
||||||
|
@ -591,37 +496,40 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
* sorting on, we'll also sort with it. Otherwise we need a new join.
|
* sorting on, we'll also sort with it. Otherwise we need a new join.
|
||||||
*/
|
*/
|
||||||
SearchBuilderJoinKey key = new SearchBuilderJoinKey(theSort.getParamName(), joinType);
|
SearchBuilderJoinKey key = new SearchBuilderJoinKey(theSort.getParamName(), joinType);
|
||||||
Join<?, ?> join = theQueryRoot.getIndexJoin(key);
|
Optional<Join<?, ?>> joinOpt = theQueryStack.getExistingJoin(key);
|
||||||
if (join == null) {
|
|
||||||
join = theQueryRoot.join(joinAttrName, JoinType.LEFT);
|
From<?, ?> join;
|
||||||
|
if (!joinOpt.isPresent()) {
|
||||||
|
join = theQueryStack.createJoin(joinType, theSort.getParamName());
|
||||||
|
|
||||||
if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
||||||
theQueryRoot.addPredicate(join.get("mySourcePath").as(String.class).in(param.getPathsSplit()));
|
theQueryStack.addPredicate(join.get("mySourcePath").as(String.class).in(param.getPathsSplit()));
|
||||||
} else {
|
} else {
|
||||||
if (myDaoConfig.getDisableHashBasedSearches()) {
|
if (myDaoConfig.getDisableHashBasedSearches()) {
|
||||||
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName());
|
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName());
|
||||||
theQueryRoot.addPredicate(joinParam1);
|
theQueryStack.addPredicate(joinParam1);
|
||||||
} else {
|
} else {
|
||||||
Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myPartitionSettings, myRequestPartitionId, myResourceName, theSort.getParamName());
|
Long hashIdentity = BaseResourceIndexedSearchParam.calculateHashIdentity(myPartitionSettings, myRequestPartitionId, myResourceName, theSort.getParamName());
|
||||||
Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
Predicate joinParam1 = theBuilder.equal(join.get("myHashIdentity"), hashIdentity);
|
||||||
theQueryRoot.addPredicate(joinParam1);
|
theQueryStack.addPredicate(joinParam1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ourLog.debug("Reusing join for {}", theSort.getParamName());
|
ourLog.debug("Reusing join for {}", theSort.getParamName());
|
||||||
|
join = joinOpt.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String next : sortAttrName) {
|
for (String next : sortAttrName) {
|
||||||
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
if (theSort.getOrder() == null || theSort.getOrder() == SortOrderEnum.ASC) {
|
||||||
theOrders.add(theBuilder.asc(join.get(next)));
|
orders.add(theBuilder.asc(join.get(next)));
|
||||||
} else {
|
} else {
|
||||||
theOrders.add(theBuilder.desc(join.get(next)));
|
orders.add(theBuilder.desc(join.get(next)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createSort(theBuilder, theQueryRoot, theSort.getChain(), theOrders);
|
orders.addAll(createSort(theBuilder, theQueryStack, theSort.getChain()));
|
||||||
|
|
||||||
return true;
|
return orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -737,7 +645,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
List<ResourcePersistentId> pids = new ArrayList<>(thePids);
|
List<ResourcePersistentId> pids = new ArrayList<>(thePids);
|
||||||
new QueryChunker<ResourcePersistentId>().chunk(pids, t -> {
|
new QueryChunker<ResourcePersistentId>().chunk(pids, t -> {
|
||||||
doLoadPids(t, theIncludedPids, theResourceListToPopulate, theForHistoryOperation, position);
|
doLoadPids(t, theIncludedPids, theResourceListToPopulate, theForHistoryOperation, position, theDetails);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1005,17 +913,16 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexedString, RequestPartitionId theRequestPartitionId) {
|
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexedString, RequestPartitionId theRequestPartitionId) {
|
||||||
Join<ResourceTable, ResourceIndexedCompositeStringUnique> join = myQueryRoot.join("myParamsCompositeStringUnique", JoinType.LEFT);
|
From<?, ResourceIndexedCompositeStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
|
||||||
|
|
||||||
if (!theRequestPartitionId.isAllPartitions()) {
|
if (!theRequestPartitionId.isAllPartitions()) {
|
||||||
Integer partitionId = theRequestPartitionId.getPartitionId();
|
Integer partitionId = theRequestPartitionId.getPartitionId();
|
||||||
Predicate predicate = myCriteriaBuilder.equal(join.get("myPartitionIdValue").as(Integer.class), partitionId);
|
Predicate predicate = myCriteriaBuilder.equal(join.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||||
myQueryRoot.addPredicate(predicate);
|
myQueryStack.addPredicate(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
|
||||||
Predicate predicate = myCriteriaBuilder.equal(join.get("myIndexString"), theIndexedString);
|
Predicate predicate = myCriteriaBuilder.equal(join.get("myIndexString"), theIndexedString);
|
||||||
myQueryRoot.addPredicate(predicate);
|
myQueryStack.addPredicateWithImplicitTypeSelection(predicate);
|
||||||
|
|
||||||
// Remove any empty parameters remaining after this
|
// Remove any empty parameters remaining after this
|
||||||
theParams.clean();
|
theParams.clean();
|
||||||
|
@ -1044,8 +951,8 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return myCriteriaBuilder;
|
return myCriteriaBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryRoot getQueryRoot() {
|
public QueryStack getQueryStack() {
|
||||||
return myQueryRoot;
|
return myQueryStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
@ -1061,6 +968,22 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
myDaoConfig = theDaoConfig;
|
myDaoConfig = theDaoConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder) {
|
||||||
|
List<Predicate> lastUpdatedPredicates = new ArrayList<>();
|
||||||
|
if (theLastUpdated != null) {
|
||||||
|
if (theLastUpdated.getLowerBoundAsInstant() != null) {
|
||||||
|
ourLog.debug("LastUpdated lower bound: {}", new InstantDt(theLastUpdated.getLowerBoundAsInstant()));
|
||||||
|
Predicate predicateLower = builder.greaterThanOrEqualTo(myQueryStack.getLastUpdatedColumn(), theLastUpdated.getLowerBoundAsInstant());
|
||||||
|
lastUpdatedPredicates.add(predicateLower);
|
||||||
|
}
|
||||||
|
if (theLastUpdated.getUpperBoundAsInstant() != null) {
|
||||||
|
Predicate predicateUpper = builder.lessThanOrEqualTo(myQueryStack.getLastUpdatedColumn(), theLastUpdated.getUpperBoundAsInstant());
|
||||||
|
lastUpdatedPredicates.add(predicateUpper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastUpdatedPredicates;
|
||||||
|
}
|
||||||
|
|
||||||
public class IncludesIterator extends BaseIterator<ResourcePersistentId> implements Iterator<ResourcePersistentId> {
|
public class IncludesIterator extends BaseIterator<ResourcePersistentId> implements Iterator<ResourcePersistentId> {
|
||||||
|
|
||||||
private final RequestDetails myRequest;
|
private final RequestDetails myRequest;
|
||||||
|
|
|
@ -24,11 +24,10 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.querystack.QueryStack;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
|
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
@ -43,8 +42,6 @@ import javax.annotation.PostConstruct;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.JoinType;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
|
@ -54,7 +51,7 @@ import java.util.List;
|
||||||
abstract class BasePredicateBuilder {
|
abstract class BasePredicateBuilder {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BasePredicateBuilder.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BasePredicateBuilder.class);
|
||||||
final CriteriaBuilder myCriteriaBuilder;
|
final CriteriaBuilder myCriteriaBuilder;
|
||||||
final QueryRoot myQueryRoot;
|
final QueryStack myQueryStack;
|
||||||
final Class<? extends IBaseResource> myResourceType;
|
final Class<? extends IBaseResource> myResourceType;
|
||||||
final String myResourceName;
|
final String myResourceName;
|
||||||
final SearchParameterMap myParams;
|
final SearchParameterMap myParams;
|
||||||
|
@ -68,7 +65,7 @@ abstract class BasePredicateBuilder {
|
||||||
|
|
||||||
BasePredicateBuilder(SearchBuilder theSearchBuilder) {
|
BasePredicateBuilder(SearchBuilder theSearchBuilder) {
|
||||||
myCriteriaBuilder = theSearchBuilder.getBuilder();
|
myCriteriaBuilder = theSearchBuilder.getBuilder();
|
||||||
myQueryRoot = theSearchBuilder.getQueryRoot();
|
myQueryStack = theSearchBuilder.getQueryStack();
|
||||||
myResourceType = theSearchBuilder.getResourceType();
|
myResourceType = theSearchBuilder.getResourceType();
|
||||||
myResourceName = theSearchBuilder.getResourceName();
|
myResourceName = theSearchBuilder.getResourceName();
|
||||||
myParams = theSearchBuilder.getParams();
|
myParams = theSearchBuilder.getParams();
|
||||||
|
@ -79,44 +76,8 @@ abstract class BasePredicateBuilder {
|
||||||
myDontUseHashesForSearch = myDaoConfig.getDisableHashBasedSearches();
|
myDontUseHashesForSearch = myDaoConfig.getDisableHashBasedSearches();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
<T> Join<ResourceTable, T> createJoin(SearchBuilderJoinEnum theType, String theSearchParameterName) {
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
|
||||||
switch (theType) {
|
|
||||||
case DATE:
|
|
||||||
join = myQueryRoot.join("myParamsDate", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case NUMBER:
|
|
||||||
join = myQueryRoot.join("myParamsNumber", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case QUANTITY:
|
|
||||||
join = myQueryRoot.join("myParamsQuantity", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case REFERENCE:
|
|
||||||
join = myQueryRoot.join("myResourceLinks", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case STRING:
|
|
||||||
join = myQueryRoot.join("myParamsString", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case URI:
|
|
||||||
join = myQueryRoot.join("myParamsUri", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case TOKEN:
|
|
||||||
join = myQueryRoot.join("myParamsToken", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
case COORDS:
|
|
||||||
join = myQueryRoot.join("myParamsCoords", JoinType.LEFT);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchBuilderJoinKey key = new SearchBuilderJoinKey(theSearchParameterName, theType);
|
|
||||||
myQueryRoot.putIndex(key, join);
|
|
||||||
|
|
||||||
return (Join<ResourceTable, T>) join;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addPredicateParamMissingForReference(String theResourceName, String theParamName, boolean theMissing, RequestPartitionId theRequestPartitionId) {
|
void addPredicateParamMissingForReference(String theResourceName, String theParamName, boolean theMissing, RequestPartitionId theRequestPartitionId) {
|
||||||
Join<ResourceTable, SearchParamPresent> paramPresentJoin = myQueryRoot.join("mySearchParamPresents", JoinType.LEFT);
|
From<?, SearchParamPresent> paramPresentJoin = myQueryStack.createJoin(SearchBuilderJoinEnum.PRESENCE, null);
|
||||||
|
|
||||||
Expression<Long> hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class);
|
Expression<Long> hashPresence = paramPresentJoin.get("myHashPresence").as(Long.class);
|
||||||
Long hash = SearchParamPresent.calculateHashPresence(myPartitionSettings, theRequestPartitionId, theResourceName, theParamName, !theMissing);
|
Long hash = SearchParamPresent.calculateHashPresence(myPartitionSettings, theRequestPartitionId, theResourceName, theParamName, !theMissing);
|
||||||
|
@ -126,22 +87,20 @@ abstract class BasePredicateBuilder {
|
||||||
|
|
||||||
addPartitionIdPredicate(theRequestPartitionId, paramPresentJoin, predicates);
|
addPartitionIdPredicate(theRequestPartitionId, paramPresentJoin, predicates);
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicatesWithImplicitTypeSelection(predicates);
|
||||||
myQueryRoot.addPredicates(predicates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, Join<ResourceTable, ? extends BaseResourceIndexedSearchParam> theJoin, RequestPartitionId theRequestPartitionId) {
|
void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, From<?, ? extends BaseResourceIndexedSearchParam> theJoin, RequestPartitionId theRequestPartitionId) {
|
||||||
if (!theRequestPartitionId.isAllPartitions()) {
|
if (!theRequestPartitionId.isAllPartitions()) {
|
||||||
if (theRequestPartitionId.getPartitionId() != null) {
|
if (theRequestPartitionId.getPartitionId() != null) {
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), theRequestPartitionId.getPartitionId()));
|
myQueryStack.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), theRequestPartitionId.getPartitionId()));
|
||||||
} else {
|
} else {
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue")));
|
myQueryStack.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
myQueryStack.addPredicateWithImplicitTypeSelection(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myParamName"), theParamName));
|
myQueryStack.addPredicate(myCriteriaBuilder.equal(theJoin.get("myParamName"), theParamName));
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(theJoin.get("myMissing"), theMissing));
|
myQueryStack.addPredicate(myCriteriaBuilder.equal(theJoin.get("myMissing"), theMissing));
|
||||||
myQueryRoot.setHasIndexJoins();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, Predicate thePredicate, RequestPartitionId theRequestPartitionId) {
|
Predicate combineParamIndexPredicateWithParamNamePredicate(String theResourceName, String theParamName, From<?, ? extends BaseResourceIndexedSearchParam> theFrom, Predicate thePredicate, RequestPartitionId theRequestPartitionId) {
|
||||||
|
@ -232,7 +191,7 @@ abstract class BasePredicateBuilder {
|
||||||
} else {
|
} else {
|
||||||
partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class));
|
partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class));
|
||||||
}
|
}
|
||||||
myQueryRoot.addPredicate(partitionPredicate);
|
myQueryStack.addPredicate(partitionPredicate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.Join;
|
||||||
|
@ -30,7 +28,7 @@ import java.util.Map;
|
||||||
public class IndexJoins {
|
public class IndexJoins {
|
||||||
Map<SearchBuilderJoinKey, Join<?, ?>> myIndexJoins = Maps.newHashMap();
|
Map<SearchBuilderJoinKey, Join<?, ?>> myIndexJoins = Maps.newHashMap();
|
||||||
|
|
||||||
public void put(SearchBuilderJoinKey theKey, Join<ResourceTable, ResourceIndexedSearchParamDate> theJoin) {
|
public void put(SearchBuilderJoinKey theKey, Join<?, ?> theJoin) {
|
||||||
myIndexJoins.put(theKey, theJoin);
|
myIndexJoins.put(theKey, theJoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,10 +97,10 @@ public class PredicateBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
Subquery<Long> createLinkSubquery(String theParameterName, String theTargetResourceType, ArrayList<IQueryParameterType> theOrValues, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
Subquery<Long> createLinkSubquery(String theParameterName, String theTargetResourceType, ArrayList<IQueryParameterType> theOrValues, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||||
return myPredicateBuilderReference.createLinkSubquery(true, theParameterName, theTargetResourceType, theOrValues, theRequest, theRequestPartitionId);
|
return myPredicateBuilderReference.createLinkSubquery(theParameterName, theTargetResourceType, theOrValues, theRequest, theRequestPartitionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate createResourceLinkPathPredicate(String theTargetResourceType, String theParamReference, Join<ResourceTable, ResourceLink> theJoin) {
|
Predicate createResourceLinkPathPredicate(String theTargetResourceType, String theParamReference, Join<?, ResourceLink> theJoin) {
|
||||||
return myPredicateBuilderReference.createResourceLinkPathPredicate(theTargetResourceType, theParamReference, theJoin);
|
return myPredicateBuilderReference.createResourceLinkPathPredicate(theTargetResourceType, theParamReference, theJoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.util.CoordCalculator;
|
import ca.uhn.fhir.jpa.util.CoordCalculator;
|
||||||
import ca.uhn.fhir.jpa.util.SearchBox;
|
import ca.uhn.fhir.jpa.util.SearchBox;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
@ -39,7 +38,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -151,7 +149,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
||||||
List<? extends IQueryParameterType> theList,
|
List<? extends IQueryParameterType> theList,
|
||||||
SearchFilterParser.CompareOperation theOperation,
|
SearchFilterParser.CompareOperation theOperation,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamCoords> join = createJoin(SearchBuilderJoinEnum.COORDS, theParamName);
|
From<?, ResourceIndexedSearchParamCoords> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COORDS, theParamName);
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
|
@ -173,8 +171,7 @@ public class PredicateBuilderCoords extends BasePredicateBuilder implements IPre
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
myQueryRoot.addPredicate(retVal);
|
myQueryStack.addPredicateWithImplicitTypeSelection(retVal);
|
||||||
myQueryRoot.setHasIndexJoins();
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
|
@ -37,7 +36,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -50,7 +48,7 @@ import java.util.Map;
|
||||||
public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder {
|
public class PredicateBuilderDate extends BasePredicateBuilder implements IPredicateBuilder {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(PredicateBuilderDate.class);
|
||||||
|
|
||||||
private Map<String, Join<ResourceTable, ResourceIndexedSearchParamDate>> myJoinMap;
|
private Map<String, From<?, ResourceIndexedSearchParamDate>> myJoinMap;
|
||||||
|
|
||||||
PredicateBuilderDate(SearchBuilder theSearchBuilder) {
|
PredicateBuilderDate(SearchBuilder theSearchBuilder) {
|
||||||
super(theSearchBuilder);
|
super(theSearchBuilder);
|
||||||
|
@ -69,9 +67,9 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
||||||
}
|
}
|
||||||
String key = theResourceName + " " + theParamName;
|
String key = theResourceName + " " + theParamName;
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = myJoinMap.get(key);
|
From<?, ResourceIndexedSearchParamDate> join = myJoinMap.get(key);
|
||||||
if (join == null) {
|
if (join == null) {
|
||||||
join = createJoin(SearchBuilderJoinEnum.DATE, theParamName);
|
join = myQueryStack.createJoin(SearchBuilderJoinEnum.DATE, theParamName);
|
||||||
myJoinMap.put(key, join);
|
myJoinMap.put(key, join);
|
||||||
newJoin = true;
|
newJoin = true;
|
||||||
}
|
}
|
||||||
|
@ -95,12 +93,11 @@ public class PredicateBuilderDate extends BasePredicateBuilder implements IPredi
|
||||||
|
|
||||||
Predicate orPredicates = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate orPredicates = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
|
||||||
if (newJoin) {
|
if (newJoin) {
|
||||||
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates, theRequestPartitionId);
|
Predicate identityAndValuePredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicates, theRequestPartitionId);
|
||||||
myQueryRoot.addPredicate(identityAndValuePredicate);
|
myQueryStack.addPredicateWithImplicitTypeSelection(identityAndValuePredicate);
|
||||||
} else {
|
} else {
|
||||||
myQueryRoot.addPredicate(orPredicates);
|
myQueryStack.addPredicateWithImplicitTypeSelection(orPredicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
return orPredicates;
|
return orPredicates;
|
||||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.param.NumberParam;
|
import ca.uhn.fhir.rest.param.NumberParam;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
|
@ -33,7 +32,7 @@ import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -57,7 +56,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
|
||||||
SearchFilterParser.CompareOperation operation,
|
SearchFilterParser.CompareOperation operation,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamNumber> join = createJoin(SearchBuilderJoinEnum.NUMBER, theParamName);
|
From<?, ResourceIndexedSearchParamNumber> join = myQueryStack.createJoin(SearchBuilderJoinEnum.NUMBER, theParamName);
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
|
@ -109,8 +108,7 @@ class PredicateBuilderNumber extends BasePredicateBuilder implements IPredicateB
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(predicate);
|
||||||
myQueryRoot.addPredicate(predicate);
|
|
||||||
return predicate;
|
return predicate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
|
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
|
@ -35,7 +34,6 @@ import org.springframework.stereotype.Component;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -59,14 +57,14 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
||||||
SearchFilterParser.CompareOperation theOperation,
|
SearchFilterParser.CompareOperation theOperation,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamQuantity> join = createJoin(SearchBuilderJoinEnum.QUANTITY, theParamName);
|
From<?, ResourceIndexedSearchParamQuantity> join = myQueryStack.createJoin(SearchBuilderJoinEnum.QUANTITY, theParamName);
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
List<Predicate> codePredicates = new ArrayList<>();
|
||||||
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
|
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
|
||||||
|
|
||||||
for (IQueryParameterType nextOr : theList) {
|
for (IQueryParameterType nextOr : theList) {
|
||||||
|
@ -75,8 +73,7 @@ class PredicateBuilderQuantity extends BasePredicateBuilder implements IPredicat
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(retVal);
|
||||||
myQueryRoot.addPredicate(retVal);
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,14 +38,12 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||||
|
@ -61,6 +59,7 @@ import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.param.CompositeParam;
|
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
import ca.uhn.fhir.rest.param.HasParam;
|
import ca.uhn.fhir.rest.param.HasParam;
|
||||||
|
@ -141,7 +140,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
RequestDetails theRequest,
|
RequestDetails theRequest,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
//Is this just to ensure the chain has been split correctly???
|
// This just to ensure the chain has been split correctly
|
||||||
assert theParamName.contains(".") == false;
|
assert theParamName.contains(".") == false;
|
||||||
|
|
||||||
if ((operation != null) &&
|
if ((operation != null) &&
|
||||||
|
@ -155,7 +154,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Join<ResourceTable, ResourceLink> join = createJoin(SearchBuilderJoinEnum.REFERENCE, theParamName);
|
From<?, ResourceLink> join = myQueryStack.createJoin(SearchBuilderJoinEnum.REFERENCE, theParamName);
|
||||||
|
|
||||||
List<IIdType> targetIds = new ArrayList<>();
|
List<IIdType> targetIds = new ArrayList<>();
|
||||||
List<String> targetQualifiedUrls = new ArrayList<>();
|
List<String> targetQualifiedUrls = new ArrayList<>();
|
||||||
|
@ -251,17 +250,12 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
codePredicates.add(myCriteriaBuilder.and(pathPredicate, pidPredicate));
|
codePredicates.add(myCriteriaBuilder.and(pathPredicate, pidPredicate));
|
||||||
}
|
}
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
|
||||||
if (codePredicates.size() > 0) {
|
if (codePredicates.size() > 0) {
|
||||||
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate predicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
myQueryRoot.addPredicate(predicate);
|
myQueryStack.addPredicateWithImplicitTypeSelection(predicate);
|
||||||
return predicate;
|
return predicate;
|
||||||
} else {
|
} else {
|
||||||
// Add a predicate that will never match
|
return myQueryStack.addNeverMatchingPredicate();
|
||||||
Predicate pidPredicate = join.get("myTargetResourcePid").in(-1L);
|
|
||||||
myQueryRoot.clearPredicates();
|
|
||||||
myQueryRoot.addPredicate(pidPredicate);
|
|
||||||
return pidPredicate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +263,124 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
* This is for handling queries like the following: /Observation?device.identifier=urn:system|foo in which we use a chain
|
* This is for handling queries like the following: /Observation?device.identifier=urn:system|foo in which we use a chain
|
||||||
* on the device.
|
* 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, RequestPartitionId theRequestPartitionId) {
|
private Predicate addPredicateReferenceWithChain(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList, From<?, ResourceLink> theJoin, List<Predicate> theCodePredicates, ReferenceParam theReferenceParam, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Which resource types can the given chained parameter actually link to? This might be a list
|
||||||
|
* where the chain is unqualified, as in: Observation?subject.identifier=(...)
|
||||||
|
* since subject can link to several possible target types.
|
||||||
|
*
|
||||||
|
* If the user has qualified the chain, as in: Observation?subject:Patient.identifier=(...)
|
||||||
|
* this is just a simple 1-entry list.
|
||||||
|
*/
|
||||||
|
final List<Class<? extends IBaseResource>> resourceTypes = determineCandidateResourceTypesForChain(theResourceName, theParamName, theReferenceParam);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle chain on _type
|
||||||
|
*/
|
||||||
|
if (Constants.PARAM_TYPE.equals(theReferenceParam.getChain())) {
|
||||||
|
return createChainPredicateOnType(theResourceName, theParamName, theJoin, theReferenceParam, resourceTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean foundChainMatch = false;
|
||||||
|
List<Class<? extends IBaseResource>> candidateTargetTypes = new ArrayList<>();
|
||||||
|
for (Class<? extends IBaseResource> nextType : resourceTypes) {
|
||||||
|
String chain = theReferenceParam.getChain();
|
||||||
|
|
||||||
|
String remainingChain = null;
|
||||||
|
int chainDotIndex = chain.indexOf('.');
|
||||||
|
if (chainDotIndex != -1) {
|
||||||
|
remainingChain = chain.substring(chainDotIndex + 1);
|
||||||
|
chain = chain.substring(0, chainDotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeResourceDefinition typeDef = myContext.getResourceDefinition(nextType);
|
||||||
|
String subResourceName = typeDef.getName();
|
||||||
|
|
||||||
|
IDao dao = myDaoRegistry.getResourceDao(nextType);
|
||||||
|
if (dao == null) {
|
||||||
|
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qualifierIndex = chain.indexOf(':');
|
||||||
|
String qualifier = null;
|
||||||
|
if (qualifierIndex != -1) {
|
||||||
|
qualifier = chain.substring(qualifierIndex);
|
||||||
|
chain = chain.substring(0, qualifierIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMeta = ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(chain);
|
||||||
|
RuntimeSearchParam param = null;
|
||||||
|
if (!isMeta) {
|
||||||
|
param = mySearchParamRegistry.getSearchParamByName(typeDef, chain);
|
||||||
|
if (param == null) {
|
||||||
|
ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<IQueryParameterType> orValues = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (IQueryParameterType next : theList) {
|
||||||
|
String nextValue = next.getValueAsQueryToken(myContext);
|
||||||
|
IQueryParameterType chainValue = mapReferenceChainToRawParamType(remainingChain, param, theParamName, qualifier, nextType, chain, isMeta, nextValue);
|
||||||
|
if (chainValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foundChainMatch = true;
|
||||||
|
orValues.add(chainValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is false, we throw an exception below so no sense doing any further processing
|
||||||
|
if (foundChainMatch) {
|
||||||
|
Subquery<Long> subQ = createLinkSubquery(chain, subResourceName, orValues, theRequest, theRequestPartitionId);
|
||||||
|
|
||||||
|
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, theJoin);
|
||||||
|
Predicate pidPredicate = theJoin.get("myTargetResourcePid").in(subQ);
|
||||||
|
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, pidPredicate);
|
||||||
|
theCodePredicates.add(andPredicate);
|
||||||
|
candidateTargetTypes.add(nextType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundChainMatch) {
|
||||||
|
throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theReferenceParam.getChain()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateTargetTypes.size() > 1) {
|
||||||
|
warnAboutPerformanceOnUnqualifiedResources(theParamName, theRequest, candidateTargetTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate predicate = myCriteriaBuilder.or(toArray(theCodePredicates));
|
||||||
|
myQueryStack.addPredicate(predicate);
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate createChainPredicateOnType(String theResourceName, String theParamName, From<?, ResourceLink> theJoin, ReferenceParam theReferenceParam, List<Class<? extends IBaseResource>> theResourceTypes) {
|
||||||
|
String typeValue = theReferenceParam.getValue();
|
||||||
|
|
||||||
|
Class<? extends IBaseResource> wantedType;
|
||||||
|
try {
|
||||||
|
wantedType = myContext.getResourceDefinition(typeValue).getImplementingClass();
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
throw newInvalidResourceTypeException(typeValue);
|
||||||
|
}
|
||||||
|
if (!theResourceTypes.contains(wantedType)) {
|
||||||
|
throw newInvalidTargetTypeForChainException(theResourceName, theParamName, typeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, theJoin);
|
||||||
|
Predicate sourceTypeParameter = myCriteriaBuilder.equal(theJoin.get("mySourceResourceType"), myResourceName);
|
||||||
|
Predicate targetTypeParameter = myCriteriaBuilder.equal(theJoin.get("myTargetResourceType"), typeValue);
|
||||||
|
|
||||||
|
Predicate composite = myCriteriaBuilder.and(pathPredicate, sourceTypeParameter, targetTypeParameter);
|
||||||
|
myQueryStack.addPredicate(composite);
|
||||||
|
return composite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private List<Class<? extends IBaseResource>> determineCandidateResourceTypesForChain(String theResourceName, String theParamName, ReferenceParam theReferenceParam) {
|
||||||
final List<Class<? extends IBaseResource>> resourceTypes;
|
final List<Class<? extends IBaseResource>> resourceTypes;
|
||||||
if (!theReferenceParam.hasResourceType()) {
|
if (!theReferenceParam.hasResourceType()) {
|
||||||
|
|
||||||
|
@ -339,101 +450,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return resourceTypes;
|
||||||
// Handle chain on _type
|
|
||||||
if (Constants.PARAM_TYPE.equals(theReferenceParam.getChain())) {
|
|
||||||
String typeValue = theReferenceParam.getValue();
|
|
||||||
|
|
||||||
Class<? extends IBaseResource> wantedType;
|
|
||||||
try {
|
|
||||||
wantedType = myContext.getResourceDefinition(typeValue).getImplementingClass();
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
throw newInvalidResourceTypeException(typeValue);
|
|
||||||
}
|
|
||||||
if (!resourceTypes.contains(wantedType)) {
|
|
||||||
throw newInvalidTargetTypeForChainException(theResourceName, theParamName, typeValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, theJoin);
|
|
||||||
Predicate sourceTypeParameter = myCriteriaBuilder.equal(theJoin.get("mySourceResourceType"), myResourceName);
|
|
||||||
Predicate targetTypeParameter = myCriteriaBuilder.equal(theJoin.get("myTargetResourceType"), typeValue);
|
|
||||||
|
|
||||||
Predicate composite = myCriteriaBuilder.and(pathPredicate, sourceTypeParameter, targetTypeParameter);
|
|
||||||
myQueryRoot.addPredicate(composite);
|
|
||||||
return composite;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean foundChainMatch = false;
|
|
||||||
List<Class<? extends IBaseResource>> candidateTargetTypes = new ArrayList<>();
|
|
||||||
for (Class<? extends IBaseResource> nextType : resourceTypes) {
|
|
||||||
String chain = theReferenceParam.getChain();
|
|
||||||
|
|
||||||
String remainingChain = null;
|
|
||||||
int chainDotIndex = chain.indexOf('.');
|
|
||||||
if (chainDotIndex != -1) {
|
|
||||||
remainingChain = chain.substring(chainDotIndex + 1);
|
|
||||||
chain = chain.substring(0, chainDotIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
RuntimeResourceDefinition typeDef = myContext.getResourceDefinition(nextType);
|
|
||||||
String subResourceName = typeDef.getName();
|
|
||||||
|
|
||||||
IDao dao = myDaoRegistry.getResourceDao(nextType);
|
|
||||||
if (dao == null) {
|
|
||||||
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qualifierIndex = chain.indexOf(':');
|
|
||||||
String qualifier = null;
|
|
||||||
if (qualifierIndex != -1) {
|
|
||||||
qualifier = chain.substring(qualifierIndex);
|
|
||||||
chain = chain.substring(0, qualifierIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isMeta = ResourceMetaParams.RESOURCE_META_PARAMS.containsKey(chain);
|
|
||||||
RuntimeSearchParam param = null;
|
|
||||||
if (!isMeta) {
|
|
||||||
param = mySearchParamRegistry.getSearchParamByName(typeDef, chain);
|
|
||||||
if (param == null) {
|
|
||||||
ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<IQueryParameterType> orValues = Lists.newArrayList();
|
|
||||||
|
|
||||||
for (IQueryParameterType next : theList) {
|
|
||||||
String nextValue = next.getValueAsQueryToken(myContext);
|
|
||||||
IQueryParameterType chainValue = mapReferenceChainToRawParamType(remainingChain, param, theParamName, qualifier, nextType, chain, isMeta, nextValue);
|
|
||||||
if (chainValue == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foundChainMatch = true;
|
|
||||||
orValues.add(chainValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Subquery<Long> subQ = createLinkSubquery(foundChainMatch, chain, subResourceName, orValues, theRequest, theRequestPartitionId);
|
|
||||||
|
|
||||||
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, theJoin);
|
|
||||||
Predicate pidPredicate = theJoin.get("myTargetResourcePid").in(subQ);
|
|
||||||
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, pidPredicate);
|
|
||||||
theCodePredicates.add(andPredicate);
|
|
||||||
candidateTargetTypes.add(nextType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundChainMatch) {
|
|
||||||
throw new InvalidRequestException(myContext.getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidParameterChain", theParamName + '.' + theReferenceParam.getChain()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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, @Nullable List<Class<? extends IBaseResource>> theCandidateTargetTypes) {
|
private void warnAboutPerformanceOnUnqualifiedResources(String theParamName, RequestDetails theRequest, @Nullable List<Class<? extends IBaseResource>> theCandidateTargetTypes) {
|
||||||
|
@ -510,33 +527,30 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
return chainValue;
|
return chainValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Subquery<Long> createLinkSubquery(boolean theFoundChainMatch, String theChain, String theSubResourceName, List<IQueryParameterType> theOrValues, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
Subquery<Long> createLinkSubquery(String theChain, String theSubResourceName, List<IQueryParameterType> theOrValues, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||||
Subquery<Long> subQ = myQueryRoot.subquery(Long.class);
|
|
||||||
/*
|
/*
|
||||||
* We're doing a chain call, so push the current query root
|
* We're doing a chain call, so push the current query root
|
||||||
* and predicate list down and put new ones at the top of the
|
* and predicate list down and put new ones at the top of the
|
||||||
* stack and run a subquery
|
* stack and run a subquery
|
||||||
*/
|
*/
|
||||||
myQueryRoot.push(subQ);
|
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(theSubResourceName, theChain);
|
||||||
subQ.select(myQueryRoot.get("myId").as(Long.class));
|
if (nextParamDef != null && !theChain.startsWith("_")) {
|
||||||
|
myQueryStack.pushIndexTableSubQuery();
|
||||||
|
} else {
|
||||||
|
myQueryStack.pushResourceTableSubQuery(theSubResourceName);
|
||||||
|
}
|
||||||
|
|
||||||
List<List<IQueryParameterType>> andOrParams = new ArrayList<>();
|
List<List<IQueryParameterType>> andOrParams = new ArrayList<>();
|
||||||
andOrParams.add(theOrValues);
|
andOrParams.add(theOrValues);
|
||||||
|
|
||||||
// Create the subquery predicates
|
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.equal(myQueryRoot.get("myResourceType"), theSubResourceName));
|
|
||||||
myQueryRoot.addPredicate(myCriteriaBuilder.isNull(myQueryRoot.get("myDeleted")));
|
|
||||||
|
|
||||||
if (theFoundChainMatch) {
|
|
||||||
searchForIdsWithAndOr(theSubResourceName, theChain, andOrParams, theRequest, theRequestPartitionId);
|
searchForIdsWithAndOr(theSubResourceName, theChain, andOrParams, theRequest, theRequestPartitionId);
|
||||||
subQ.where(myQueryRoot.getPredicateArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop the old query root and predicate list back
|
* Pop the old query root and predicate list back
|
||||||
*/
|
*/
|
||||||
myQueryRoot.pop();
|
return (Subquery<Long>) myQueryStack.pop();
|
||||||
return subQ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void searchForIdsWithAndOr(String theResourceName, String theParamName, List<List<IQueryParameterType>> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
void searchForIdsWithAndOr(String theResourceName, String theParamName, List<List<IQueryParameterType>> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
||||||
|
@ -656,18 +670,18 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
// TODO: we clear the predicates below because the filter builds up
|
// TODO: we clear the predicates below because the filter builds up
|
||||||
// its own collection of predicates. It'd probably be good at some
|
// its own collection of predicates. It'd probably be good at some
|
||||||
// point to do something more fancy...
|
// point to do something more fancy...
|
||||||
ArrayList<Predicate> holdPredicates = new ArrayList<>(myQueryRoot.getPredicates());
|
ArrayList<Predicate> holdPredicates = new ArrayList<>(myQueryStack.getPredicates());
|
||||||
|
|
||||||
Predicate filterPredicate = processFilter(filter, theResourceName, theRequest, theRequestPartitionId);
|
Predicate filterPredicate = processFilter(filter, theResourceName, theRequest, theRequestPartitionId);
|
||||||
myQueryRoot.clearPredicates();
|
myQueryStack.clearPredicates();
|
||||||
myQueryRoot.addPredicates(holdPredicates);
|
myQueryStack.addPredicates(holdPredicates);
|
||||||
myQueryRoot.addPredicate(filterPredicate);
|
myQueryStack.addPredicate(filterPredicate);
|
||||||
|
|
||||||
// Because filters can have an OR at the root, we never know for sure that we haven't done an optimized
|
// Because filters can have an OR at the root, we never know for sure that we haven't done an optimized
|
||||||
// search that doesn't check the resource type. This could be improved in the future, but for now it's
|
// search that doesn't check the resource type. This could be improved in the future, but for now it's
|
||||||
// safest to just clear this flag. The test "testRetrieveDifferentTypeEq" will fail if we don't clear
|
// safest to just clear this flag. The test "testRetrieveDifferentTypeEq" will fail if we don't clear
|
||||||
// this here.
|
// this here.
|
||||||
myQueryRoot.clearHasIndexJoins();
|
myQueryStack.clearHasImplicitTypeSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,13 +858,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
Predicate predicate;
|
Predicate predicate;
|
||||||
if ((operation == null) ||
|
if ((operation == null) ||
|
||||||
(operation == SearchFilterParser.CompareOperation.eq)) {
|
(operation == SearchFilterParser.CompareOperation.eq)) {
|
||||||
predicate = myQueryRoot.get("myLanguage").as(String.class).in(values);
|
predicate = myQueryStack.get("myLanguage").as(String.class).in(values);
|
||||||
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
} else if (operation == SearchFilterParser.CompareOperation.ne) {
|
||||||
predicate = myQueryRoot.get("myLanguage").as(String.class).in(values).not();
|
predicate = myQueryStack.get("myLanguage").as(String.class).in(values).not();
|
||||||
} else {
|
} else {
|
||||||
throw new InvalidRequestException("Unsupported operator specified in language query, only \"eq\" and \"ne\" are supported");
|
throw new InvalidRequestException("Unsupported operator specified in language query, only \"eq\" and \"ne\" are supported");
|
||||||
}
|
}
|
||||||
myQueryRoot.addPredicate(predicate);
|
myQueryStack.addPredicate(predicate);
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
return predicate;
|
return predicate;
|
||||||
}
|
}
|
||||||
|
@ -874,7 +888,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
throw new InvalidRequestException(msg);
|
throw new InvalidRequestException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Join<ResourceTable, ResourceHistoryProvenanceEntity> join = myQueryRoot.join("myProvenance", JoinType.LEFT);
|
From<?, ResourceHistoryProvenanceEntity> join = myQueryStack.createJoin(SearchBuilderJoinEnum.PROVENANCE, Constants.PARAM_SOURCE);
|
||||||
|
|
||||||
List<Predicate> codePredicates = new ArrayList<>();
|
List<Predicate> codePredicates = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -894,7 +908,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
myQueryRoot.addPredicate(retVal);
|
myQueryStack.addPredicate(retVal);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -962,13 +976,13 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
Subquery<Long> subQ = myPredicateBuilder.createLinkSubquery(paramName, targetResourceType, orValues, theRequest, theRequestPartitionId);
|
Subquery<Long> subQ = myPredicateBuilder.createLinkSubquery(paramName, targetResourceType, orValues, theRequest, theRequestPartitionId);
|
||||||
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinksAsTarget", JoinType.LEFT);
|
Join<?, ResourceLink> join = (Join) myQueryStack.createJoin(SearchBuilderJoinEnum.HAS, "_has");
|
||||||
|
|
||||||
Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join);
|
Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join);
|
||||||
Predicate sourceTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType"), theResourceType);
|
Predicate sourceTypePredicate = myCriteriaBuilder.equal(join.get("myTargetResourceType"), theResourceType);
|
||||||
Predicate sourcePidPredicate = join.get("mySourceResourcePid").in(subQ);
|
Predicate sourcePidPredicate = join.get("mySourceResourcePid").in(subQ);
|
||||||
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate);
|
Predicate andPredicate = myCriteriaBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate);
|
||||||
myQueryRoot.addPredicate(andPredicate);
|
myQueryStack.addPredicate(andPredicate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,15 +1001,15 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
|
|
||||||
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
|
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
|
||||||
IQueryParameterType leftValue = cp.getLeftValue();
|
IQueryParameterType leftValue = cp.getLeftValue();
|
||||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), left, leftValue, theRequestPartitionId));
|
myQueryStack.addPredicate(createCompositeParamPart(theResourceName, myQueryStack.getRootForComposite(), left, leftValue, theRequestPartitionId));
|
||||||
|
|
||||||
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
|
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
|
||||||
IQueryParameterType rightValue = cp.getRightValue();
|
IQueryParameterType rightValue = cp.getRightValue();
|
||||||
myQueryRoot.addPredicate(createCompositeParamPart(theResourceName, myQueryRoot.getRoot(), right, rightValue, theRequestPartitionId));
|
myQueryStack.addPredicate(createCompositeParamPart(theResourceName, myQueryStack.getRootForComposite(), right, rightValue, theRequestPartitionId));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate createCompositeParamPart(String theResourceName, Root<ResourceTable> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue, RequestPartitionId theRequestPartitionId) {
|
private Predicate createCompositeParamPart(String theResourceName, Root<?> theRoot, RuntimeSearchParam theParam, IQueryParameterType leftValue, RequestPartitionId theRequestPartitionId) {
|
||||||
Predicate retVal = null;
|
Predicate retVal = null;
|
||||||
switch (theParam.getParamType()) {
|
switch (theParam.getParamType()) {
|
||||||
case STRING: {
|
case STRING: {
|
||||||
|
|
|
@ -23,9 +23,8 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -36,7 +35,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -60,10 +58,10 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
||||||
@Nullable
|
@Nullable
|
||||||
Predicate addPredicateResourceId(List<List<IQueryParameterType>> theValues, String theResourceName, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
Predicate addPredicateResourceId(List<List<IQueryParameterType>> theValues, String theResourceName, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
Predicate nextPredicate = createPredicate(myQueryRoot.getRoot(), theResourceName, theValues, theOperation, theRequestPartitionId);
|
Predicate nextPredicate = createPredicate(theResourceName, theValues, theOperation, theRequestPartitionId);
|
||||||
|
|
||||||
if (nextPredicate != null) {
|
if (nextPredicate != null) {
|
||||||
myQueryRoot.addPredicate(nextPredicate);
|
myQueryStack.addPredicate(nextPredicate);
|
||||||
return nextPredicate;
|
return nextPredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +69,7 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Predicate createPredicate(Root<ResourceTable> theRoot, String theResourceName, List<List<IQueryParameterType>> theValues, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
private Predicate createPredicate(String theResourceName, List<List<IQueryParameterType>> theValues, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
|
||||||
Predicate nextPredicate = null;
|
Predicate nextPredicate = null;
|
||||||
|
|
||||||
Set<ResourcePersistentId> allOrPids = null;
|
Set<ResourcePersistentId> allOrPids = null;
|
||||||
|
@ -110,7 +108,7 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
||||||
if (allOrPids != null && allOrPids.isEmpty()) {
|
if (allOrPids != null && allOrPids.isEmpty()) {
|
||||||
|
|
||||||
// This will never match
|
// This will never match
|
||||||
nextPredicate = myCriteriaBuilder.equal(theRoot.get("myId").as(Long.class), -1);
|
nextPredicate = myCriteriaBuilder.equal(myQueryStack.getResourcePidColumn(), -1);
|
||||||
|
|
||||||
} else if (allOrPids != null) {
|
} else if (allOrPids != null) {
|
||||||
|
|
||||||
|
@ -120,11 +118,11 @@ class PredicateBuilderResourceId extends BasePredicateBuilder {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
default:
|
default:
|
||||||
case eq:
|
case eq:
|
||||||
codePredicates.add(theRoot.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(allOrPids)));
|
codePredicates.add(myQueryStack.getResourcePidColumn().in(ResourcePersistentId.toLongList(allOrPids)));
|
||||||
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
||||||
break;
|
break;
|
||||||
case ne:
|
case ne:
|
||||||
codePredicates.add(theRoot.get("myId").as(Long.class).in(ResourcePersistentId.toLongList(allOrPids)).not());
|
codePredicates.add(myQueryStack.getResourcePidColumn().in(ResourcePersistentId.toLongList(allOrPids)).not());
|
||||||
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
nextPredicate = myCriteriaBuilder.and(toArray(codePredicates));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
|
||||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
@ -38,7 +37,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -46,6 +44,7 @@ import java.util.List;
|
||||||
@Component
|
@Component
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
class PredicateBuilderString extends BasePredicateBuilder implements IPredicateBuilder {
|
class PredicateBuilderString extends BasePredicateBuilder implements IPredicateBuilder {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
DaoConfig myDaoConfig;
|
DaoConfig myDaoConfig;
|
||||||
|
|
||||||
|
@ -60,7 +59,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
||||||
SearchFilterParser.CompareOperation theOperation,
|
SearchFilterParser.CompareOperation theOperation,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamString> join = createJoin(SearchBuilderJoinEnum.STRING, theParamName);
|
From<?, ResourceIndexedSearchParamString> join = myQueryStack.createJoin(SearchBuilderJoinEnum.STRING, theParamName);
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
|
@ -77,8 +76,7 @@ class PredicateBuilderString extends BasePredicateBuilder implements IPredicateB
|
||||||
|
|
||||||
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate retVal = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(retVal);
|
||||||
myQueryRoot.addPredicate(retVal);
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.dao.predicate;
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||||
|
@ -41,8 +40,6 @@ import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.JoinType;
|
|
||||||
import javax.persistence.criteria.Path;
|
import javax.persistence.criteria.Path;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
|
@ -137,14 +134,14 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
||||||
if (paramInverted) {
|
if (paramInverted) {
|
||||||
ourLog.debug("Searching for _tag:not");
|
ourLog.debug("Searching for _tag:not");
|
||||||
|
|
||||||
Subquery<Long> subQ = myQueryRoot.subquery(Long.class);
|
Subquery<Long> subQ = myQueryStack.subqueryForTagNegation();
|
||||||
Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
|
Root<ResourceTag> subQfrom = subQ.from(ResourceTag.class);
|
||||||
subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
subQ.select(subQfrom.get("myResourceId").as(Long.class));
|
||||||
|
|
||||||
myQueryRoot.addPredicate(
|
myQueryStack.addPredicate(
|
||||||
myCriteriaBuilder.not(
|
myCriteriaBuilder.not(
|
||||||
myCriteriaBuilder.in(
|
myCriteriaBuilder.in(
|
||||||
myQueryRoot.get("myId")
|
myQueryStack.get("myId")
|
||||||
).value(subQ)
|
).value(subQ)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -162,7 +159,7 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Join<ResourceTable, ResourceTag> tagJoin = myQueryRoot.join("myTags", JoinType.LEFT);
|
From<?, ResourceTag> tagJoin = myQueryStack.createJoin(SearchBuilderJoinEnum.RESOURCE_TAGS, null);
|
||||||
From<ResourceTag, TagDefinition> defJoin = tagJoin.join("myTag");
|
From<ResourceTag, TagDefinition> defJoin = tagJoin.join("myTag");
|
||||||
|
|
||||||
Predicate tagListPredicate = createPredicateTagList(defJoin, myCriteriaBuilder, tagType, tokens);
|
Predicate tagListPredicate = createPredicateTagList(defJoin, myCriteriaBuilder, tagType, tokens);
|
||||||
|
@ -172,7 +169,7 @@ class PredicateBuilderTag extends BasePredicateBuilder {
|
||||||
addPartitionIdPredicate(theRequestPartitionId, tagJoin, predicates);
|
addPartitionIdPredicate(theRequestPartitionId, tagJoin, predicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
myQueryRoot.addPredicates(predicates);
|
myQueryStack.addPredicates(predicates);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
|
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||||
|
@ -53,7 +52,6 @@ import org.springframework.stereotype.Component;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.Expression;
|
import javax.persistence.criteria.Expression;
|
||||||
import javax.persistence.criteria.From;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Path;
|
import javax.persistence.criteria.Path;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -91,7 +89,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
From<?, ResourceIndexedSearchParamToken> join = myQueryStack.createJoin(SearchBuilderJoinEnum.TOKEN, theParamName);
|
||||||
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
|
addPartitionIdPredicate(theRequestPartitionId, join, codePredicates);
|
||||||
|
|
||||||
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
|
Collection<Predicate> singleCode = createPredicateToken(tokens, theResourceName, theParamName, myCriteriaBuilder, join, theOperation, theRequestPartitionId);
|
||||||
|
@ -139,8 +137,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
||||||
|
|
||||||
Predicate spPredicate = myCriteriaBuilder.or(toArray(codePredicates));
|
Predicate spPredicate = myCriteriaBuilder.or(toArray(codePredicates));
|
||||||
|
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(spPredicate);
|
||||||
myQueryRoot.addPredicate(spPredicate);
|
|
||||||
|
|
||||||
return spPredicate;
|
return spPredicate;
|
||||||
}
|
}
|
||||||
|
@ -375,7 +372,13 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate predicate = hashField.in(values);
|
Predicate predicate;
|
||||||
|
if (values.size() == 1) {
|
||||||
|
predicate = myCriteriaBuilder.equal(hashField, values.get(0));
|
||||||
|
} else {
|
||||||
|
predicate = hashField.in(values);
|
||||||
|
}
|
||||||
|
|
||||||
if (theModifier == TokenParamModifier.NOT) {
|
if (theModifier == TokenParamModifier.NOT) {
|
||||||
Predicate identityPredicate = theBuilder.equal(theFrom.get("myHashIdentity").as(Long.class), BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName));
|
Predicate identityPredicate = theBuilder.equal(theFrom.get("myHashIdentity").as(Long.class), BaseResourceIndexedSearchParam.calculateHashIdentity(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName));
|
||||||
Predicate disjunctionPredicate = theBuilder.not(predicate);
|
Predicate disjunctionPredicate = theBuilder.not(predicate);
|
||||||
|
|
|
@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.dao.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.param.UriParam;
|
import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
|
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
|
||||||
|
@ -35,7 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Scope;
|
import org.springframework.context.annotation.Scope;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.persistence.criteria.Join;
|
import javax.persistence.criteria.From;
|
||||||
import javax.persistence.criteria.Predicate;
|
import javax.persistence.criteria.Predicate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -59,7 +58,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
||||||
SearchFilterParser.CompareOperation operation,
|
SearchFilterParser.CompareOperation operation,
|
||||||
RequestPartitionId theRequestPartitionId) {
|
RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamUri> join = createJoin(SearchBuilderJoinEnum.URI, theParamName);
|
From<?, ResourceIndexedSearchParamUri> join = myQueryStack.createJoin(SearchBuilderJoinEnum.URI, theParamName);
|
||||||
|
|
||||||
if (theList.get(0).getMissing() != null) {
|
if (theList.get(0).getMissing() != null) {
|
||||||
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
addPredicateParamMissingForNonReference(theResourceName, theParamName, theList.get(0).getMissing(), join, theRequestPartitionId);
|
||||||
|
@ -169,8 +168,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
||||||
*/
|
*/
|
||||||
if (codePredicates.isEmpty()) {
|
if (codePredicates.isEmpty()) {
|
||||||
Predicate predicate = myCriteriaBuilder.isNull(join.get("myMissing").as(String.class));
|
Predicate predicate = myCriteriaBuilder.isNull(join.get("myMissing").as(String.class));
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(predicate);
|
||||||
myQueryRoot.addPredicate(predicate);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,8 +179,7 @@ class PredicateBuilderUri extends BasePredicateBuilder implements IPredicateBuil
|
||||||
join,
|
join,
|
||||||
orPredicate,
|
orPredicate,
|
||||||
theRequestPartitionId);
|
theRequestPartitionId);
|
||||||
myQueryRoot.setHasIndexJoins();
|
myQueryStack.addPredicateWithImplicitTypeSelection(outerPredicate);
|
||||||
myQueryRoot.addPredicate(outerPredicate);
|
|
||||||
return outerPredicate;
|
return outerPredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.predicate;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2020 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
|
|
||||||
import javax.persistence.criteria.*;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
public class QueryRoot {
|
|
||||||
private final Stack<QueryRootEntry> myQueryRootStack = new Stack<>();
|
|
||||||
private boolean myHasIndexJoins;
|
|
||||||
|
|
||||||
public void push(AbstractQuery<Long> theResourceTableQuery) {
|
|
||||||
myQueryRootStack.push(new QueryRootEntry(theResourceTableQuery));
|
|
||||||
}
|
|
||||||
|
|
||||||
private QueryRootEntry top() {
|
|
||||||
return myQueryRootStack.peek();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pop() {
|
|
||||||
myQueryRootStack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Root<ResourceTable> getRoot() {
|
|
||||||
return top().getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <Y> Path<Y> get(String theAttributeName) {
|
|
||||||
return top().get(theAttributeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <Y> Join<ResourceTable, Y> join(String theAttributeName, JoinType theJoinType) {
|
|
||||||
return top().join(theAttributeName, theJoinType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Join<?,?> getIndexJoin(SearchBuilderJoinKey theKey) {
|
|
||||||
return top().getIndexJoin(theKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPredicate(Predicate thePredicate) {
|
|
||||||
top().addPredicate(thePredicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPredicates(List<Predicate> thePredicates) {
|
|
||||||
top().addPredicates(thePredicates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Predicate[] getPredicateArray() {
|
|
||||||
return top().getPredicateArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
void putIndex(SearchBuilderJoinKey theKey, Join<ResourceTable, ResourceIndexedSearchParamDate> theJoin) {
|
|
||||||
myHasIndexJoins = true;
|
|
||||||
top().putIndex(theKey, theJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearPredicates() {
|
|
||||||
top().clearPredicates();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Predicate> getPredicates() {
|
|
||||||
return top().getPredicates();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void where(Predicate theAnd) {
|
|
||||||
top().where(theAnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> Subquery<T> subquery(Class<T> theClass) {
|
|
||||||
return top().subquery(theClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasIndexJoins() {
|
|
||||||
return myHasIndexJoins;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHasIndexJoins() {
|
|
||||||
myHasIndexJoins = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearHasIndexJoins() {
|
|
||||||
myHasIndexJoins = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.predicate;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2020 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
|
|
||||||
import javax.persistence.criteria.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class QueryRootEntry {
|
|
||||||
private final AbstractQuery<Long> myResourceTableQuery;
|
|
||||||
private final Root<ResourceTable> myResourceTableRoot;
|
|
||||||
private final ArrayList<Predicate> myPredicates = new ArrayList<>();
|
|
||||||
private final IndexJoins myIndexJoins = new IndexJoins();
|
|
||||||
|
|
||||||
public QueryRootEntry(AbstractQuery<Long> theResourceTableQuery) {
|
|
||||||
myResourceTableQuery = theResourceTableQuery;
|
|
||||||
myResourceTableRoot = theResourceTableQuery.from(ResourceTable.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Root<ResourceTable> getRoot() {
|
|
||||||
return myResourceTableRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <Y> Path<Y> get(String theAttributeName) {
|
|
||||||
return myResourceTableRoot.get(theAttributeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <Y> Join<ResourceTable, Y> join(String theAttributeName, JoinType theJoinType) {
|
|
||||||
return myResourceTableRoot.join(theAttributeName, theJoinType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Join<?,?> getIndexJoin(SearchBuilderJoinKey theKey) {
|
|
||||||
return myIndexJoins.get(theKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPredicate(Predicate thePredicate) {
|
|
||||||
myPredicates.add(thePredicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPredicates(List<Predicate> thePredicates) {
|
|
||||||
myPredicates.addAll(thePredicates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Predicate[] getPredicateArray() {
|
|
||||||
return myPredicates.toArray(new Predicate[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void putIndex(SearchBuilderJoinKey theKey, Join<ResourceTable, ResourceIndexedSearchParamDate> theJoin) {
|
|
||||||
myIndexJoins.put(theKey, theJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearPredicates() {
|
|
||||||
myPredicates.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Predicate> getPredicates() {
|
|
||||||
return Collections.unmodifiableList(myPredicates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void where(Predicate theAnd) {
|
|
||||||
myResourceTableQuery.where(theAnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T> Subquery<T> subquery(Class<T> theClass) {
|
|
||||||
return myResourceTableQuery.subquery(theClass);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,6 +28,8 @@ public enum SearchBuilderJoinEnum {
|
||||||
STRING,
|
STRING,
|
||||||
TOKEN,
|
TOKEN,
|
||||||
URI,
|
URI,
|
||||||
COORDS
|
COORDS,
|
||||||
|
HAS,
|
||||||
|
FORCED_ID, PRESENCE, COMPOSITE_UNIQUE, RESOURCE_TAGS, PROVENANCE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.IndexJoins;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.AbstractQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.Expression;
|
||||||
|
import javax.persistence.criteria.From;
|
||||||
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.Order;
|
||||||
|
import javax.persistence.criteria.Path;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Subquery;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
abstract class QueryRootEntry {
|
||||||
|
private final ArrayList<Predicate> myPredicates = new ArrayList<>();
|
||||||
|
private final IndexJoins myIndexJoins = new IndexJoins();
|
||||||
|
private final CriteriaBuilder myCriteriaBuilder;
|
||||||
|
private boolean myHasImplicitTypeSelection;
|
||||||
|
|
||||||
|
QueryRootEntry(CriteriaBuilder theCriteriaBuilder) {
|
||||||
|
myCriteriaBuilder = theCriteriaBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHasImplicitTypeSelection() {
|
||||||
|
return myHasImplicitTypeSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHasImplicitTypeSelection(boolean theHasImplicitTypeSelection) {
|
||||||
|
myHasImplicitTypeSelection = theHasImplicitTypeSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Join<?, ?>> getIndexJoin(SearchBuilderJoinKey theKey) {
|
||||||
|
return Optional.ofNullable(myIndexJoins.get(theKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPredicate(Predicate thePredicate) {
|
||||||
|
myPredicates.add(thePredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPredicates(List<Predicate> thePredicates) {
|
||||||
|
myPredicates.addAll(thePredicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate addNeverMatchingPredicate() {
|
||||||
|
Predicate predicate = myCriteriaBuilder.equal(getResourcePidColumn(), -1L);
|
||||||
|
clearPredicates();
|
||||||
|
addPredicate(predicate);
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate[] getPredicateArray() {
|
||||||
|
return myPredicates.toArray(new Predicate[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void putIndex(SearchBuilderJoinKey theKey, Join<?, ?> theJoin) {
|
||||||
|
myIndexJoins.put(theKey, theJoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPredicates() {
|
||||||
|
myPredicates.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Predicate> getPredicates() {
|
||||||
|
return Collections.unmodifiableList(myPredicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
<Y> Path<Y> get(String theAttributeName) {
|
||||||
|
return getRoot().get(theAttributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractQuery<Long> pop() {
|
||||||
|
Predicate[] predicateArray = getPredicateArray();
|
||||||
|
if (predicateArray.length == 1) {
|
||||||
|
getQueryRoot().where(predicateArray[0]);
|
||||||
|
} else {
|
||||||
|
getQueryRoot().where(myCriteriaBuilder.and(predicateArray));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getQueryRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void orderBy(List<Order> theOrders);
|
||||||
|
|
||||||
|
abstract Expression<Date> getLastUpdatedColumn();
|
||||||
|
|
||||||
|
abstract <T> From<?, T> createJoin(SearchBuilderJoinEnum theType, String theSearchParameterName);
|
||||||
|
|
||||||
|
abstract AbstractQuery<Long> getQueryRoot();
|
||||||
|
|
||||||
|
abstract Root<?> getRoot();
|
||||||
|
|
||||||
|
abstract Expression<Long> getResourcePidColumn();
|
||||||
|
|
||||||
|
abstract Subquery<Long> subqueryForTagNegation();
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndex;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.AbstractQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.Expression;
|
||||||
|
import javax.persistence.criteria.From;
|
||||||
|
import javax.persistence.criteria.Order;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Subquery;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class QueryRootEntryIndexTable extends QueryRootEntry {
|
||||||
|
private final Subquery<Long> myQuery;
|
||||||
|
private Root<? extends BaseResourceIndex> myRoot;
|
||||||
|
private SearchBuilderJoinEnum myParamType;
|
||||||
|
private Expression<Long> myResourcePidColumn;
|
||||||
|
|
||||||
|
public QueryRootEntryIndexTable(CriteriaBuilder theCriteriaBuilder, QueryRootEntry theParent) {
|
||||||
|
super(theCriteriaBuilder);
|
||||||
|
|
||||||
|
AbstractQuery<Long> queryRoot = theParent.getQueryRoot();
|
||||||
|
myQuery = queryRoot.subquery(Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void orderBy(List<Order> theOrders) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Expression<Date> getLastUpdatedColumn() {
|
||||||
|
return getRoot().get("myUpdated").as(Date.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<T> From<?, T> createJoin(SearchBuilderJoinEnum theType, String theSearchParameterName) {
|
||||||
|
if (myParamType == null) {
|
||||||
|
switch (theType) {
|
||||||
|
case REFERENCE:
|
||||||
|
myRoot = myQuery.from(ResourceLink.class);
|
||||||
|
myResourcePidColumn = myRoot.get("mySourceResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.REFERENCE;
|
||||||
|
break;
|
||||||
|
case NUMBER:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamNumber.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.NUMBER;
|
||||||
|
break;
|
||||||
|
case DATE:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamDate.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.DATE;
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamString.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.STRING;
|
||||||
|
break;
|
||||||
|
case TOKEN:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamToken.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.TOKEN;
|
||||||
|
break;
|
||||||
|
case QUANTITY:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamQuantity.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.QUANTITY;
|
||||||
|
break;
|
||||||
|
case URI:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamUri.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.URI;
|
||||||
|
break;
|
||||||
|
case COORDS:
|
||||||
|
myRoot = myQuery.from(ResourceIndexedSearchParamCoords.class);
|
||||||
|
myResourcePidColumn = myRoot.get("myResourcePid").as(Long.class);
|
||||||
|
myParamType = SearchBuilderJoinEnum.COORDS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
myQuery.select(myResourcePidColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
Validate.isTrue(theType == myParamType, "Wanted %s but got %s for %s", myParamType, theType, theSearchParameterName);
|
||||||
|
return (From<?, T>) myRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
AbstractQuery<Long> getQueryRoot() {
|
||||||
|
Validate.isTrue(myQuery != null);
|
||||||
|
return myQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Root<?> getRoot() {
|
||||||
|
Validate.isTrue(myRoot != null);
|
||||||
|
return myRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression<Long> getResourcePidColumn() {
|
||||||
|
Validate.isTrue(myResourcePidColumn != null);
|
||||||
|
return myResourcePidColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Subquery<Long> subqueryForTagNegation() {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.AbstractQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Expression;
|
||||||
|
import javax.persistence.criteria.From;
|
||||||
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.JoinType;
|
||||||
|
import javax.persistence.criteria.Order;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Subquery;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class QueryRootEntryResourceTable extends QueryRootEntry {
|
||||||
|
|
||||||
|
private final CriteriaBuilder myCriteriaBuilder;
|
||||||
|
private final AbstractQuery<Long> myQuery;
|
||||||
|
private final SearchParameterMap mySearchParameterMap;
|
||||||
|
private final RequestPartitionId myRequestPartitionId;
|
||||||
|
private final String myResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will ddd a predicate to make sure we only include non-deleted resources, and only include
|
||||||
|
* resources of the right type.
|
||||||
|
*
|
||||||
|
* If we have any joins to index tables, we get this behaviour already guaranteed so we don't
|
||||||
|
* need an explicit predicate for it.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
AbstractQuery<Long> pop() {
|
||||||
|
|
||||||
|
if (!isHasImplicitTypeSelection()) {
|
||||||
|
if (mySearchParameterMap.getEverythingMode() == null) {
|
||||||
|
addPredicate(myCriteriaBuilder.equal(getRoot().get("myResourceType"), myResourceType));
|
||||||
|
}
|
||||||
|
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myDeleted")));
|
||||||
|
if (!myRequestPartitionId.isAllPartitions()) {
|
||||||
|
if (myRequestPartitionId.getPartitionId() != null) {
|
||||||
|
addPredicate(myCriteriaBuilder.equal(getRoot().get("myPartitionIdValue").as(Integer.class), myRequestPartitionId.getPartitionId()));
|
||||||
|
} else {
|
||||||
|
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myPartitionIdValue").as(Integer.class)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Root<ResourceTable> myResourceTableRoot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root query constructor
|
||||||
|
*/
|
||||||
|
QueryRootEntryResourceTable(CriteriaBuilder theCriteriaBuilder, boolean theCountQuery, SearchParameterMap theSearchParameterMap, String theResourceType, RequestPartitionId theRequestPartitionId) {
|
||||||
|
super(theCriteriaBuilder);
|
||||||
|
myCriteriaBuilder = theCriteriaBuilder;
|
||||||
|
mySearchParameterMap = theSearchParameterMap;
|
||||||
|
myRequestPartitionId = theRequestPartitionId;
|
||||||
|
myResourceType = theResourceType;
|
||||||
|
|
||||||
|
CriteriaQuery<Long> query = myCriteriaBuilder.createQuery(Long.class);
|
||||||
|
myResourceTableRoot = query.from(ResourceTable.class);
|
||||||
|
|
||||||
|
if (theCountQuery) {
|
||||||
|
query.multiselect(myCriteriaBuilder.countDistinct(myResourceTableRoot));
|
||||||
|
} else {
|
||||||
|
query.multiselect(get("myId").as(Long.class));
|
||||||
|
}
|
||||||
|
myQuery = query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subquery constructor
|
||||||
|
*/
|
||||||
|
QueryRootEntryResourceTable(CriteriaBuilder theCriteriaBuilder, QueryRootEntry theParent, SearchParameterMap theSearchParameterMap, String theResourceType, RequestPartitionId theRequestPartitionId) {
|
||||||
|
super(theCriteriaBuilder);
|
||||||
|
myCriteriaBuilder = theCriteriaBuilder;
|
||||||
|
mySearchParameterMap = theSearchParameterMap;
|
||||||
|
myRequestPartitionId = theRequestPartitionId;
|
||||||
|
myResourceType = theResourceType;
|
||||||
|
|
||||||
|
AbstractQuery<Long> queryRoot = theParent.getQueryRoot();
|
||||||
|
Subquery<Long> query = queryRoot.subquery(Long.class);
|
||||||
|
myQuery = query;
|
||||||
|
myResourceTableRoot = myQuery.from(ResourceTable.class);
|
||||||
|
query.select(myResourceTableRoot.get("myId").as(Long.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void orderBy(List<Order> theOrders) {
|
||||||
|
assert myQuery instanceof CriteriaQuery;
|
||||||
|
|
||||||
|
((CriteriaQuery<?>)myQuery).orderBy(theOrders);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Expression<Date> getLastUpdatedColumn() {
|
||||||
|
return myResourceTableRoot.get("myUpdated").as(Date.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
<T> From<?, T> createJoin(SearchBuilderJoinEnum theType, String theSearchParameterName) {
|
||||||
|
Join<?,?> join = null;
|
||||||
|
switch (theType) {
|
||||||
|
case DATE:
|
||||||
|
join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case NUMBER:
|
||||||
|
join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case QUANTITY:
|
||||||
|
join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case REFERENCE:
|
||||||
|
join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case STRING:
|
||||||
|
join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case URI:
|
||||||
|
join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case TOKEN:
|
||||||
|
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case COORDS:
|
||||||
|
join = myResourceTableRoot.join("myParamsCoords", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case HAS:
|
||||||
|
join = myResourceTableRoot.join("myResourceLinksAsTarget", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case PROVENANCE:
|
||||||
|
join = myResourceTableRoot.join("myProvenance", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case FORCED_ID:
|
||||||
|
join = myResourceTableRoot.join("myForcedId", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case PRESENCE:
|
||||||
|
join = myResourceTableRoot.join("mySearchParamPresents", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case COMPOSITE_UNIQUE:
|
||||||
|
join = myResourceTableRoot.join("myParamsCompositeStringUnique", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
case RESOURCE_TAGS:
|
||||||
|
join = myResourceTableRoot.join("myTags", JoinType.LEFT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchBuilderJoinKey key = new SearchBuilderJoinKey(theSearchParameterName, theType);
|
||||||
|
putIndex(key, join);
|
||||||
|
|
||||||
|
return (From<?, T>) join;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
AbstractQuery<Long> getQueryRoot() {
|
||||||
|
return myQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Root<ResourceTable> getRoot() {
|
||||||
|
return myResourceTableRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression<Long> getResourcePidColumn() {
|
||||||
|
return myResourceTableRoot.get("myId").as(Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Subquery<Long> subqueryForTagNegation() {
|
||||||
|
return myQuery.subquery(Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,284 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao.predicate.querystack;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum;
|
||||||
|
import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
import javax.persistence.criteria.AbstractQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.Expression;
|
||||||
|
import javax.persistence.criteria.From;
|
||||||
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.Order;
|
||||||
|
import javax.persistence.criteria.Path;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Subquery;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents a SQL SELECT statement that is selecting for resource PIDs, ie.
|
||||||
|
* the <code>RES_ID</code> column on the <code>HFJ_RESOURCE</code> ({@link ca.uhn.fhir.jpa.model.entity.ResourceTable})
|
||||||
|
* table.
|
||||||
|
* <p>
|
||||||
|
* We add predicates (WHERE A=B) to it, and can join other tables to it as well. At the root of the query
|
||||||
|
* we are typically doing a <code>select RES_ID from HFJ_RESOURCE where (....)</code> and this class
|
||||||
|
* is used to build the <i>where</i> clause. In the case of subqueries though, we may be performing a
|
||||||
|
* select on a different table since many tables have a column with a FK dependency on RES_ID.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class QueryStack {
|
||||||
|
|
||||||
|
private final Stack<QueryRootEntry> myQueryRootStack = new Stack<>();
|
||||||
|
private final CriteriaBuilder myCriteriaBuilder;
|
||||||
|
private final SearchParameterMap mySearchParameterMap;
|
||||||
|
private final RequestPartitionId myRequestPartitionId;
|
||||||
|
private final String myResourceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public QueryStack(CriteriaBuilder theCriteriaBuilder, String theResourceType, SearchParameterMap theSearchParameterMap, RequestPartitionId theRequestPartitionId) {
|
||||||
|
assert theCriteriaBuilder != null;
|
||||||
|
assert isNotBlank(theResourceType);
|
||||||
|
assert theSearchParameterMap != null;
|
||||||
|
assert theRequestPartitionId != null;
|
||||||
|
myCriteriaBuilder = theCriteriaBuilder;
|
||||||
|
mySearchParameterMap = theSearchParameterMap;
|
||||||
|
myRequestPartitionId = theRequestPartitionId;
|
||||||
|
myResourceType = theResourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new <code>select RES_ID from HFJ_RESOURCE</code> to the stack. All predicates added to the {@literal QueryRootStack}
|
||||||
|
* will be added to this select clause until {@link #pop()} is called.
|
||||||
|
* <p>
|
||||||
|
* This method must only be called when the stack is empty.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void pushResourceTableQuery() {
|
||||||
|
assert myQueryRootStack.isEmpty();
|
||||||
|
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, false, mySearchParameterMap, myResourceType, myRequestPartitionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new <code>select count(RES_ID) from HFJ_RESOURCE</code> to the stack. All predicates added to the {@literal QueryRootStack}
|
||||||
|
* will be added to this select clause until {@link #pop()} is called.
|
||||||
|
* <p>
|
||||||
|
* This method must only be called when the stack is empty.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void pushResourceTableCountQuery() {
|
||||||
|
assert myQueryRootStack.isEmpty();
|
||||||
|
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, true, mySearchParameterMap, myResourceType, myRequestPartitionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new <code>select RES_ID from HFJ_RESOURCE</code> to the stack. All predicates added to the {@literal QueryRootStack}
|
||||||
|
* will be added to this select clause until {@link #pop()} is called.
|
||||||
|
* <p>
|
||||||
|
* This method must only be called when the stack is NOT empty.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void pushResourceTableSubQuery(String theResourceType) {
|
||||||
|
assert !myQueryRootStack.isEmpty();
|
||||||
|
myQueryRootStack.push(new QueryRootEntryResourceTable(myCriteriaBuilder, top(), mySearchParameterMap, theResourceType, myRequestPartitionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new <code>select RES_ID from (....)</code> to the stack, where the specific table being selected on will be
|
||||||
|
* determined based on the first call to {@link #createJoin(SearchBuilderJoinEnum, String)}. All predicates added
|
||||||
|
* to the {@literal QueryRootStack} will be added to this select clause until {@link #pop()} is called.
|
||||||
|
* <p>
|
||||||
|
* This method must only be called when the stack is NOT empty.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void pushIndexTableSubQuery() {
|
||||||
|
assert !myQueryRootStack.isEmpty();
|
||||||
|
myQueryRootStack.push(new QueryRootEntryIndexTable(myCriteriaBuilder, top()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must be called once all predicates have been added
|
||||||
|
*/
|
||||||
|
public AbstractQuery<Long> pop() {
|
||||||
|
QueryRootEntry element = myQueryRootStack.pop();
|
||||||
|
return element.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new SQL join from the current select statement to another table, using the resource PID as the
|
||||||
|
* joining key
|
||||||
|
*/
|
||||||
|
public <T> From<?, T> createJoin(SearchBuilderJoinEnum theType, String theSearchParameterName) {
|
||||||
|
return top().createJoin(theType, theSearchParameterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a join that was previously created by a call to {@link #createJoin(SearchBuilderJoinEnum, String)},
|
||||||
|
* if one exists for the given key.
|
||||||
|
*/
|
||||||
|
public Optional<Join<?, ?>> getExistingJoin(SearchBuilderJoinKey theKey) {
|
||||||
|
return top().getIndexJoin(theKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an attribute (aka a column) from the current select statement.
|
||||||
|
*
|
||||||
|
* @param theAttributeName Must be the name of a java field for the entity/table being selected on
|
||||||
|
*/
|
||||||
|
public <Y> Path<Y> get(String theAttributeName) {
|
||||||
|
return top().get(theAttributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a predicate to the current select statement
|
||||||
|
*/
|
||||||
|
public void addPredicate(Predicate thePredicate) {
|
||||||
|
top().addPredicate(thePredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a predicate and marks it as having implicit type selection in it. In other words, call this method if a
|
||||||
|
* this predicate will ensure:
|
||||||
|
* <ul>
|
||||||
|
* <li>Only Resource PIDs for the correct resource type will be selected</li>
|
||||||
|
* <li>Only Resource PIDs for non-deleted resources will be selected</li>
|
||||||
|
* </ul>
|
||||||
|
* Setting this flag is a performance optimization, since it avoids the need for us to explicitly
|
||||||
|
* add predicates for the two conditions above.
|
||||||
|
*/
|
||||||
|
public void addPredicateWithImplicitTypeSelection(Predicate thePredicate) {
|
||||||
|
setHasImplicitTypeSelection();
|
||||||
|
addPredicate(thePredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds predicates and marks them as having implicit type selection in it. In other words, call this method if a
|
||||||
|
* this predicate will ensure:
|
||||||
|
* <ul>
|
||||||
|
* <li>Only Resource PIDs for the correct resource type will be selected</li>
|
||||||
|
* <li>Only Resource PIDs for non-deleted resources will be selected</li>
|
||||||
|
* </ul>
|
||||||
|
* Setting this flag is a performance optimization, since it avoids the need for us to explicitly
|
||||||
|
* add predicates for the two conditions above.
|
||||||
|
*/
|
||||||
|
public void addPredicatesWithImplicitTypeSelection(List<Predicate> thePredicates) {
|
||||||
|
setHasImplicitTypeSelection();
|
||||||
|
addPredicates(thePredicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds predicate(s) to the current select statement
|
||||||
|
*/
|
||||||
|
public void addPredicates(List<Predicate> thePredicates) {
|
||||||
|
top().addPredicates(thePredicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all predicates from the current select statement
|
||||||
|
*/
|
||||||
|
public void clearPredicates() {
|
||||||
|
top().clearPredicates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all the current predicates
|
||||||
|
* <p>
|
||||||
|
* TODO This should really be package protected, but it is called externally in one spot - We need to clean that up
|
||||||
|
* at some point.
|
||||||
|
*/
|
||||||
|
public List<Predicate> getPredicates() {
|
||||||
|
return top().getPredicates();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setHasImplicitTypeSelection() {
|
||||||
|
top().setHasImplicitTypeSelection(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #setHasImplicitTypeSelection()
|
||||||
|
*/
|
||||||
|
public void clearHasImplicitTypeSelection() {
|
||||||
|
top().setHasImplicitTypeSelection(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return myQueryRootStack.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an SQL <code>order by</code> expression
|
||||||
|
*/
|
||||||
|
public void orderBy(List<Order> theOrders) {
|
||||||
|
top().orderBy(theOrders);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the column for the current table root that corresponds to the resource's lastUpdated time
|
||||||
|
*/
|
||||||
|
public Expression<Date> getLastUpdatedColumn() {
|
||||||
|
return top().getLastUpdatedColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the column for the current table root that corresponds to the resource's PID
|
||||||
|
*/
|
||||||
|
public Expression<Long> getResourcePidColumn() {
|
||||||
|
return top().getResourcePidColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Subquery<Long> subqueryForTagNegation() {
|
||||||
|
return top().subqueryForTagNegation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private QueryRootEntry top() {
|
||||||
|
Validate.isTrue(!myQueryRootStack.empty());
|
||||||
|
return myQueryRootStack.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO This class should avoid leaking the internal query root, but we need to do so for how composite search params are
|
||||||
|
* currently implemented. These only half work in the first place so I'm not going to worry about the fact that
|
||||||
|
* they rely on a leaky abstraction right now.. But when we get around to implementing composites properly,
|
||||||
|
* let's not continue this. JA 2020-05-12
|
||||||
|
*/
|
||||||
|
public Root<?> getRootForComposite() {
|
||||||
|
return top().getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a predicate that will never match any resources
|
||||||
|
*/
|
||||||
|
public Predicate addNeverMatchingPredicate() {
|
||||||
|
return top().addNeverMatchingPredicate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1225,7 +1225,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
ourLog.info("Created patient, got it: {}", id);
|
ourLog.info("Created patient, got it: {}", id);
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", mySrd);
|
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", mySrd);
|
||||||
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||||
assertGone(id);
|
assertGone(id);
|
||||||
|
|
||||||
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", mySrd);
|
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", mySrd);
|
||||||
|
|
|
@ -434,8 +434,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest {
|
||||||
|
|
||||||
IPreResourceAccessDetails accessDetails = theArgs.get(IPreResourceAccessDetails.class);
|
IPreResourceAccessDetails accessDetails = theArgs.get(IPreResourceAccessDetails.class);
|
||||||
|
|
||||||
// FIXME: restore
|
assertThat(accessDetails.size(), greaterThan(0));
|
||||||
// assertThat(accessDetails.size(), greaterThan(0));
|
|
||||||
|
|
||||||
List<String> currentPassIds = new ArrayList<>();
|
List<String> currentPassIds = new ArrayList<>();
|
||||||
for (int i = 0; i < accessDetails.size(); i++) {
|
for (int i = 0; i < accessDetails.size(); i++) {
|
||||||
|
|
|
@ -21,9 +21,11 @@ import org.junit.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.contains;
|
||||||
|
|
||||||
public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4QueryCountTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4QueryCountTest.class);
|
||||||
|
@ -459,6 +461,35 @@ public class FhirResourceDaoR4QueryCountTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchOnChainedToken() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("P");
|
||||||
|
patient.addIdentifier().setSystem("sys").setValue("val");
|
||||||
|
myPatientDao.update(patient);
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.setId("O");
|
||||||
|
obs.getSubject().setReference("Patient/P");
|
||||||
|
myObservationDao.update(obs);
|
||||||
|
|
||||||
|
SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_SUBJECT, new ReferenceParam("identifier", "sys|val"));
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
IBundleProvider outcome = myObservationDao.search(map);
|
||||||
|
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder("Observation/O"));
|
||||||
|
|
||||||
|
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||||
|
assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||||
|
assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
||||||
|
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
|
||||||
|
|
||||||
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||||
|
assertEquals(1, StringUtils.countMatches(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toLowerCase(), "join"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransactionWithMultipleReferences() {
|
public void testTransactionWithMultipleReferences() {
|
||||||
Bundle input = new Bundle();
|
Bundle input = new Bundle();
|
||||||
|
|
|
@ -405,24 +405,30 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
IBundleProvider results;
|
IBundleProvider results;
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "foo|bar").setChain("identifier"));
|
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "foo|bar").setChain("identifier"));
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
results = myEncounterDao.search(map);
|
results = myEncounterDao.search(map);
|
||||||
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||||
ids = toUnqualifiedVersionlessIdValues(results);
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids, hasItems(enc1Id, enc2Id));
|
assertThat(ids, hasItems(enc1Id, enc2Id));
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Patient", "foo|bar").setChain("identifier"));
|
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Patient", "foo|bar").setChain("identifier"));
|
||||||
results = myEncounterDao.search(map);
|
results = myEncounterDao.search(map);
|
||||||
ids = toUnqualifiedVersionlessIdValues(results);
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids, hasItems(enc1Id));
|
assertThat(ids, hasItems(enc1Id));
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Group", "foo|bar").setChain("identifier"));
|
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject:Group", "foo|bar").setChain("identifier"));
|
||||||
results = myEncounterDao.search(map);
|
results = myEncounterDao.search(map);
|
||||||
ids = toUnqualifiedVersionlessIdValues(results);
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
assertThat(ids, hasItems(enc2Id));
|
assertThat(ids, hasItems(enc2Id));
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "04823543").setChain("identifier"));
|
map.add(Encounter.SP_SUBJECT, new ReferenceParam("subject", "04823543").setChain("identifier"));
|
||||||
results = myEncounterDao.search(map);
|
results = myEncounterDao.search(map);
|
||||||
ids = toUnqualifiedVersionlessIdValues(results);
|
ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
@ -3021,7 +3027,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
String searchQuery = queries.get(0);
|
String searchQuery = queries.get(0);
|
||||||
assertEquals(searchQuery, 3, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"));
|
assertEquals(searchQuery, 3, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN"));
|
||||||
assertEquals(searchQuery, 5, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"));
|
assertEquals(searchQuery, 4, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -692,7 +692,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
|
||||||
myCaptureQueriesListener.logSelectQueries();
|
myCaptureQueriesListener.logSelectQueries();
|
||||||
|
|
||||||
String selectQuery = myCaptureQueriesListener.getSelectQueries().get(1).getSql(true, true);
|
String selectQuery = myCaptureQueriesListener.getSelectQueries().get(1).getSql(true, true);
|
||||||
assertThat(selectQuery, containsString("HASH_VALUE in"));
|
assertThat(selectQuery, containsString("HASH_VALUE="));
|
||||||
assertThat(selectQuery, not(containsString("HASH_SYS")));
|
assertThat(selectQuery, not(containsString("HASH_SYS")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,20 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.hamcrest.Matchers.contains;
|
||||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
@ -320,6 +324,46 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test {
|
||||||
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortWithChainedSearch() {
|
||||||
|
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
|
||||||
|
|
||||||
|
Patient pCA = new Patient();
|
||||||
|
pCA.setId("CA");
|
||||||
|
pCA.addIdentifier().setSystem("PCA").setValue("PCA");
|
||||||
|
myPatientDao.update(pCA);
|
||||||
|
|
||||||
|
Observation obs1 = new Observation();
|
||||||
|
obs1.setId("OBS1");
|
||||||
|
obs1.getSubject().setReference("Patient/CA");
|
||||||
|
obs1.setEffective(new DateTimeType("2000-01-01"));
|
||||||
|
myObservationDao.update(obs1);
|
||||||
|
|
||||||
|
Observation obs2 = new Observation();
|
||||||
|
obs2.setId("OBS2");
|
||||||
|
obs2.getSubject().setReference("Patient/CA");
|
||||||
|
obs2.setEffective(new DateTimeType("2000-02-02"));
|
||||||
|
myObservationDao.update(obs2);
|
||||||
|
|
||||||
|
SearchParameterMap map;
|
||||||
|
List<String> ids;
|
||||||
|
|
||||||
|
runInTransaction(()->{
|
||||||
|
ourLog.info("Dates:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
||||||
|
});
|
||||||
|
|
||||||
|
map = new SearchParameterMap();
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
map.add(Observation.SP_SUBJECT, new ReferenceParam("Patient", "identifier", "PCA|PCA"));
|
||||||
|
map.setSort(new SortSpec("date").setOrder(SortOrderEnum.DESC));
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
|
||||||
|
ourLog.info("IDS: {}", ids);
|
||||||
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||||
|
assertThat(ids.toString(), ids, contains("Observation/OBS2", "Observation/OBS1"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClassClearContext() {
|
public static void afterClassClearContext() {
|
||||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||||
|
|
|
@ -398,7 +398,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||||
assertThat(unformattedSql, stringContainsInOrder(
|
assertThat(unformattedSql, stringContainsInOrder(
|
||||||
"IDX_STRING='Patient?identifier=urn%7C111'",
|
"IDX_STRING='Patient?identifier=urn%7C111'",
|
||||||
"HASH_SYS_AND_VALUE in ('-3122824860083758210')"
|
"HASH_SYS_AND_VALUE='-3122824860083758210'"
|
||||||
));
|
));
|
||||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||||
|
@ -535,7 +535,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||||
assertThat(unformattedSql, stringContainsInOrder(
|
assertThat(unformattedSql, stringContainsInOrder(
|
||||||
"IDX_STRING='ServiceRequest?identifier=sys%7C111&patient=Patient%2F" + ptId.getIdPart() + "&performer=Practitioner%2F" + practId.getIdPart() + "'",
|
"IDX_STRING='ServiceRequest?identifier=sys%7C111&patient=Patient%2F" + ptId.getIdPart() + "&performer=Practitioner%2F" + practId.getIdPart() + "'",
|
||||||
"HASH_SYS_AND_VALUE in ('6795110643554413877')"
|
"HASH_SYS_AND_VALUE='6795110643554413877'"
|
||||||
));
|
));
|
||||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class SearchWithInterceptorR4Test extends BaseJpaR4Test {
|
||||||
String query = list.get(0).getSql(true, false);
|
String query = list.get(0).getSql(true, false);
|
||||||
ourLog.info("Query: {}", query);
|
ourLog.info("Query: {}", query);
|
||||||
|
|
||||||
assertThat(query, containsString("HASH_SYS_AND_VALUE in ('3788488238034018567')"));
|
assertThat(query, containsString("HASH_SYS_AND_VALUE='3788488238034018567'"));
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
myInterceptorRegistry.unregisterInterceptor(interceptor);
|
myInterceptorRegistry.unregisterInterceptor(interceptor);
|
||||||
|
|
|
@ -60,9 +60,11 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
init410(); // 20190815 - 20191014
|
init410(); // 20190815 - 20191014
|
||||||
init420(); // 20191015 - 20200217
|
init420(); // 20191015 - 20200217
|
||||||
init430(); // Replaced by 5.0.0
|
init430(); // Replaced by 5.0.0
|
||||||
init500(); // 20200218 - present
|
init500(); // 20200218 - 20200513
|
||||||
|
init501(); // 20200514 - present
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Partway through the 4.3.0 releaase cycle we renumbered to
|
* Partway through the 4.3.0 releaase cycle we renumbered to
|
||||||
* 5.0.0 - We have a bunch of NOP tasks here to avoid breakage for anyone
|
* 5.0.0 - We have a bunch of NOP tasks here to avoid breakage for anyone
|
||||||
|
@ -122,7 +124,17 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
version.addNop("20200420.42");
|
version.addNop("20200420.42");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init500() { // 20200218 - present
|
|
||||||
|
private void init501() { //20200514 - present
|
||||||
|
Builder version = forVersion(VersionEnum.V5_0_1);
|
||||||
|
|
||||||
|
Builder.BuilderWithTableName spidxDate = version.onTable("HFJ_SPIDX_DATE");
|
||||||
|
spidxDate.addIndex("20200514.1", "IDX_SP_DATE_HASH_LOW").unique(false).withColumns("HASH_IDENTITY", "SP_VALUE_LOW");
|
||||||
|
spidxDate.addIndex("20200514.2", "IDX_SP_DATE_ORD_HASH").unique(false).withColumns("HASH_IDENTITY", "SP_VALUE_LOW_DATE_ORDINAL", "SP_VALUE_HIGH_DATE_ORDINAL");
|
||||||
|
spidxDate.addIndex("20200514.3", "IDX_SP_DATE_ORD_HASH_LOW").unique(false).withColumns("HASH_IDENTITY", "SP_VALUE_LOW_DATE_ORDINAL");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init500() { // 20200218 - 20200519
|
||||||
Builder version = forVersion(VersionEnum.V5_0_0);
|
Builder version = forVersion(VersionEnum.V5_0_0);
|
||||||
|
|
||||||
// Eliminate circular dependency.
|
// Eliminate circular dependency.
|
||||||
|
|
|
@ -65,7 +65,12 @@ public class BaseMigrationTasks<T extends Enum> {
|
||||||
theTask.validate();
|
theTask.validate();
|
||||||
myTasks.put(theRelease, theTask);
|
myTasks.put(theRelease, theTask);
|
||||||
};
|
};
|
||||||
return new Builder(theRelease.name(), sink);
|
return new Builder(toReleaseName(theRelease), sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected String toReleaseName(T theRelease) {
|
||||||
|
return theRelease.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BaseTask> getAllTasks(T[] theVersionEnumValues) {
|
public List<BaseTask> getAllTasks(T[] theVersionEnumValues) {
|
||||||
|
|
|
@ -54,8 +54,11 @@ import java.util.Date;
|
||||||
@Table(name = "HFJ_SPIDX_DATE", indexes = {
|
@Table(name = "HFJ_SPIDX_DATE", indexes = {
|
||||||
// We previously had an index called IDX_SP_DATE - Dont reuse
|
// We previously had an index called IDX_SP_DATE - Dont reuse
|
||||||
@Index(name = "IDX_SP_DATE_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW,SP_VALUE_HIGH"),
|
@Index(name = "IDX_SP_DATE_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW,SP_VALUE_HIGH"),
|
||||||
|
@Index(name = "IDX_SP_DATE_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW"),
|
||||||
|
@Index(name = "IDX_SP_DATE_ORD_HASH", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL,SP_VALUE_HIGH_DATE_ORDINAL"),
|
||||||
|
@Index(name = "IDX_SP_DATE_ORD_HASH_LOW", columnList = "HASH_IDENTITY,SP_VALUE_LOW_DATE_ORDINAL"),
|
||||||
|
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID"),
|
||||||
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_DATE_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_DATE_RESID", columnList = "RES_ID")
|
|
||||||
})
|
})
|
||||||
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
|
|
|
@ -677,4 +677,18 @@ public class SearchParameterMap implements Serializable {
|
||||||
public int size() {
|
public int size() {
|
||||||
return mySearchParameterMap.size();
|
return mySearchParameterMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SearchParameterMap newSynchronous() {
|
||||||
|
SearchParameterMap retVal = new SearchParameterMap();
|
||||||
|
retVal.setLoadSynchronous(true);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SearchParameterMap newSynchronous(String theName, IQueryParameterType theParam) {
|
||||||
|
SearchParameterMap retVal = new SearchParameterMap();
|
||||||
|
retVal.setLoadSynchronous(true);
|
||||||
|
retVal.add(theName, theParam);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,10 +107,12 @@ public interface ITestDataBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
default <T extends IBaseResource> Consumer<T> withId(String theId) {
|
default <T extends IBaseResource> Consumer<T> withId(String theId) {
|
||||||
return t -> {
|
assertThat(theId, matchesPattern("[a-zA-Z0-9-]+"));
|
||||||
assertThat(theId, matchesPattern("[a-zA-Z0-9]+"));
|
return t -> t.setId(theId);
|
||||||
t.setId(theId);
|
}
|
||||||
};
|
|
||||||
|
default <T extends IBaseResource> Consumer<T> withId(IIdType theId) {
|
||||||
|
return withId(theId.getIdPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
default Consumer<IBaseResource> withTag(String theSystem, String theCode) {
|
default Consumer<IBaseResource> withTag(String theSystem, String theCode) {
|
||||||
|
|
Loading…
Reference in New Issue