Clean up unique composite search params
This commit is contained in:
parent
8d49b4e6d2
commit
d1667487c2
|
@ -29,8 +29,8 @@ import ca.uhn.fhir.rest.api.QualifiedParamList;
|
|||
|
||||
public interface IQueryParameterOr<T extends IQueryParameterType> extends Serializable {
|
||||
|
||||
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters);
|
||||
void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters);
|
||||
|
||||
public List<T> getValuesAsQueryTokens();
|
||||
List<T> getValuesAsQueryTokens();
|
||||
|
||||
}
|
||||
|
|
|
@ -23,30 +23,27 @@ package ca.uhn.fhir.rest.param;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.QualifiedParamList;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private final DateParamDateTimeHolder myValue = new DateParamDateTimeHolder();
|
||||
|
||||
/**
|
||||
|
@ -119,9 +116,7 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
|||
b.append(ParameterUtil.escapeWithDefault(getPrefix().getValue()));
|
||||
}
|
||||
|
||||
if (myValue != null) {
|
||||
b.append(ParameterUtil.escapeWithDefault(myValue.getValueAsString()));
|
||||
}
|
||||
b.append(ParameterUtil.escapeWithDefault(myValue.getValueAsString()));
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
@ -132,38 +127,15 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
|||
}
|
||||
|
||||
public TemporalPrecisionEnum getPrecision() {
|
||||
if (myValue != null) {
|
||||
return myValue.getPrecision();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getValue() {
|
||||
if (myValue != null) {
|
||||
return myValue.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public DateTimeDt getValueAsDateTimeDt() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
}
|
||||
return new DateTimeDt(myValue.getValue());
|
||||
}
|
||||
|
||||
public InstantDt getValueAsInstantDt() {
|
||||
if (myValue == null) {
|
||||
return null;
|
||||
}
|
||||
return new InstantDt(myValue.getValue());
|
||||
}
|
||||
|
||||
public String getValueAsString() {
|
||||
if (myValue != null) {
|
||||
return myValue.getValueAsString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -260,7 +232,7 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
|||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DateParamDateTimeHolder() {
|
||||
DateParamDateTimeHolder() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
|
|
@ -36,9 +36,17 @@ public class TokenAndListParam extends BaseAndListParam<TokenOrListParam> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TokenAndListParam addAnd(TokenParam theValue) {
|
||||
/**
|
||||
* @param theValue The OR values
|
||||
* @return Returns a reference to this for convenient chaining
|
||||
*/
|
||||
public TokenAndListParam addAnd(TokenParam... theValue) {
|
||||
Validate.notNull(theValue, "theValue must not be null");
|
||||
addValue(new TokenOrListParam().add(theValue));
|
||||
TokenOrListParam orListParam = new TokenOrListParam();
|
||||
for (TokenParam next : theValue) {
|
||||
orListParam.add(next);
|
||||
}
|
||||
addValue(orListParam);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1071,7 +1071,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
||||
|
||||
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
|
||||
for (List<List<? extends IQueryParameterType>> nextAnds : theParams.values()) {
|
||||
for (List<List<IQueryParameterType>> nextAnds : theParams.values()) {
|
||||
for (List<? extends IQueryParameterType> nextOrs : nextAnds) {
|
||||
for (IQueryParameterType next : nextOrs) {
|
||||
if (next.getMissing() != null) {
|
||||
|
|
|
@ -81,7 +81,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
|||
super();
|
||||
}
|
||||
|
||||
private void addTextSearch(QueryBuilder theQueryBuilder, BooleanJunction<?> theBoolean, List<List<? extends IQueryParameterType>> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameNGram) {
|
||||
private void addTextSearch(QueryBuilder theQueryBuilder, BooleanJunction<?> theBoolean, List<List<IQueryParameterType>> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameNGram) {
|
||||
if (theTerms == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -171,13 +171,13 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
|||
/*
|
||||
* Handle _content parameter (resource body content)
|
||||
*/
|
||||
List<List<? extends IQueryParameterType>> contentAndTerms = theParams.remove(Constants.PARAM_CONTENT);
|
||||
List<List<IQueryParameterType>> contentAndTerms = theParams.remove(Constants.PARAM_CONTENT);
|
||||
addTextSearch(qb, bool, contentAndTerms, "myContentText", "myContentTextEdgeNGram", "myContentTextNGram");
|
||||
|
||||
/*
|
||||
* Handle _text parameter (resource narrative content)
|
||||
*/
|
||||
List<List<? extends IQueryParameterType>> textAndTerms = theParams.remove(Constants.PARAM_TEXT);
|
||||
List<List<IQueryParameterType>> textAndTerms = theParams.remove(Constants.PARAM_TEXT);
|
||||
addTextSearch(qb, bool, textAndTerms, "myNarrativeText", "myNarrativeTextEdgeNGram", "myNarrativeTextNGram");
|
||||
|
||||
if (theReferencingPid != null) {
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
|
|||
* 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.
|
||||
|
@ -37,7 +37,6 @@ import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
|||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
||||
|
@ -91,7 +90,6 @@ import java.math.BigDecimal;
|
|||
import java.math.MathContext;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
@ -158,6 +156,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private int myFetchSize;
|
||||
private Integer myMaxResultsToFetch;
|
||||
private Set<Long> myPidSet;
|
||||
private boolean myHaveIndexJoins = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -213,7 +212,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
private void addPredicateHas(List<List<? extends IQueryParameterType>> theHasParameters) {
|
||||
private void addPredicateHas(List<List<IQueryParameterType>> theHasParameters) {
|
||||
|
||||
for (List<? extends IQueryParameterType> nextOrList : theHasParameters) {
|
||||
|
||||
|
@ -274,7 +273,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList) {
|
||||
private void addPredicateLanguage(List<List<IQueryParameterType>> theList) {
|
||||
for (List<? extends IQueryParameterType> nextList : theList) {
|
||||
|
||||
Set<String> values = new HashSet<>();
|
||||
|
@ -286,7 +285,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
values.add(nextValue);
|
||||
} else {
|
||||
throw new InternalErrorException("Lanugage parameter must be of type " + StringParam.class.getCanonicalName() + " - Got " + next.getClass().getCanonicalName());
|
||||
throw new InternalErrorException("Language parameter must be of type " + StringParam.class.getCanonicalName() + " - Got " + next.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,7 +585,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
|
||||
subQ.select(subQfrom.get("myId").as(Long.class));
|
||||
|
||||
List<List<? extends IQueryParameterType>> andOrParams = new ArrayList<>();
|
||||
List<List<IQueryParameterType>> andOrParams = new ArrayList<>();
|
||||
andOrParams.add(theOrValues);
|
||||
|
||||
/*
|
||||
|
@ -641,7 +640,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return chainValue;
|
||||
}
|
||||
|
||||
private void addPredicateResourceId(List<List<? extends IQueryParameterType>> theValues) {
|
||||
private void addPredicateResourceId(List<List<IQueryParameterType>> theValues) {
|
||||
for (List<? extends IQueryParameterType> nextValue : theValues) {
|
||||
Set<Long> orPids = new HashSet<>();
|
||||
for (IQueryParameterType next : nextValue) {
|
||||
|
@ -701,7 +700,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
private void addPredicateTag(List<List<? extends IQueryParameterType>> theList, String theParamName) {
|
||||
private void addPredicateTag(List<List<IQueryParameterType>> theList, String theParamName) {
|
||||
TagTypeEnum tagType;
|
||||
if (Constants.PARAM_TAG.equals(theParamName)) {
|
||||
tagType = TagTypeEnum.TAG;
|
||||
|
@ -1013,13 +1012,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Join<ResourceTable, T> createOrReuseJoin(JoinEnum theType, String theSearchParameterName) {
|
||||
JoinKey key = new JoinKey(theSearchParameterName, theType);
|
||||
return (Join<ResourceTable, T>) myIndexJoins.computeIfAbsent(key, k -> createJoin(theType, theSearchParameterName));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Join<ResourceTable, T> createJoin(JoinEnum theType, String theSearchParameterName) {
|
||||
private <T> Join<ResourceTable, T> createJoin(JoinEnum theType, String theSearchParameterName) {
|
||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
||||
switch (theType) {
|
||||
case DATE:
|
||||
|
@ -1044,6 +1037,11 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||
break;
|
||||
}
|
||||
|
||||
JoinKey key = new JoinKey(theSearchParameterName, theType);
|
||||
myIndexJoins.put(key, join);
|
||||
myHaveIndexJoins = true;
|
||||
|
||||
return (Join<ResourceTable, T>) join;
|
||||
}
|
||||
|
||||
|
@ -1531,51 +1529,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myBuilder = myEntityManager.getCriteriaBuilder();
|
||||
mySearchUuid = theSearchUuid;
|
||||
|
||||
/*
|
||||
* Check if there is a unique key associated with the set
|
||||
* of parameters passed in
|
||||
*/
|
||||
ourLog.debug("Checking for unique index for query: {}", theParams.toNormalizedQueryString(myContext));
|
||||
if (myDaoConfig.isUniqueIndexesEnabled()) {
|
||||
if (myParams.getIncludes().isEmpty()) {
|
||||
if (myParams.getRevIncludes().isEmpty()) {
|
||||
if (myParams.getEverythingMode() == null) {
|
||||
if (myParams.isAllParametersHaveNoModifier()) {
|
||||
Set<String> paramNames = theParams.keySet();
|
||||
if (paramNames.isEmpty() == false) {
|
||||
List<JpaRuntimeSearchParam> searchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, paramNames);
|
||||
if (searchParams.size() > 0) {
|
||||
List<List<String>> params = new ArrayList<>();
|
||||
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamNameToValues : theParams.entrySet()) {
|
||||
String nextParamName = nextParamNameToValues.getKey();
|
||||
nextParamName = UrlUtil.escapeUrlParam(nextParamName);
|
||||
for (List<? extends IQueryParameterType> nextAnd : nextParamNameToValues.getValue()) {
|
||||
ArrayList<String> nextValueList = new ArrayList<>();
|
||||
params.add(nextValueList);
|
||||
for (IQueryParameterType nextOr : nextAnd) {
|
||||
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
|
||||
nextOrValue = UrlUtil.escapeUrlParam(nextOrValue);
|
||||
nextValueList.add(nextParamName + "=" + nextOrValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> uniqueQueryStrings = ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(myResourceName, params);
|
||||
if (ourTrackHandlersForUnitTest) {
|
||||
ourLastHandlerParamsForUnitTest = theParams;
|
||||
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.UNIQUE_INDEX;
|
||||
ourLastHandlerThreadForUnitTest = Thread.currentThread().getName();
|
||||
}
|
||||
return new UniqueIndexIterator(uniqueQueryStrings);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ourTrackHandlersForUnitTest) {
|
||||
ourLastHandlerParamsForUnitTest = theParams;
|
||||
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.STANDARD_QUERY;
|
||||
|
@ -1602,31 +1555,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (sort != null) {
|
||||
assert !theCount;
|
||||
|
||||
// outerQuery = myBuilder.createQuery(Long.class);
|
||||
// Root<ResourceTable> outerQueryFrom = outerQuery.from(ResourceTable.class);
|
||||
//
|
||||
// List<Order> orders = Lists.newArrayList();
|
||||
// List<Predicate> predicates = Lists.newArrayList();
|
||||
//
|
||||
// createSort(myBuilder, outerQueryFrom, sort, orders, predicates);
|
||||
// if (orders.size() > 0) {
|
||||
// outerQuery.orderBy(orders);
|
||||
// }
|
||||
//
|
||||
// Subquery<Long> subQ = outerQuery.subquery(Long.class);
|
||||
// Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
|
||||
//
|
||||
// myResourceTableQuery = subQ;
|
||||
// myResourceTableRoot = subQfrom;
|
||||
//
|
||||
// Expression<Long> selectExpr = subQfrom.get("myId").as(Long.class);
|
||||
// subQ.select(selectExpr);
|
||||
//
|
||||
// predicates.add(0, myBuilder.in(outerQueryFrom.get("myId").as(Long.class)).value(subQ));
|
||||
//
|
||||
// outerQuery.multiselect(outerQueryFrom.get("myId").as(Long.class));
|
||||
// outerQuery.where(predicates.toArray(new Predicate[0]));
|
||||
|
||||
outerQuery = myBuilder.createQuery(Long.class);
|
||||
myResourceTableQuery = outerQuery;
|
||||
myResourceTableRoot = myResourceTableQuery.from(ResourceTable.class);
|
||||
|
@ -1644,7 +1572,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
outerQuery.orderBy(orders);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
outerQuery = myBuilder.createQuery(Long.class);
|
||||
|
@ -1713,7 +1640,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* 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 (myIndexJoins.isEmpty()) {
|
||||
if (!myHaveIndexJoins) {
|
||||
if (myParams.getEverythingMode() == null) {
|
||||
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
|
||||
}
|
||||
|
@ -2186,17 +2113,111 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private void searchForIdsWithAndOr(@Nonnull SearchParameterMap theParams) {
|
||||
myParams = theParams;
|
||||
|
||||
// Remove any empty parameters
|
||||
theParams.clean();
|
||||
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : myParams.entrySet()) {
|
||||
String nextParamName = nextParamEntry.getKey();
|
||||
List<List<? extends IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
searchForIdsWithAndOr(myResourceName, nextParamName, andOrParams);
|
||||
|
||||
/*
|
||||
* Check if there is a unique key associated with the set
|
||||
* of parameters passed in
|
||||
*/
|
||||
boolean couldBeEligibleForCompositeUniqueSpProcessing =
|
||||
myDaoConfig.isUniqueIndexesEnabled() &&
|
||||
myParams.getEverythingMode() == null &&
|
||||
myParams.isAllParametersHaveNoModifier();
|
||||
if (couldBeEligibleForCompositeUniqueSpProcessing) {
|
||||
|
||||
// Since we're going to remove elements below
|
||||
theParams.values().forEach(nextAndList -> ensureSubListsAreWritable(nextAndList));
|
||||
|
||||
List<JpaRuntimeSearchParam> activeUniqueSearchParams = mySearchParamRegistry.getActiveUniqueSearchParams(myResourceName, theParams.keySet());
|
||||
if (activeUniqueSearchParams.size() > 0) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(myResourceName);
|
||||
sb.append("?");
|
||||
|
||||
boolean first = true;
|
||||
|
||||
ArrayList<String> keys = new ArrayList<>(theParams.keySet());
|
||||
Collections.sort(keys);
|
||||
for (String nextParamName : keys) {
|
||||
List<List<IQueryParameterType>> nextValues = theParams.get(nextParamName);
|
||||
|
||||
nextParamName = UrlUtil.escapeUrlParam(nextParamName);
|
||||
if (nextValues.get(0).size() != 1) {
|
||||
sb = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Reference params are only eligible for using a composite index if they
|
||||
// are qualified
|
||||
RuntimeSearchParam nextParamDef = mySearchParamRegistry.getActiveSearchParam(myResourceName, nextParamName);
|
||||
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
||||
ReferenceParam param = (ReferenceParam) nextValues.get(0).get(0);
|
||||
if (isBlank(param.getResourceType())) {
|
||||
sb = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<? extends IQueryParameterType> nextAnd = nextValues.remove(0);
|
||||
IQueryParameterType nextOr = nextAnd.remove(0);
|
||||
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
|
||||
nextOrValue = UrlUtil.escapeUrlParam(nextOrValue);
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append('&');
|
||||
}
|
||||
|
||||
sb.append(nextParamName).append('=').append(nextOrValue);
|
||||
|
||||
}
|
||||
|
||||
if (sb != null) {
|
||||
String indexString = sb.toString();
|
||||
ourLog.debug("Checking for unique index for query: {}", indexString);
|
||||
if (ourTrackHandlersForUnitTest) {
|
||||
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.UNIQUE_INDEX;
|
||||
}
|
||||
addPredicateCompositeStringUnique(theParams, indexString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle each parameter
|
||||
for (Entry<String, List<List<IQueryParameterType>>> nextParamEntry : myParams.entrySet()) {
|
||||
String nextParamName = nextParamEntry.getKey();
|
||||
List<List<IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
searchForIdsWithAndOr(myResourceName, nextParamName, andOrParams);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void searchForIdsWithAndOr(String theResourceName, String theParamName, List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
|
||||
private <T> void ensureSubListsAreWritable(List<List<T>> theListOfLists) {
|
||||
for (int i = 0; i < theListOfLists.size(); i++) {
|
||||
List<T> oldSubList = theListOfLists.get(i);
|
||||
if (!(oldSubList instanceof ArrayList)) {
|
||||
List<T> newSubList = new ArrayList<>(oldSubList);
|
||||
theListOfLists.set(i, newSubList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPredicateCompositeStringUnique(@Nonnull SearchParameterMap theParams, String theIndexdString) {
|
||||
myHaveIndexJoins = true;
|
||||
|
||||
Join<ResourceTable, ResourceIndexedCompositeStringUnique> join = myResourceTableRoot.join("myParamsCompositeStringUnique", JoinType.LEFT);
|
||||
Predicate predicate = myBuilder.equal(join.get("myIndexString"), theIndexdString);
|
||||
myPredicates.add(predicate);
|
||||
|
||||
// Remove any empty parameters remaining after this
|
||||
theParams.clean();
|
||||
}
|
||||
|
||||
private void searchForIdsWithAndOr(String theResourceName, String theParamName, List<List<IQueryParameterType>> theAndOrParams) {
|
||||
|
||||
if (theAndOrParams.isEmpty()) {
|
||||
return;
|
||||
|
@ -2566,47 +2587,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
private class UniqueIndexIterator implements IResultIterator {
|
||||
private final Set<String> myUniqueQueryStrings;
|
||||
private Iterator<Long> myWrap = null;
|
||||
|
||||
UniqueIndexIterator(Set<String> theUniqueQueryStrings) {
|
||||
myUniqueQueryStrings = theUniqueQueryStrings;
|
||||
}
|
||||
|
||||
private void ensureHaveQuery() {
|
||||
if (myWrap == null) {
|
||||
ourLog.debug("Searching for unique index matches over {} candidate query strings", myUniqueQueryStrings.size());
|
||||
StopWatch sw = new StopWatch();
|
||||
Collection<Long> resourcePids = myResourceIndexedCompositeStringUniqueDao.findResourcePidsByQueryStrings(myUniqueQueryStrings);
|
||||
ourLog.debug("Found {} unique index matches in {}ms", resourcePids.size(), sw.getMillis());
|
||||
myWrap = resourcePids.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
ensureHaveQuery();
|
||||
return myWrap.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long next() {
|
||||
ensureHaveQuery();
|
||||
return myWrap.next();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkippedCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CountQueryIterator implements Iterator<Long> {
|
||||
private final TypedQuery<Long> myQuery;
|
||||
|
|
|
@ -23,7 +23,6 @@ package ca.uhn.fhir.jpa.dao.index;
|
|||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.*;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
@ -44,17 +43,17 @@ public class DaoSearchParamSynchronizer {
|
|||
|
||||
public void synchronizeSearchParamsToDatabase(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
|
||||
|
||||
synchronize(theParams, theEntity, theParams.stringParams, existingParams.stringParams);
|
||||
synchronize(theParams, theEntity, theParams.tokenParams, existingParams.tokenParams);
|
||||
synchronize(theParams, theEntity, theParams.numberParams, existingParams.numberParams);
|
||||
synchronize(theParams, theEntity, theParams.quantityParams, existingParams.quantityParams);
|
||||
synchronize(theParams, theEntity, theParams.dateParams, existingParams.dateParams);
|
||||
synchronize(theParams, theEntity, theParams.uriParams, existingParams.uriParams);
|
||||
synchronize(theParams, theEntity, theParams.coordsParams, existingParams.coordsParams);
|
||||
synchronize(theParams, theEntity, theParams.links, existingParams.links);
|
||||
synchronize(theParams, theEntity, theParams.myStringParams, existingParams.myStringParams);
|
||||
synchronize(theParams, theEntity, theParams.myTokenParams, existingParams.myTokenParams);
|
||||
synchronize(theParams, theEntity, theParams.myNumberParams, existingParams.myNumberParams);
|
||||
synchronize(theParams, theEntity, theParams.myQuantityParams, existingParams.myQuantityParams);
|
||||
synchronize(theParams, theEntity, theParams.myDateParams, existingParams.myDateParams);
|
||||
synchronize(theParams, theEntity, theParams.myUriParams, existingParams.myUriParams);
|
||||
synchronize(theParams, theEntity, theParams.myCoordsParams, existingParams.myCoordsParams);
|
||||
synchronize(theParams, theEntity, theParams.myLinks, existingParams.myLinks);
|
||||
|
||||
// make sure links are indexed
|
||||
theEntity.setResourceLinks(theParams.links);
|
||||
theEntity.setResourceLinks(theParams.myLinks);
|
||||
}
|
||||
|
||||
private <T extends BaseResourceIndex> void synchronize(ResourceIndexedSearchParams theParams, ResourceTable theEntity, Collection<T> theNewParms, Collection<T> theExistingParms) {
|
||||
|
|
|
@ -106,9 +106,9 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
*/
|
||||
for (Iterator<ResourceLink> existingLinkIter = existingParams.getResourceLinks().iterator(); existingLinkIter.hasNext(); ) {
|
||||
ResourceLink nextExisting = existingLinkIter.next();
|
||||
if (theParams.links.remove(nextExisting)) {
|
||||
if (theParams.myLinks.remove(nextExisting)) {
|
||||
existingLinkIter.remove();
|
||||
theParams.links.add(nextExisting);
|
||||
theParams.myLinks.add(nextExisting);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,28 +133,28 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
Collection<String> linksForCompositePartWantPaths = null;
|
||||
switch (nextCompositeOf.getParamType()) {
|
||||
case NUMBER:
|
||||
paramsListForCompositePart = theParams.numberParams;
|
||||
paramsListForCompositePart = theParams.myNumberParams;
|
||||
break;
|
||||
case DATE:
|
||||
paramsListForCompositePart = theParams.dateParams;
|
||||
paramsListForCompositePart = theParams.myDateParams;
|
||||
break;
|
||||
case STRING:
|
||||
paramsListForCompositePart = theParams.stringParams;
|
||||
paramsListForCompositePart = theParams.myStringParams;
|
||||
break;
|
||||
case TOKEN:
|
||||
paramsListForCompositePart = theParams.tokenParams;
|
||||
paramsListForCompositePart = theParams.myTokenParams;
|
||||
break;
|
||||
case REFERENCE:
|
||||
linksForCompositePart = theParams.links;
|
||||
linksForCompositePartWantPaths = new HashSet<>();
|
||||
linksForCompositePartWantPaths.addAll(nextCompositeOf.getPathsSplit());
|
||||
linksForCompositePart = theParams.myLinks;
|
||||
linksForCompositePartWantPaths = new HashSet<>(nextCompositeOf.getPathsSplit());
|
||||
break;
|
||||
case QUANTITY:
|
||||
paramsListForCompositePart = theParams.quantityParams;
|
||||
paramsListForCompositePart = theParams.myQuantityParams;
|
||||
break;
|
||||
case URI:
|
||||
paramsListForCompositePart = theParams.uriParams;
|
||||
paramsListForCompositePart = theParams.myUriParams;
|
||||
break;
|
||||
case SPECIAL:
|
||||
case COMPOSITE:
|
||||
case HAS:
|
||||
break;
|
||||
|
@ -189,11 +189,13 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
Set<String> queryStringsToPopulate = theParams.extractCompositeStringUniquesValueChains(resourceType, partsChoices);
|
||||
Set<String> queryStringsToPopulate = ResourceIndexedSearchParams.extractCompositeStringUniquesValueChains(resourceType, partsChoices);
|
||||
|
||||
for (String nextQueryString : queryStringsToPopulate) {
|
||||
if (isNotBlank(nextQueryString)) {
|
||||
theParams.compositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString));
|
||||
// FIXME: JA change to trace
|
||||
ourLog.info("Adding composite unique SP: {}", nextQueryString);
|
||||
theParams.myCompositeStringUniques.add(new ResourceIndexedCompositeStringUnique(theEntity, nextQueryString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +207,6 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
|
||||
* matching resource.
|
||||
*/
|
||||
|
||||
public void extractInlineReferences(IBaseResource theResource) {
|
||||
if (!myDaoConfig.isAllowInlineMatchUrlReferences()) {
|
||||
return;
|
||||
|
@ -258,12 +259,12 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
|
||||
// Store composite string uniques
|
||||
if (myDaoConfig.isUniqueIndexesEnabled()) {
|
||||
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(existingParams.compositeStringUniques, theParams.compositeStringUniques)) {
|
||||
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(existingParams.myCompositeStringUniques, theParams.myCompositeStringUniques)) {
|
||||
ourLog.debug("Removing unique index: {}", next);
|
||||
myEntityManager.remove(next);
|
||||
theEntity.getParamsCompositeStringUnique().remove(next);
|
||||
}
|
||||
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.compositeStringUniques, existingParams.compositeStringUniques)) {
|
||||
for (ResourceIndexedCompositeStringUnique next : myDaoSearchParamSynchronizer.subtract(theParams.myCompositeStringUniques, existingParams.myCompositeStringUniques)) {
|
||||
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
|
||||
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
||||
if (existing != null) {
|
||||
|
|
|
@ -100,9 +100,16 @@ public abstract class BaseCaptureQueriesListener implements ProxyDataSourceBuild
|
|||
|
||||
if (theInlineParams) {
|
||||
List<String> nextParams = new ArrayList<>(myParams);
|
||||
while (retVal.contains("?") && nextParams.size() > 0) {
|
||||
int idx = retVal.indexOf("?");
|
||||
retVal = retVal.substring(0, idx) + "'" + nextParams.remove(0) + "'" + retVal.substring(idx + 1);
|
||||
|
||||
int idx = 0;
|
||||
while (nextParams.size() > 0) {
|
||||
idx = retVal.indexOf("?", idx);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
String nextSubstitution = "'" + nextParams.remove(0) + "'";
|
||||
retVal = retVal.substring(0, idx) + nextSubstitution + retVal.substring(idx + 1);
|
||||
idx += nextSubstitution.length();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,18 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe
|
|||
ourLog.info("Select Queries:\n{}", String.join("\n", queries));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log first captured SELECT query
|
||||
*/
|
||||
public void logFirstSelectQueryForCurrentThread() {
|
||||
String firstSelectQuery = getSelectQueriesForCurrentThread()
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(CircularQueueCaptureQueriesListener::formatQueryAsSql)
|
||||
.orElse("NONE FOUND");
|
||||
ourLog.info("First select Query:\n{}", firstSelectQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log all captured INSERT queries
|
||||
*/
|
||||
|
|
|
@ -32,10 +32,10 @@ public class ExpungeOptions {
|
|||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||
.append("myLimit", myLimit)
|
||||
.append("myExpungeOldVersions", myExpungeOldVersions)
|
||||
.append("myExpungeDeletedResources", myExpungeDeletedResources)
|
||||
.append("myExpungeEverything", myExpungeEverything)
|
||||
.append("limit", myLimit)
|
||||
.append("oldVersions", myExpungeOldVersions)
|
||||
.append("deletedResources", myExpungeDeletedResources)
|
||||
.append("everything", myExpungeEverything)
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
|
@ -44,6 +46,8 @@ import static org.junit.Assert.*;
|
|||
public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class);
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
|
@ -101,7 +105,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
private void createUniqueIndexCoverageBeneficiary() {
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.setId("SearchParameter/coverage-beneficiary");
|
||||
|
@ -141,7 +144,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
mySearchParamRegistry.forceRefresh();
|
||||
}
|
||||
|
||||
|
||||
private void createUniqueIndexObservationSubject() {
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
|
@ -170,7 +172,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
mySearchParamRegistry.forceRefresh();
|
||||
}
|
||||
|
||||
|
||||
private void createUniqueIndexPatientIdentifier() {
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
|
@ -199,7 +200,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
mySearchParamRegistry.forceRefresh();
|
||||
}
|
||||
|
||||
|
||||
private void createUniqueIndexPatientIdentifierCount1() {
|
||||
|
||||
SearchParameter sp = new SearchParameter();
|
||||
|
@ -331,9 +331,209 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
assertEquals("gender", params.get(0).getCompositeOf().get(1).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleMatchingOnAnd_Search() {
|
||||
createUniqueIndexPatientIdentifier();
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setActive(true);
|
||||
pt.addIdentifier().setSystem("urn").setValue("111");
|
||||
pt.addIdentifier().setSystem("urn").setValue("222");
|
||||
String id1 = myPatientDao.create(pt).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
pt = new Patient();
|
||||
pt.setActive(true);
|
||||
pt.addIdentifier().setSystem("urn").setValue("333");
|
||||
String id2 = myPatientDao.create(pt).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
pt = new Patient();
|
||||
pt.setActive(false);
|
||||
pt.addIdentifier().setSystem("urn").setValue("444");
|
||||
myPatientDao.create(pt);
|
||||
|
||||
String unformattedSql;
|
||||
|
||||
// Two AND values
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap sp = new SearchParameterMap();
|
||||
sp.setLoadSynchronous(true);
|
||||
sp.add("identifier",
|
||||
new TokenAndListParam()
|
||||
.addAnd(new TokenParam("urn", "111"))
|
||||
.addAnd(new TokenParam("urn", "222"))
|
||||
);
|
||||
IBundleProvider outcome = myPatientDao.search(sp);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(id1));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"IDX_STRING='Patient?identifier=urn%7C111'",
|
||||
"HASH_SYS_AND_VALUE in ('-3122824860083758210')"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
||||
// Two OR values on the same resource - Currently composite SPs don't work for this
|
||||
myCaptureQueriesListener.clear();
|
||||
sp = new SearchParameterMap();
|
||||
sp.setLoadSynchronous(true);
|
||||
sp.add("identifier",
|
||||
new TokenAndListParam()
|
||||
.addAnd(new TokenParam("urn", "111"), new TokenParam("urn", "222"))
|
||||
);
|
||||
outcome = myPatientDao.search(sp);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(id1));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, containsString("HASH_SYS_AND_VALUE in ('4101160957635429999' , '-3122824860083758210')"));
|
||||
assertThat(unformattedSql, not(containsString(("IDX_STRING"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
||||
// Not matching the composite SP at all
|
||||
myCaptureQueriesListener.clear();
|
||||
sp = new SearchParameterMap();
|
||||
sp.setLoadSynchronous(true);
|
||||
sp.add("active",
|
||||
new TokenAndListParam()
|
||||
.addAnd(new TokenParam(null, "true"))
|
||||
);
|
||||
outcome = myPatientDao.search(sp);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(id1, id2));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, not(containsString(("IDX_STRING"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleMatching() {
|
||||
public void testDoubleMatchingOnAnd_Search2() {
|
||||
SearchParameter sp;
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setStatus(PublicationStatus.ACTIVE);
|
||||
sp.setCode("patient");
|
||||
sp.setName("patient");
|
||||
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
sp.addBase(ServiceRequest.class.getName());
|
||||
sp.setExpression("ServiceRequest.subject.where(resolve() is Patient)");
|
||||
String patientParamId = mySearchParameterDao.create(sp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setStatus(PublicationStatus.ACTIVE);
|
||||
sp.setCode("performer");
|
||||
sp.setName("performer");
|
||||
sp.setType(Enumerations.SearchParamType.REFERENCE);
|
||||
sp.addBase(ServiceRequest.class.getName());
|
||||
sp.setExpression("ServiceRequest.performer");
|
||||
String performerParamId = mySearchParameterDao.create(sp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setStatus(PublicationStatus.ACTIVE);
|
||||
sp.setCode("identifier");
|
||||
sp.setName("identifier");
|
||||
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||
sp.addBase(ServiceRequest.class.getName());
|
||||
sp.setExpression("ServiceRequest.identifier");
|
||||
String identifierParamId = mySearchParameterDao.create(sp).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
sp = new SearchParameter();
|
||||
sp.setId("SearchParameter/patient-uniq-identifier");
|
||||
sp.setCode("procreq-patient-performer-identifier");
|
||||
sp.setExpression("ServiceRequest.patient");
|
||||
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||
sp.setStatus(PublicationStatus.ACTIVE);
|
||||
sp.addBase("ServiceRequest");
|
||||
sp.addComponent()
|
||||
.setExpression("ServiceRequest")
|
||||
.setDefinition(patientParamId); // SearchParameter?base=ServiceRequest&name=patient
|
||||
sp.addComponent()
|
||||
.setExpression("ServiceRequest")
|
||||
.setDefinition(performerParamId); // SearchParameter?base=ServiceRequest&name=performer
|
||||
sp.addComponent()
|
||||
.setExpression("ServiceRequest")
|
||||
.setDefinition(identifierParamId); // SearchParameter?base=ServiceRequest&name=identifier
|
||||
sp.addExtension()
|
||||
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
|
||||
.setValue(new BooleanType(true));
|
||||
mySearchParameterDao.create(sp);
|
||||
mySearchParamRegistry.forceRefresh();
|
||||
|
||||
// Now create matching/non-matching resources
|
||||
Patient pt = new Patient();
|
||||
pt.setActive(true);
|
||||
IIdType ptId = myPatientDao.create(pt).getId().toUnqualifiedVersionless();
|
||||
|
||||
Practitioner pract = new Practitioner();
|
||||
pract.setActive(true);
|
||||
IIdType practId = myPractitionerDao.create(pract).getId().toUnqualifiedVersionless();
|
||||
|
||||
ServiceRequest sr = new ServiceRequest();
|
||||
sr.addIdentifier().setSystem("sys").setValue("111");
|
||||
sr.addIdentifier().setSystem("sys").setValue("222");
|
||||
sr.setSubject(new Reference(ptId));
|
||||
sr.addPerformer(new Reference(practId));
|
||||
String srId = myServiceRequestDao.create(sr).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
sr = new ServiceRequest();
|
||||
sr.addIdentifier().setSystem("sys").setValue("888");
|
||||
sr.addIdentifier().setSystem("sys").setValue("999");
|
||||
sr.setSubject(new Reference(ptId));
|
||||
sr.addPerformer(new Reference(practId));
|
||||
myServiceRequestDao.create(sr).getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
String unformattedSql;
|
||||
|
||||
// Use qualified references
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add("identifier",
|
||||
new TokenAndListParam()
|
||||
.addAnd(new TokenParam("sys", "111"))
|
||||
.addAnd(new TokenParam("sys", "222"))
|
||||
);
|
||||
map.add("patient", new ReferenceParam(ptId.getValue()));
|
||||
map.add("performer", new ReferenceParam(practId.getValue()));
|
||||
IBundleProvider outcome = myServiceRequestDao.search(map);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(srId));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"IDX_STRING='ServiceRequest?identifier=sys%7C111&patient=Patient%2F" + ptId.getIdPart() + "&performer=Practitioner%2F"+ practId.getIdPart() +"'",
|
||||
"HASH_SYS_AND_VALUE in ('6795110643554413877')"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
||||
// Don't use qualified references
|
||||
myCaptureQueriesListener.clear();
|
||||
map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
map.add("identifier",
|
||||
new TokenAndListParam()
|
||||
.addAnd(new TokenParam("sys", "111"))
|
||||
.addAnd(new TokenParam("sys", "222"))
|
||||
);
|
||||
map.add("patient", new ReferenceParam(ptId.getIdPart()));
|
||||
map.add("performer", new ReferenceParam(practId.getIdPart()));
|
||||
outcome = myServiceRequestDao.search(map);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), containsInAnyOrder(srId));
|
||||
unformattedSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(unformattedSql, stringContainsInOrder(
|
||||
"SRC_PATH in ('ServiceRequest.subject.where(resolve() is Patient)')",
|
||||
"SRC_PATH in ('ServiceRequest.performer')"
|
||||
));
|
||||
assertThat(unformattedSql, not(containsString(("RES_DELETED_AT"))));
|
||||
assertThat(unformattedSql, not(containsString(("RES_TYPE"))));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleMatchingOnOr_ConditionalCreate() {
|
||||
createUniqueIndexPatientIdentifier();
|
||||
|
||||
Patient pt = new Patient();
|
||||
|
@ -369,12 +569,10 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
.setIfNoneExist("/Patient?identifier=urn|111,urn|222");
|
||||
mySystemDao.transaction(mySrd, input);
|
||||
|
||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus status) {
|
||||
List<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||
assertEquals(2, all.size());
|
||||
}
|
||||
// Make sure entries are saved
|
||||
runInTransaction(() -> {
|
||||
List<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||
assertEquals(2, all.size());
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -424,10 +622,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
|
||||
|
||||
@Test
|
||||
public void testDuplicateUniqueValuesAreReIndexed() {
|
||||
myDaoConfig.setSchedulingDisabled(true);
|
||||
|
@ -767,12 +961,14 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
|||
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||
myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLoadSynchronousUpTo(100);
|
||||
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||
params.add("birthdate", new DateParam("2011-01-01"));
|
||||
IBundleProvider results = myPatientDao.search(params);
|
||||
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||
assertEquals(SearchBuilder.getLastHandlerParamsForUnitTest(), SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class LoggingRule implements TestRule {
|
|||
try {
|
||||
statement.evaluate();
|
||||
} catch (final Throwable e) {
|
||||
logger.info(MessageFormat.format("Exception thrown in test case [{0}]", description.getDisplayName()), e);
|
||||
logger.info(MessageFormat.format("Exception thrown in test case [{0}]: {1}", description.getDisplayName(), e.toString()));
|
||||
throw e;
|
||||
} finally {
|
||||
logger.info(MessageFormat.format("Finished test case [{0}]", description.getDisplayName()));
|
||||
|
|
|
@ -45,7 +45,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public class SearchParameterMap implements Serializable {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMap.class);
|
||||
|
||||
private final HashMap<String, List<List<? extends IQueryParameterType>>> mySearchParameterMap = new LinkedHashMap<>();
|
||||
private final HashMap<String, List<List<IQueryParameterType>>> mySearchParameterMap = new LinkedHashMap<>();
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
@ -107,7 +107,7 @@ public class SearchParameterMap implements Serializable {
|
|||
if (next == null) {
|
||||
continue;
|
||||
}
|
||||
get(theName).add(next.getValuesAsQueryTokens());
|
||||
get(theName).add((List<IQueryParameterType>) next.getValuesAsQueryTokens());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,10 +119,10 @@ public class SearchParameterMap implements Serializable {
|
|||
put(theName, new ArrayList<>());
|
||||
}
|
||||
|
||||
get(theName).add(theOr.getValuesAsQueryTokens());
|
||||
get(theName).add((List<IQueryParameterType>) theOr.getValuesAsQueryTokens());
|
||||
}
|
||||
|
||||
public Collection<List<List<? extends IQueryParameterType>>> values() {
|
||||
public Collection<List<List<IQueryParameterType>>> values() {
|
||||
return mySearchParameterMap.values();
|
||||
}
|
||||
|
||||
|
@ -272,8 +272,8 @@ public class SearchParameterMap implements Serializable {
|
|||
* This will only return true if all parameters have no modifier of any kind
|
||||
*/
|
||||
public boolean isAllParametersHaveNoModifier() {
|
||||
for (List<List<? extends IQueryParameterType>> nextParamName : values()) {
|
||||
for (List<? extends IQueryParameterType> nextAnd : nextParamName) {
|
||||
for (List<List<IQueryParameterType>> nextParamName : values()) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamName) {
|
||||
for (IQueryParameterType nextOr : nextAnd) {
|
||||
if (isNotBlank(nextOr.getQueryParameterQualifier())) {
|
||||
return false;
|
||||
|
@ -319,7 +319,7 @@ public class SearchParameterMap implements Serializable {
|
|||
Collections.sort(keys);
|
||||
for (String nextKey : keys) {
|
||||
|
||||
List<List<? extends IQueryParameterType>> nextValuesAndsIn = get(nextKey);
|
||||
List<List<IQueryParameterType>> nextValuesAndsIn = get(nextKey);
|
||||
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<>();
|
||||
|
||||
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
|
||||
|
@ -448,9 +448,9 @@ public class SearchParameterMap implements Serializable {
|
|||
}
|
||||
|
||||
public void clean() {
|
||||
for (Map.Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : this.entrySet()) {
|
||||
for (Map.Entry<String, List<List<IQueryParameterType>>> nextParamEntry : this.entrySet()) {
|
||||
String nextParamName = nextParamEntry.getKey();
|
||||
List<List<? extends IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
List<List<IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||
clean(nextParamName, andOrParams);
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ public class SearchParameterMap implements Serializable {
|
|||
/*
|
||||
* Filter out
|
||||
*/
|
||||
private void clean(String theParamName, List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
private void clean(String theParamName, List<List<IQueryParameterType>> theAndOrParams) {
|
||||
for (int andListIdx = 0; andListIdx < theAndOrParams.size(); andListIdx++) {
|
||||
List<? extends IQueryParameterType> nextOrList = theAndOrParams.get(andListIdx);
|
||||
|
||||
|
@ -603,11 +603,11 @@ public class SearchParameterMap implements Serializable {
|
|||
|
||||
// Wrapper methods
|
||||
|
||||
public List<List<? extends IQueryParameterType>> get(String theName) {
|
||||
public List<List<IQueryParameterType>> get(String theName) {
|
||||
return mySearchParameterMap.get(theName);
|
||||
}
|
||||
|
||||
private void put(String theName, List<List<? extends IQueryParameterType>> theParams) {
|
||||
private void put(String theName, List<List<IQueryParameterType>> theParams) {
|
||||
mySearchParameterMap.put(theName, theParams);
|
||||
}
|
||||
|
||||
|
@ -623,11 +623,11 @@ public class SearchParameterMap implements Serializable {
|
|||
return mySearchParameterMap.isEmpty();
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, List<List<? extends IQueryParameterType>>>> entrySet() {
|
||||
public Set<Map.Entry<String, List<List<IQueryParameterType>>>> entrySet() {
|
||||
return mySearchParameterMap.entrySet();
|
||||
}
|
||||
|
||||
public List<List<? extends IQueryParameterType>> remove(String theName) {
|
||||
public List<List<IQueryParameterType>> remove(String theName) {
|
||||
return mySearchParameterMap.remove(theName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,86 +37,86 @@ import static org.apache.commons.lang3.StringUtils.compare;
|
|||
public final class ResourceIndexedSearchParams {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceIndexedSearchParams.class);
|
||||
|
||||
final public Collection<ResourceIndexedSearchParamString> stringParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamToken> tokenParams = new HashSet<>();
|
||||
final public Collection<ResourceIndexedSearchParamNumber> numberParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamQuantity> quantityParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamDate> dateParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamUri> uriParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamCoords> coordsParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamString> myStringParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamToken> myTokenParams = new HashSet<>();
|
||||
final public Collection<ResourceIndexedSearchParamNumber> myNumberParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamQuantity> myQuantityParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamDate> myDateParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamUri> myUriParams = new ArrayList<>();
|
||||
final public Collection<ResourceIndexedSearchParamCoords> myCoordsParams = new ArrayList<>();
|
||||
|
||||
final public Collection<ResourceIndexedCompositeStringUnique> compositeStringUniques = new HashSet<>();
|
||||
final public Collection<ResourceLink> links = new HashSet<>();
|
||||
final public Set<String> populatedResourceLinkParameters = new HashSet<>();
|
||||
final public Collection<ResourceIndexedCompositeStringUnique> myCompositeStringUniques = new HashSet<>();
|
||||
final public Collection<ResourceLink> myLinks = new HashSet<>();
|
||||
final public Set<String> myPopulatedResourceLinkParameters = new HashSet<>();
|
||||
|
||||
public ResourceIndexedSearchParams() {
|
||||
}
|
||||
|
||||
public ResourceIndexedSearchParams(ResourceTable theEntity) {
|
||||
if (theEntity.isParamsStringPopulated()) {
|
||||
stringParams.addAll(theEntity.getParamsString());
|
||||
myStringParams.addAll(theEntity.getParamsString());
|
||||
}
|
||||
if (theEntity.isParamsTokenPopulated()) {
|
||||
tokenParams.addAll(theEntity.getParamsToken());
|
||||
myTokenParams.addAll(theEntity.getParamsToken());
|
||||
}
|
||||
if (theEntity.isParamsNumberPopulated()) {
|
||||
numberParams.addAll(theEntity.getParamsNumber());
|
||||
myNumberParams.addAll(theEntity.getParamsNumber());
|
||||
}
|
||||
if (theEntity.isParamsQuantityPopulated()) {
|
||||
quantityParams.addAll(theEntity.getParamsQuantity());
|
||||
myQuantityParams.addAll(theEntity.getParamsQuantity());
|
||||
}
|
||||
if (theEntity.isParamsDatePopulated()) {
|
||||
dateParams.addAll(theEntity.getParamsDate());
|
||||
myDateParams.addAll(theEntity.getParamsDate());
|
||||
}
|
||||
if (theEntity.isParamsUriPopulated()) {
|
||||
uriParams.addAll(theEntity.getParamsUri());
|
||||
myUriParams.addAll(theEntity.getParamsUri());
|
||||
}
|
||||
if (theEntity.isParamsCoordsPopulated()) {
|
||||
coordsParams.addAll(theEntity.getParamsCoords());
|
||||
myCoordsParams.addAll(theEntity.getParamsCoords());
|
||||
}
|
||||
if (theEntity.isHasLinks()) {
|
||||
links.addAll(theEntity.getResourceLinks());
|
||||
myLinks.addAll(theEntity.getResourceLinks());
|
||||
}
|
||||
|
||||
if (theEntity.isParamsCompositeStringUniquePresent()) {
|
||||
compositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
||||
myCompositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Collection<ResourceLink> getResourceLinks() {
|
||||
return links;
|
||||
return myLinks;
|
||||
}
|
||||
|
||||
public void setParamsOn(ResourceTable theEntity) {
|
||||
theEntity.setParamsString(stringParams);
|
||||
theEntity.setParamsStringPopulated(stringParams.isEmpty() == false);
|
||||
theEntity.setParamsToken(tokenParams);
|
||||
theEntity.setParamsTokenPopulated(tokenParams.isEmpty() == false);
|
||||
theEntity.setParamsNumber(numberParams);
|
||||
theEntity.setParamsNumberPopulated(numberParams.isEmpty() == false);
|
||||
theEntity.setParamsQuantity(quantityParams);
|
||||
theEntity.setParamsQuantityPopulated(quantityParams.isEmpty() == false);
|
||||
theEntity.setParamsDate(dateParams);
|
||||
theEntity.setParamsDatePopulated(dateParams.isEmpty() == false);
|
||||
theEntity.setParamsUri(uriParams);
|
||||
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
|
||||
theEntity.setParamsCoords(coordsParams);
|
||||
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
|
||||
theEntity.setParamsCompositeStringUniquePresent(compositeStringUniques.isEmpty() == false);
|
||||
theEntity.setResourceLinks(links);
|
||||
theEntity.setHasLinks(links.isEmpty() == false);
|
||||
theEntity.setParamsString(myStringParams);
|
||||
theEntity.setParamsStringPopulated(myStringParams.isEmpty() == false);
|
||||
theEntity.setParamsToken(myTokenParams);
|
||||
theEntity.setParamsTokenPopulated(myTokenParams.isEmpty() == false);
|
||||
theEntity.setParamsNumber(myNumberParams);
|
||||
theEntity.setParamsNumberPopulated(myNumberParams.isEmpty() == false);
|
||||
theEntity.setParamsQuantity(myQuantityParams);
|
||||
theEntity.setParamsQuantityPopulated(myQuantityParams.isEmpty() == false);
|
||||
theEntity.setParamsDate(myDateParams);
|
||||
theEntity.setParamsDatePopulated(myDateParams.isEmpty() == false);
|
||||
theEntity.setParamsUri(myUriParams);
|
||||
theEntity.setParamsUriPopulated(myUriParams.isEmpty() == false);
|
||||
theEntity.setParamsCoords(myCoordsParams);
|
||||
theEntity.setParamsCoordsPopulated(myCoordsParams.isEmpty() == false);
|
||||
theEntity.setParamsCompositeStringUniquePresent(myCompositeStringUniques.isEmpty() == false);
|
||||
theEntity.setResourceLinks(myLinks);
|
||||
theEntity.setHasLinks(myLinks.isEmpty() == false);
|
||||
}
|
||||
|
||||
public void setUpdatedTime(Date theUpdateTime) {
|
||||
setUpdatedTime(stringParams, theUpdateTime);
|
||||
setUpdatedTime(numberParams, theUpdateTime);
|
||||
setUpdatedTime(quantityParams, theUpdateTime);
|
||||
setUpdatedTime(dateParams, theUpdateTime);
|
||||
setUpdatedTime(uriParams, theUpdateTime);
|
||||
setUpdatedTime(coordsParams, theUpdateTime);
|
||||
setUpdatedTime(tokenParams, theUpdateTime);
|
||||
setUpdatedTime(myStringParams, theUpdateTime);
|
||||
setUpdatedTime(myNumberParams, theUpdateTime);
|
||||
setUpdatedTime(myQuantityParams, theUpdateTime);
|
||||
setUpdatedTime(myDateParams, theUpdateTime);
|
||||
setUpdatedTime(myUriParams, theUpdateTime);
|
||||
setUpdatedTime(myCoordsParams, theUpdateTime);
|
||||
setUpdatedTime(myTokenParams, theUpdateTime);
|
||||
}
|
||||
|
||||
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
|
||||
|
@ -215,7 +215,7 @@ public final class ResourceIndexedSearchParams {
|
|||
}
|
||||
|
||||
public Set<String> getPopulatedResourceLinkParameters() {
|
||||
return populatedResourceLinkParameters;
|
||||
return myPopulatedResourceLinkParameters;
|
||||
}
|
||||
|
||||
public boolean matchParam(String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
|
||||
|
@ -225,22 +225,22 @@ public final class ResourceIndexedSearchParams {
|
|||
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
|
||||
switch (theParamDef.getParamType()) {
|
||||
case TOKEN:
|
||||
resourceParams = tokenParams;
|
||||
resourceParams = myTokenParams;
|
||||
break;
|
||||
case QUANTITY:
|
||||
resourceParams = quantityParams;
|
||||
resourceParams = myQuantityParams;
|
||||
break;
|
||||
case STRING:
|
||||
resourceParams = stringParams;
|
||||
resourceParams = myStringParams;
|
||||
break;
|
||||
case NUMBER:
|
||||
resourceParams = numberParams;
|
||||
resourceParams = myNumberParams;
|
||||
break;
|
||||
case URI:
|
||||
resourceParams = uriParams;
|
||||
resourceParams = myUriParams;
|
||||
break;
|
||||
case DATE:
|
||||
resourceParams = dateParams;
|
||||
resourceParams = myDateParams;
|
||||
break;
|
||||
case REFERENCE:
|
||||
return matchResourceLinks(theResourceName, theParamName, theParam, theParamDef.getPath());
|
||||
|
@ -267,7 +267,7 @@ public final class ResourceIndexedSearchParams {
|
|||
resourceLinkMatches(theResourceName, resourceLink, theParamName, theParamPath)
|
||||
&& resourceIdMatches(resourceLink, reference);
|
||||
|
||||
return links.stream().anyMatch(namedParamPredicate);
|
||||
return myLinks.stream().anyMatch(namedParamPredicate);
|
||||
}
|
||||
|
||||
private boolean resourceIdMatches(ResourceLink theResourceLink, ReferenceParam theReference) {
|
||||
|
@ -293,25 +293,25 @@ public final class ResourceIndexedSearchParams {
|
|||
@Override
|
||||
public String toString() {
|
||||
return "ResourceIndexedSearchParams{" +
|
||||
"stringParams=" + stringParams +
|
||||
", tokenParams=" + tokenParams +
|
||||
", numberParams=" + numberParams +
|
||||
", quantityParams=" + quantityParams +
|
||||
", dateParams=" + dateParams +
|
||||
", uriParams=" + uriParams +
|
||||
", coordsParams=" + coordsParams +
|
||||
", compositeStringUniques=" + compositeStringUniques +
|
||||
", links=" + links +
|
||||
"stringParams=" + myStringParams +
|
||||
", tokenParams=" + myTokenParams +
|
||||
", numberParams=" + myNumberParams +
|
||||
", quantityParams=" + myQuantityParams +
|
||||
", dateParams=" + myDateParams +
|
||||
", uriParams=" + myUriParams +
|
||||
", coordsParams=" + myCoordsParams +
|
||||
", compositeStringUniques=" + myCompositeStringUniques +
|
||||
", links=" + myLinks +
|
||||
'}';
|
||||
}
|
||||
|
||||
public void findMissingSearchParams(ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, stringParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, numberParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, quantityParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, dateParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, uriParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, tokenParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, myStringParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, myDateParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
|
||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, myTokenParams);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -74,7 +74,7 @@ public class ResourceLinkExtractor {
|
|||
extractResourceLinks(theParams, theEntity, theResource, theUpdateTime, theResourceLinkResolver, resourceType, nextSpDef, theFailOnInvalidReference);
|
||||
}
|
||||
|
||||
theEntity.setHasLinks(theParams.links.size() > 0);
|
||||
theEntity.setHasLinks(theParams.myLinks.size() > 0);
|
||||
}
|
||||
|
||||
private void extractResourceLinks(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource, Date theUpdateTime, IResourceLinkResolver theResourceLinkResolver, String theResourceType, RuntimeSearchParam nextSpDef, boolean theFailOnInvalidReference) {
|
||||
|
@ -155,11 +155,11 @@ public class ResourceLinkExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
theParams.populatedResourceLinkParameters.add(nextSpDef.getName());
|
||||
theParams.myPopulatedResourceLinkParameters.add(nextSpDef.getName());
|
||||
|
||||
if (LogicalReferenceHelper.isLogicalReference(myModelConfig, nextId)) {
|
||||
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
|
||||
if (theParams.links.add(resourceLink)) {
|
||||
if (theParams.myLinks.add(resourceLink)) {
|
||||
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
|
||||
}
|
||||
return;
|
||||
|
@ -195,7 +195,7 @@ public class ResourceLinkExtractor {
|
|||
throw new InvalidRequestException(msg);
|
||||
} else {
|
||||
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
|
||||
if (theParams.links.add(resourceLink)) {
|
||||
if (theParams.myLinks.add(resourceLink)) {
|
||||
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
|
||||
}
|
||||
return;
|
||||
|
@ -217,7 +217,7 @@ public class ResourceLinkExtractor {
|
|||
theResourceLinkResolver.validateTypeOrThrowException(type);
|
||||
ResourceLink resourceLink = createResourceLink(theEntity, theUpdateTime, theResourceLinkResolver, nextSpDef, theNextPathsUnsplit, nextPathAndRef, nextId, typeString, type, id);
|
||||
if (resourceLink == null) return;
|
||||
theParams.links.add(resourceLink);
|
||||
theParams.myLinks.add(resourceLink);
|
||||
}
|
||||
|
||||
private ResourceLink createResourceLink(ResourceTable theEntity, Date theUpdateTime, IResourceLinkResolver theResourceLinkResolver, RuntimeSearchParam nextSpDef, String theNextPathsUnsplit, PathAndRef nextPathAndRef, IIdType theNextId, String theTypeString, Class<? extends IBaseResource> theType, String theId) {
|
||||
|
|
|
@ -37,20 +37,20 @@ public class SearchParamExtractorService {
|
|||
private ISearchParamExtractor mySearchParamExtractor;
|
||||
|
||||
public void extractFromResource(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource) {
|
||||
theParams.stringParams.addAll(extractSearchParamStrings(theEntity, theResource));
|
||||
theParams.numberParams.addAll(extractSearchParamNumber(theEntity, theResource));
|
||||
theParams.quantityParams.addAll(extractSearchParamQuantity(theEntity, theResource));
|
||||
theParams.dateParams.addAll(extractSearchParamDates(theEntity, theResource));
|
||||
theParams.uriParams.addAll(extractSearchParamUri(theEntity, theResource));
|
||||
theParams.coordsParams.addAll(extractSearchParamCoords(theEntity, theResource));
|
||||
theParams.myStringParams.addAll(extractSearchParamStrings(theEntity, theResource));
|
||||
theParams.myNumberParams.addAll(extractSearchParamNumber(theEntity, theResource));
|
||||
theParams.myQuantityParams.addAll(extractSearchParamQuantity(theEntity, theResource));
|
||||
theParams.myDateParams.addAll(extractSearchParamDates(theEntity, theResource));
|
||||
theParams.myUriParams.addAll(extractSearchParamUri(theEntity, theResource));
|
||||
theParams.myCoordsParams.addAll(extractSearchParamCoords(theEntity, theResource));
|
||||
|
||||
ourLog.trace("Storing date indexes: {}", theParams.dateParams);
|
||||
ourLog.trace("Storing date indexes: {}", theParams.myDateParams);
|
||||
|
||||
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
|
||||
if (next instanceof ResourceIndexedSearchParamToken) {
|
||||
theParams.tokenParams.add((ResourceIndexedSearchParamToken) next);
|
||||
theParams.myTokenParams.add((ResourceIndexedSearchParamToken) next);
|
||||
} else {
|
||||
theParams.stringParams.add((ResourceIndexedSearchParamString) next);
|
||||
theParams.myStringParams.add((ResourceIndexedSearchParamString) next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,12 +169,7 @@ public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implemen
|
|||
}
|
||||
|
||||
if (next.getCompositeOf() != null) {
|
||||
next.getCompositeOf().sort(new Comparator<RuntimeSearchParam>() {
|
||||
@Override
|
||||
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
|
||||
return StringUtils.compare(theO1.getName(), theO2.getName());
|
||||
}
|
||||
});
|
||||
next.getCompositeOf().sort((theO1, theO2) -> StringUtils.compare(theO1.getName(), theO2.getName()));
|
||||
for (String nextBase : next.getBase()) {
|
||||
if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) {
|
||||
activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<>());
|
||||
|
|
|
@ -47,7 +47,6 @@ import java.util.function.Predicate;
|
|||
@Service
|
||||
public class CriteriaResourceMatcher {
|
||||
|
||||
private static final String CRITERIA = "CRITERIA";
|
||||
@Autowired
|
||||
private MatchUrlService myMatchUrlService;
|
||||
@Autowired
|
||||
|
@ -83,9 +82,9 @@ public class CriteriaResourceMatcher {
|
|||
return SubscriptionMatchResult.unsupportedFromParameterAndReason(Constants.PARAM_LASTUPDATED, SubscriptionMatchResult.STANDARD_PARAMETER);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<List<? extends IQueryParameterType>>> entry : searchParameterMap.entrySet()) {
|
||||
for (Map.Entry<String, List<List<IQueryParameterType>>> entry : searchParameterMap.entrySet()) {
|
||||
String theParamName = entry.getKey();
|
||||
List<List<? extends IQueryParameterType>> theAndOrParams = entry.getValue();
|
||||
List<List<IQueryParameterType>> theAndOrParams = entry.getValue();
|
||||
SubscriptionMatchResult result = matchIdsWithAndOr(theParamName, theAndOrParams, resourceDefinition, theResource, theSearchParams);
|
||||
if (!result.matched()){
|
||||
return result;
|
||||
|
@ -95,7 +94,7 @@ public class CriteriaResourceMatcher {
|
|||
}
|
||||
|
||||
// This method is modelled from SearchBuilder.searchForIdsWithAndOr()
|
||||
private SubscriptionMatchResult matchIdsWithAndOr(String theParamName, List<List<? extends IQueryParameterType>> theAndOrParams, RuntimeResourceDefinition theResourceDefinition, IBaseResource theResource, ResourceIndexedSearchParams theSearchParams) {
|
||||
private SubscriptionMatchResult matchIdsWithAndOr(String theParamName, List<List<IQueryParameterType>> theAndOrParams, RuntimeResourceDefinition theResourceDefinition, IBaseResource theResource, ResourceIndexedSearchParams theSearchParams) {
|
||||
if (theAndOrParams.isEmpty()) {
|
||||
return SubscriptionMatchResult.successfulMatch();
|
||||
}
|
||||
|
@ -132,13 +131,13 @@ public class CriteriaResourceMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean matchIdsAndOr(List<List<? extends IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
||||
private boolean matchIdsAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
||||
if (theResource == null) {
|
||||
return true;
|
||||
}
|
||||
return theAndOrParams.stream().allMatch(nextAnd -> matchIdsOr(nextAnd, theResource));
|
||||
}
|
||||
private boolean matchIdsOr(List<? extends IQueryParameterType> theOrParams, IBaseResource theResource) {
|
||||
private boolean matchIdsOr(List<IQueryParameterType> theOrParams, IBaseResource theResource) {
|
||||
if (theResource == null) {
|
||||
return true;
|
||||
}
|
||||
|
@ -149,7 +148,7 @@ public class CriteriaResourceMatcher {
|
|||
return theValue.equals(theId.getValue()) || theValue.equals(theId.getIdPart());
|
||||
}
|
||||
|
||||
private SubscriptionMatchResult matchResourceParam(String theParamName, List<List<? extends IQueryParameterType>> theAndOrParams, ResourceIndexedSearchParams theSearchParams, String theResourceName, RuntimeSearchParam theParamDef) {
|
||||
private SubscriptionMatchResult matchResourceParam(String theParamName, List<List<IQueryParameterType>> theAndOrParams, ResourceIndexedSearchParams theSearchParams, String theResourceName, RuntimeSearchParam theParamDef) {
|
||||
if (theParamDef != null) {
|
||||
switch (theParamDef.getParamType()) {
|
||||
case QUANTITY:
|
||||
|
@ -183,15 +182,15 @@ public class CriteriaResourceMatcher {
|
|||
return theNextAnd.stream().anyMatch(token -> theSearchParams.matchParam(theResourceName, theParamName, paramDef, token));
|
||||
}
|
||||
|
||||
private boolean hasChain(List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
private boolean hasChain(List<List<IQueryParameterType>> theAndOrParams) {
|
||||
return theAndOrParams.stream().flatMap(List::stream).anyMatch(param -> param instanceof ReferenceParam && ((ReferenceParam)param).getChain() != null);
|
||||
}
|
||||
|
||||
private boolean hasQualifiers(List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
private boolean hasQualifiers(List<List<IQueryParameterType>> theAndOrParams) {
|
||||
return theAndOrParams.stream().flatMap(List::stream).anyMatch(param -> param.getQueryParameterQualifier() != null);
|
||||
}
|
||||
|
||||
private boolean hasPrefixes(List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
private boolean hasPrefixes(List<List<IQueryParameterType>> theAndOrParams) {
|
||||
Predicate<IQueryParameterType> hasPrefixPredicate = param -> param instanceof BaseParamWithPrefix &&
|
||||
((BaseParamWithPrefix) param).getPrefix() != null;
|
||||
return theAndOrParams.stream().flatMap(List::stream).anyMatch(hasPrefixPredicate);
|
||||
|
|
|
@ -90,6 +90,16 @@
|
|||
HapiLocalizer can now handle message patterns with braces that aren't a part of a
|
||||
message format expression. E.g. "Here is an {example}".
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA searches using a Composite Unique Index will now use that index for faster
|
||||
searching even if the search has _includes and/or _sorts. Previously these two
|
||||
features caused the search builder to skip using the index.
|
||||
</action>
|
||||
<action type="fix">
|
||||
JPA searches using a Composite Unique Index did not return the correct results if
|
||||
a REFERENCE search parameter was used with arguments that consisted of
|
||||
unqualified resource IDs.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.7.0" date="2019-02-06" description="Gale">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue