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 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.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
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.BaseDateTimeDt;
|
||||||
import ca.uhn.fhir.model.primitive.DateDt;
|
import ca.uhn.fhir.model.primitive.DateDt;
|
||||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
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.api.QualifiedParamList;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.util.ObjectUtil;
|
|
||||||
import ca.uhn.fhir.util.ValidateUtil;
|
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.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
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;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> {
|
public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final DateParamDateTimeHolder myValue = new DateParamDateTimeHolder();
|
private final DateParamDateTimeHolder myValue = new DateParamDateTimeHolder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,9 +116,7 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
||||||
b.append(ParameterUtil.escapeWithDefault(getPrefix().getValue()));
|
b.append(ParameterUtil.escapeWithDefault(getPrefix().getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myValue != null) {
|
b.append(ParameterUtil.escapeWithDefault(myValue.getValueAsString()));
|
||||||
b.append(ParameterUtil.escapeWithDefault(myValue.getValueAsString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.toString();
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
@ -132,38 +127,15 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
||||||
}
|
}
|
||||||
|
|
||||||
public TemporalPrecisionEnum getPrecision() {
|
public TemporalPrecisionEnum getPrecision() {
|
||||||
if (myValue != null) {
|
|
||||||
return myValue.getPrecision();
|
return myValue.getPrecision();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getValue() {
|
public Date getValue() {
|
||||||
if (myValue != null) {
|
|
||||||
return myValue.getValue();
|
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() {
|
public String getValueAsString() {
|
||||||
if (myValue != null) {
|
|
||||||
return myValue.getValueAsString();
|
return myValue.getValueAsString();
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -260,7 +232,7 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public DateParamDateTimeHolder() {
|
DateParamDateTimeHolder() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,17 @@ public class TokenAndListParam extends BaseAndListParam<TokenOrListParam> {
|
||||||
return this;
|
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");
|
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;
|
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) {
|
public IBundleProvider search(final SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
||||||
|
|
||||||
if (myDaoConfig.getIndexMissingFields() == DaoConfig.IndexEnabledEnum.DISABLED) {
|
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 (List<? extends IQueryParameterType> nextOrs : nextAnds) {
|
||||||
for (IQueryParameterType next : nextOrs) {
|
for (IQueryParameterType next : nextOrs) {
|
||||||
if (next.getMissing() != null) {
|
if (next.getMissing() != null) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
||||||
super();
|
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) {
|
if (theTerms == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -171,13 +171,13 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
||||||
/*
|
/*
|
||||||
* Handle _content parameter (resource body content)
|
* 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");
|
addTextSearch(qb, bool, contentAndTerms, "myContentText", "myContentTextEdgeNGram", "myContentTextNGram");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle _text parameter (resource narrative content)
|
* 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");
|
addTextSearch(qb, bool, textAndTerms, "myNarrativeText", "myNarrativeTextEdgeNGram", "myNarrativeTextNGram");
|
||||||
|
|
||||||
if (theReferencingPid != null) {
|
if (theReferencingPid != null) {
|
||||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* 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.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
import ca.uhn.fhir.jpa.searchparam.ResourceMetaParams;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
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.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||||
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
|
||||||
|
@ -91,7 +90,6 @@ import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
@ -158,6 +156,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
private int myFetchSize;
|
private int myFetchSize;
|
||||||
private Integer myMaxResultsToFetch;
|
private Integer myMaxResultsToFetch;
|
||||||
private Set<Long> myPidSet;
|
private Set<Long> myPidSet;
|
||||||
|
private boolean myHaveIndexJoins = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* 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) {
|
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) {
|
for (List<? extends IQueryParameterType> nextList : theList) {
|
||||||
|
|
||||||
Set<String> values = new HashSet<>();
|
Set<String> values = new HashSet<>();
|
||||||
|
@ -286,7 +285,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
}
|
}
|
||||||
values.add(nextValue);
|
values.add(nextValue);
|
||||||
} else {
|
} 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);
|
Root<ResourceTable> subQfrom = subQ.from(ResourceTable.class);
|
||||||
subQ.select(subQfrom.get("myId").as(Long.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);
|
andOrParams.add(theOrValues);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -641,7 +640,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return chainValue;
|
return chainValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateResourceId(List<List<? extends IQueryParameterType>> theValues) {
|
private void addPredicateResourceId(List<List<IQueryParameterType>> theValues) {
|
||||||
for (List<? extends IQueryParameterType> nextValue : theValues) {
|
for (List<? extends IQueryParameterType> nextValue : theValues) {
|
||||||
Set<Long> orPids = new HashSet<>();
|
Set<Long> orPids = new HashSet<>();
|
||||||
for (IQueryParameterType next : nextValue) {
|
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;
|
TagTypeEnum tagType;
|
||||||
if (Constants.PARAM_TAG.equals(theParamName)) {
|
if (Constants.PARAM_TAG.equals(theParamName)) {
|
||||||
tagType = TagTypeEnum.TAG;
|
tagType = TagTypeEnum.TAG;
|
||||||
|
@ -1013,13 +1012,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T> Join<ResourceTable, T> createOrReuseJoin(JoinEnum theType, String theSearchParameterName) {
|
private <T> Join<ResourceTable, T> createJoin(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) {
|
|
||||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
||||||
switch (theType) {
|
switch (theType) {
|
||||||
case DATE:
|
case DATE:
|
||||||
|
@ -1044,6 +1037,11 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JoinKey key = new JoinKey(theSearchParameterName, theType);
|
||||||
|
myIndexJoins.put(key, join);
|
||||||
|
myHaveIndexJoins = true;
|
||||||
|
|
||||||
return (Join<ResourceTable, T>) join;
|
return (Join<ResourceTable, T>) join;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,51 +1529,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
myBuilder = myEntityManager.getCriteriaBuilder();
|
myBuilder = myEntityManager.getCriteriaBuilder();
|
||||||
mySearchUuid = theSearchUuid;
|
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) {
|
if (ourTrackHandlersForUnitTest) {
|
||||||
ourLastHandlerParamsForUnitTest = theParams;
|
ourLastHandlerParamsForUnitTest = theParams;
|
||||||
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.STANDARD_QUERY;
|
ourLastHandlerMechanismForUnitTest = HandlerTypeEnum.STANDARD_QUERY;
|
||||||
|
@ -1602,31 +1555,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
if (sort != null) {
|
if (sort != null) {
|
||||||
assert !theCount;
|
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);
|
outerQuery = myBuilder.createQuery(Long.class);
|
||||||
myResourceTableQuery = outerQuery;
|
myResourceTableQuery = outerQuery;
|
||||||
myResourceTableRoot = myResourceTableQuery.from(ResourceTable.class);
|
myResourceTableRoot = myResourceTableQuery.from(ResourceTable.class);
|
||||||
|
@ -1644,7 +1572,6 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
outerQuery.orderBy(orders);
|
outerQuery.orderBy(orders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
outerQuery = myBuilder.createQuery(Long.class);
|
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
|
* If we have any joins to index tables, we get this behaviour already guaranteed so we don't
|
||||||
* need an explicit predicate for it.
|
* need an explicit predicate for it.
|
||||||
*/
|
*/
|
||||||
if (myIndexJoins.isEmpty()) {
|
if (!myHaveIndexJoins) {
|
||||||
if (myParams.getEverythingMode() == null) {
|
if (myParams.getEverythingMode() == null) {
|
||||||
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
|
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
|
||||||
}
|
}
|
||||||
|
@ -2186,17 +2113,111 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
private void searchForIdsWithAndOr(@Nonnull SearchParameterMap theParams) {
|
private void searchForIdsWithAndOr(@Nonnull SearchParameterMap theParams) {
|
||||||
myParams = theParams;
|
myParams = theParams;
|
||||||
|
|
||||||
|
// Remove any empty parameters
|
||||||
theParams.clean();
|
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()) {
|
if (theAndOrParams.isEmpty()) {
|
||||||
return;
|
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 static class CountQueryIterator implements Iterator<Long> {
|
||||||
private final TypedQuery<Long> myQuery;
|
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.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.*;
|
import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -44,17 +43,17 @@ public class DaoSearchParamSynchronizer {
|
||||||
|
|
||||||
public void synchronizeSearchParamsToDatabase(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
|
public void synchronizeSearchParamsToDatabase(ResourceIndexedSearchParams theParams, ResourceTable theEntity, ResourceIndexedSearchParams existingParams) {
|
||||||
|
|
||||||
synchronize(theParams, theEntity, theParams.stringParams, existingParams.stringParams);
|
synchronize(theParams, theEntity, theParams.myStringParams, existingParams.myStringParams);
|
||||||
synchronize(theParams, theEntity, theParams.tokenParams, existingParams.tokenParams);
|
synchronize(theParams, theEntity, theParams.myTokenParams, existingParams.myTokenParams);
|
||||||
synchronize(theParams, theEntity, theParams.numberParams, existingParams.numberParams);
|
synchronize(theParams, theEntity, theParams.myNumberParams, existingParams.myNumberParams);
|
||||||
synchronize(theParams, theEntity, theParams.quantityParams, existingParams.quantityParams);
|
synchronize(theParams, theEntity, theParams.myQuantityParams, existingParams.myQuantityParams);
|
||||||
synchronize(theParams, theEntity, theParams.dateParams, existingParams.dateParams);
|
synchronize(theParams, theEntity, theParams.myDateParams, existingParams.myDateParams);
|
||||||
synchronize(theParams, theEntity, theParams.uriParams, existingParams.uriParams);
|
synchronize(theParams, theEntity, theParams.myUriParams, existingParams.myUriParams);
|
||||||
synchronize(theParams, theEntity, theParams.coordsParams, existingParams.coordsParams);
|
synchronize(theParams, theEntity, theParams.myCoordsParams, existingParams.myCoordsParams);
|
||||||
synchronize(theParams, theEntity, theParams.links, existingParams.links);
|
synchronize(theParams, theEntity, theParams.myLinks, existingParams.myLinks);
|
||||||
|
|
||||||
// make sure links are indexed
|
// 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) {
|
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(); ) {
|
for (Iterator<ResourceLink> existingLinkIter = existingParams.getResourceLinks().iterator(); existingLinkIter.hasNext(); ) {
|
||||||
ResourceLink nextExisting = existingLinkIter.next();
|
ResourceLink nextExisting = existingLinkIter.next();
|
||||||
if (theParams.links.remove(nextExisting)) {
|
if (theParams.myLinks.remove(nextExisting)) {
|
||||||
existingLinkIter.remove();
|
existingLinkIter.remove();
|
||||||
theParams.links.add(nextExisting);
|
theParams.myLinks.add(nextExisting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,28 +133,28 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
Collection<String> linksForCompositePartWantPaths = null;
|
Collection<String> linksForCompositePartWantPaths = null;
|
||||||
switch (nextCompositeOf.getParamType()) {
|
switch (nextCompositeOf.getParamType()) {
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
paramsListForCompositePart = theParams.numberParams;
|
paramsListForCompositePart = theParams.myNumberParams;
|
||||||
break;
|
break;
|
||||||
case DATE:
|
case DATE:
|
||||||
paramsListForCompositePart = theParams.dateParams;
|
paramsListForCompositePart = theParams.myDateParams;
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
paramsListForCompositePart = theParams.stringParams;
|
paramsListForCompositePart = theParams.myStringParams;
|
||||||
break;
|
break;
|
||||||
case TOKEN:
|
case TOKEN:
|
||||||
paramsListForCompositePart = theParams.tokenParams;
|
paramsListForCompositePart = theParams.myTokenParams;
|
||||||
break;
|
break;
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
linksForCompositePart = theParams.links;
|
linksForCompositePart = theParams.myLinks;
|
||||||
linksForCompositePartWantPaths = new HashSet<>();
|
linksForCompositePartWantPaths = new HashSet<>(nextCompositeOf.getPathsSplit());
|
||||||
linksForCompositePartWantPaths.addAll(nextCompositeOf.getPathsSplit());
|
|
||||||
break;
|
break;
|
||||||
case QUANTITY:
|
case QUANTITY:
|
||||||
paramsListForCompositePart = theParams.quantityParams;
|
paramsListForCompositePart = theParams.myQuantityParams;
|
||||||
break;
|
break;
|
||||||
case URI:
|
case URI:
|
||||||
paramsListForCompositePart = theParams.uriParams;
|
paramsListForCompositePart = theParams.myUriParams;
|
||||||
break;
|
break;
|
||||||
|
case SPECIAL:
|
||||||
case COMPOSITE:
|
case COMPOSITE:
|
||||||
case HAS:
|
case HAS:
|
||||||
break;
|
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) {
|
for (String nextQueryString : queryStringsToPopulate) {
|
||||||
if (isNotBlank(nextQueryString)) {
|
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
|
* 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.
|
* matching resource.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void extractInlineReferences(IBaseResource theResource) {
|
public void extractInlineReferences(IBaseResource theResource) {
|
||||||
if (!myDaoConfig.isAllowInlineMatchUrlReferences()) {
|
if (!myDaoConfig.isAllowInlineMatchUrlReferences()) {
|
||||||
return;
|
return;
|
||||||
|
@ -258,12 +259,12 @@ public class SearchParamWithInlineReferencesExtractor {
|
||||||
|
|
||||||
// Store composite string uniques
|
// Store composite string uniques
|
||||||
if (myDaoConfig.isUniqueIndexesEnabled()) {
|
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);
|
ourLog.debug("Removing unique index: {}", next);
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
theEntity.getParamsCompositeStringUnique().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()) {
|
if (myDaoConfig.isUniqueIndexesCheckedBeforeSave()) {
|
||||||
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
|
|
|
@ -100,9 +100,16 @@ public abstract class BaseCaptureQueriesListener implements ProxyDataSourceBuild
|
||||||
|
|
||||||
if (theInlineParams) {
|
if (theInlineParams) {
|
||||||
List<String> nextParams = new ArrayList<>(myParams);
|
List<String> nextParams = new ArrayList<>(myParams);
|
||||||
while (retVal.contains("?") && nextParams.size() > 0) {
|
|
||||||
int idx = retVal.indexOf("?");
|
int idx = 0;
|
||||||
retVal = retVal.substring(0, idx) + "'" + nextParams.remove(0) + "'" + retVal.substring(idx + 1);
|
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));
|
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
|
* Log all captured INSERT queries
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,10 +32,10 @@ public class ExpungeOptions {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
|
||||||
.append("myLimit", myLimit)
|
.append("limit", myLimit)
|
||||||
.append("myExpungeOldVersions", myExpungeOldVersions)
|
.append("oldVersions", myExpungeOldVersions)
|
||||||
.append("myExpungeDeletedResources", myExpungeDeletedResources)
|
.append("deletedResources", myExpungeDeletedResources)
|
||||||
.append("myExpungeEverything", myExpungeEverything)
|
.append("everything", myExpungeEverything)
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
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.ReferenceParam;
|
||||||
|
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
@ -44,6 +46,8 @@ import static org.junit.Assert.*;
|
||||||
public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class);
|
||||||
|
@Autowired
|
||||||
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() {
|
public void after() {
|
||||||
|
@ -101,7 +105,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void createUniqueIndexCoverageBeneficiary() {
|
private void createUniqueIndexCoverageBeneficiary() {
|
||||||
SearchParameter sp = new SearchParameter();
|
SearchParameter sp = new SearchParameter();
|
||||||
sp.setId("SearchParameter/coverage-beneficiary");
|
sp.setId("SearchParameter/coverage-beneficiary");
|
||||||
|
@ -141,7 +144,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void createUniqueIndexObservationSubject() {
|
private void createUniqueIndexObservationSubject() {
|
||||||
|
|
||||||
SearchParameter sp = new SearchParameter();
|
SearchParameter sp = new SearchParameter();
|
||||||
|
@ -170,7 +172,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void createUniqueIndexPatientIdentifier() {
|
private void createUniqueIndexPatientIdentifier() {
|
||||||
|
|
||||||
SearchParameter sp = new SearchParameter();
|
SearchParameter sp = new SearchParameter();
|
||||||
|
@ -199,7 +200,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void createUniqueIndexPatientIdentifierCount1() {
|
private void createUniqueIndexPatientIdentifierCount1() {
|
||||||
|
|
||||||
SearchParameter sp = new SearchParameter();
|
SearchParameter sp = new SearchParameter();
|
||||||
|
@ -331,9 +331,209 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
assertEquals("gender", params.get(0).getCompositeOf().get(1).getName());
|
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
|
@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();
|
createUniqueIndexPatientIdentifier();
|
||||||
|
|
||||||
Patient pt = new Patient();
|
Patient pt = new Patient();
|
||||||
|
@ -369,12 +569,10 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
.setIfNoneExist("/Patient?identifier=urn|111,urn|222");
|
.setIfNoneExist("/Patient?identifier=urn|111,urn|222");
|
||||||
mySystemDao.transaction(mySrd, input);
|
mySystemDao.transaction(mySrd, input);
|
||||||
|
|
||||||
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
// Make sure entries are saved
|
||||||
@Override
|
runInTransaction(() -> {
|
||||||
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus status) {
|
List<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
|
||||||
List<ResourceIndexedCompositeStringUnique> all = myResourceIndexedCompositeStringUniqueDao.findAll();
|
assertEquals(2, all.size());
|
||||||
assertEquals(2, all.size());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -424,10 +622,6 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDuplicateUniqueValuesAreReIndexed() {
|
public void testDuplicateUniqueValuesAreReIndexed() {
|
||||||
myDaoConfig.setSchedulingDisabled(true);
|
myDaoConfig.setSchedulingDisabled(true);
|
||||||
|
@ -767,12 +961,14 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
pt2.setBirthDateElement(new DateType("2011-01-02"));
|
||||||
myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
myPatientDao.create(pt2).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
SearchBuilder.resetLastHandlerMechanismForUnitTest();
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
params.setLoadSynchronousUpTo(100);
|
params.setLoadSynchronousUpTo(100);
|
||||||
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
params.add("gender", new TokenParam("http://hl7.org/fhir/administrative-gender", "male"));
|
||||||
params.add("birthdate", new DateParam("2011-01-01"));
|
params.add("birthdate", new DateParam("2011-01-01"));
|
||||||
IBundleProvider results = myPatientDao.search(params);
|
IBundleProvider results = myPatientDao.search(params);
|
||||||
|
myCaptureQueriesListener.logFirstSelectQueryForCurrentThread();
|
||||||
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
assertThat(toUnqualifiedVersionlessIdValues(results), containsInAnyOrder(id1.getValue()));
|
||||||
assertEquals(SearchBuilder.getLastHandlerParamsForUnitTest(), SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
assertEquals(SearchBuilder.getLastHandlerParamsForUnitTest(), SearchBuilder.HandlerTypeEnum.UNIQUE_INDEX, SearchBuilder.getLastHandlerMechanismForUnitTest());
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class LoggingRule implements TestRule {
|
||||||
try {
|
try {
|
||||||
statement.evaluate();
|
statement.evaluate();
|
||||||
} catch (final Throwable e) {
|
} 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;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
logger.info(MessageFormat.format("Finished test case [{0}]", description.getDisplayName()));
|
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 {
|
public class SearchParameterMap implements Serializable {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMap.class);
|
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;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ public class SearchParameterMap implements Serializable {
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
continue;
|
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<>());
|
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();
|
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
|
* This will only return true if all parameters have no modifier of any kind
|
||||||
*/
|
*/
|
||||||
public boolean isAllParametersHaveNoModifier() {
|
public boolean isAllParametersHaveNoModifier() {
|
||||||
for (List<List<? extends IQueryParameterType>> nextParamName : values()) {
|
for (List<List<IQueryParameterType>> nextParamName : values()) {
|
||||||
for (List<? extends IQueryParameterType> nextAnd : nextParamName) {
|
for (List<IQueryParameterType> nextAnd : nextParamName) {
|
||||||
for (IQueryParameterType nextOr : nextAnd) {
|
for (IQueryParameterType nextOr : nextAnd) {
|
||||||
if (isNotBlank(nextOr.getQueryParameterQualifier())) {
|
if (isNotBlank(nextOr.getQueryParameterQualifier())) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -319,7 +319,7 @@ public class SearchParameterMap implements Serializable {
|
||||||
Collections.sort(keys);
|
Collections.sort(keys);
|
||||||
for (String nextKey : keys) {
|
for (String nextKey : keys) {
|
||||||
|
|
||||||
List<List<? extends IQueryParameterType>> nextValuesAndsIn = get(nextKey);
|
List<List<IQueryParameterType>> nextValuesAndsIn = get(nextKey);
|
||||||
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<>();
|
List<List<IQueryParameterType>> nextValuesAndsOut = new ArrayList<>();
|
||||||
|
|
||||||
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
|
for (List<? extends IQueryParameterType> nextValuesAndIn : nextValuesAndsIn) {
|
||||||
|
@ -448,9 +448,9 @@ public class SearchParameterMap implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clean() {
|
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();
|
String nextParamName = nextParamEntry.getKey();
|
||||||
List<List<? extends IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
List<List<IQueryParameterType>> andOrParams = nextParamEntry.getValue();
|
||||||
clean(nextParamName, andOrParams);
|
clean(nextParamName, andOrParams);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -458,7 +458,7 @@ public class SearchParameterMap implements Serializable {
|
||||||
/*
|
/*
|
||||||
* Filter out
|
* 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++) {
|
for (int andListIdx = 0; andListIdx < theAndOrParams.size(); andListIdx++) {
|
||||||
List<? extends IQueryParameterType> nextOrList = theAndOrParams.get(andListIdx);
|
List<? extends IQueryParameterType> nextOrList = theAndOrParams.get(andListIdx);
|
||||||
|
|
||||||
|
@ -603,11 +603,11 @@ public class SearchParameterMap implements Serializable {
|
||||||
|
|
||||||
// Wrapper methods
|
// Wrapper methods
|
||||||
|
|
||||||
public List<List<? extends IQueryParameterType>> get(String theName) {
|
public List<List<IQueryParameterType>> get(String theName) {
|
||||||
return mySearchParameterMap.get(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);
|
mySearchParameterMap.put(theName, theParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,11 +623,11 @@ public class SearchParameterMap implements Serializable {
|
||||||
return mySearchParameterMap.isEmpty();
|
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();
|
return mySearchParameterMap.entrySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<List<? extends IQueryParameterType>> remove(String theName) {
|
public List<List<IQueryParameterType>> remove(String theName) {
|
||||||
return mySearchParameterMap.remove(theName);
|
return mySearchParameterMap.remove(theName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,86 +37,86 @@ import static org.apache.commons.lang3.StringUtils.compare;
|
||||||
public final class ResourceIndexedSearchParams {
|
public final class ResourceIndexedSearchParams {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceIndexedSearchParams.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceIndexedSearchParams.class);
|
||||||
|
|
||||||
final public Collection<ResourceIndexedSearchParamString> stringParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamString> myStringParams = new ArrayList<>();
|
||||||
final public Collection<ResourceIndexedSearchParamToken> tokenParams = new HashSet<>();
|
final public Collection<ResourceIndexedSearchParamToken> myTokenParams = new HashSet<>();
|
||||||
final public Collection<ResourceIndexedSearchParamNumber> numberParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamNumber> myNumberParams = new ArrayList<>();
|
||||||
final public Collection<ResourceIndexedSearchParamQuantity> quantityParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamQuantity> myQuantityParams = new ArrayList<>();
|
||||||
final public Collection<ResourceIndexedSearchParamDate> dateParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamDate> myDateParams = new ArrayList<>();
|
||||||
final public Collection<ResourceIndexedSearchParamUri> uriParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamUri> myUriParams = new ArrayList<>();
|
||||||
final public Collection<ResourceIndexedSearchParamCoords> coordsParams = new ArrayList<>();
|
final public Collection<ResourceIndexedSearchParamCoords> myCoordsParams = new ArrayList<>();
|
||||||
|
|
||||||
final public Collection<ResourceIndexedCompositeStringUnique> compositeStringUniques = new HashSet<>();
|
final public Collection<ResourceIndexedCompositeStringUnique> myCompositeStringUniques = new HashSet<>();
|
||||||
final public Collection<ResourceLink> links = new HashSet<>();
|
final public Collection<ResourceLink> myLinks = new HashSet<>();
|
||||||
final public Set<String> populatedResourceLinkParameters = new HashSet<>();
|
final public Set<String> myPopulatedResourceLinkParameters = new HashSet<>();
|
||||||
|
|
||||||
public ResourceIndexedSearchParams() {
|
public ResourceIndexedSearchParams() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceIndexedSearchParams(ResourceTable theEntity) {
|
public ResourceIndexedSearchParams(ResourceTable theEntity) {
|
||||||
if (theEntity.isParamsStringPopulated()) {
|
if (theEntity.isParamsStringPopulated()) {
|
||||||
stringParams.addAll(theEntity.getParamsString());
|
myStringParams.addAll(theEntity.getParamsString());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsTokenPopulated()) {
|
if (theEntity.isParamsTokenPopulated()) {
|
||||||
tokenParams.addAll(theEntity.getParamsToken());
|
myTokenParams.addAll(theEntity.getParamsToken());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsNumberPopulated()) {
|
if (theEntity.isParamsNumberPopulated()) {
|
||||||
numberParams.addAll(theEntity.getParamsNumber());
|
myNumberParams.addAll(theEntity.getParamsNumber());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsQuantityPopulated()) {
|
if (theEntity.isParamsQuantityPopulated()) {
|
||||||
quantityParams.addAll(theEntity.getParamsQuantity());
|
myQuantityParams.addAll(theEntity.getParamsQuantity());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsDatePopulated()) {
|
if (theEntity.isParamsDatePopulated()) {
|
||||||
dateParams.addAll(theEntity.getParamsDate());
|
myDateParams.addAll(theEntity.getParamsDate());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsUriPopulated()) {
|
if (theEntity.isParamsUriPopulated()) {
|
||||||
uriParams.addAll(theEntity.getParamsUri());
|
myUriParams.addAll(theEntity.getParamsUri());
|
||||||
}
|
}
|
||||||
if (theEntity.isParamsCoordsPopulated()) {
|
if (theEntity.isParamsCoordsPopulated()) {
|
||||||
coordsParams.addAll(theEntity.getParamsCoords());
|
myCoordsParams.addAll(theEntity.getParamsCoords());
|
||||||
}
|
}
|
||||||
if (theEntity.isHasLinks()) {
|
if (theEntity.isHasLinks()) {
|
||||||
links.addAll(theEntity.getResourceLinks());
|
myLinks.addAll(theEntity.getResourceLinks());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theEntity.isParamsCompositeStringUniquePresent()) {
|
if (theEntity.isParamsCompositeStringUniquePresent()) {
|
||||||
compositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
myCompositeStringUniques.addAll(theEntity.getParamsCompositeStringUnique());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Collection<ResourceLink> getResourceLinks() {
|
public Collection<ResourceLink> getResourceLinks() {
|
||||||
return links;
|
return myLinks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParamsOn(ResourceTable theEntity) {
|
public void setParamsOn(ResourceTable theEntity) {
|
||||||
theEntity.setParamsString(stringParams);
|
theEntity.setParamsString(myStringParams);
|
||||||
theEntity.setParamsStringPopulated(stringParams.isEmpty() == false);
|
theEntity.setParamsStringPopulated(myStringParams.isEmpty() == false);
|
||||||
theEntity.setParamsToken(tokenParams);
|
theEntity.setParamsToken(myTokenParams);
|
||||||
theEntity.setParamsTokenPopulated(tokenParams.isEmpty() == false);
|
theEntity.setParamsTokenPopulated(myTokenParams.isEmpty() == false);
|
||||||
theEntity.setParamsNumber(numberParams);
|
theEntity.setParamsNumber(myNumberParams);
|
||||||
theEntity.setParamsNumberPopulated(numberParams.isEmpty() == false);
|
theEntity.setParamsNumberPopulated(myNumberParams.isEmpty() == false);
|
||||||
theEntity.setParamsQuantity(quantityParams);
|
theEntity.setParamsQuantity(myQuantityParams);
|
||||||
theEntity.setParamsQuantityPopulated(quantityParams.isEmpty() == false);
|
theEntity.setParamsQuantityPopulated(myQuantityParams.isEmpty() == false);
|
||||||
theEntity.setParamsDate(dateParams);
|
theEntity.setParamsDate(myDateParams);
|
||||||
theEntity.setParamsDatePopulated(dateParams.isEmpty() == false);
|
theEntity.setParamsDatePopulated(myDateParams.isEmpty() == false);
|
||||||
theEntity.setParamsUri(uriParams);
|
theEntity.setParamsUri(myUriParams);
|
||||||
theEntity.setParamsUriPopulated(uriParams.isEmpty() == false);
|
theEntity.setParamsUriPopulated(myUriParams.isEmpty() == false);
|
||||||
theEntity.setParamsCoords(coordsParams);
|
theEntity.setParamsCoords(myCoordsParams);
|
||||||
theEntity.setParamsCoordsPopulated(coordsParams.isEmpty() == false);
|
theEntity.setParamsCoordsPopulated(myCoordsParams.isEmpty() == false);
|
||||||
theEntity.setParamsCompositeStringUniquePresent(compositeStringUniques.isEmpty() == false);
|
theEntity.setParamsCompositeStringUniquePresent(myCompositeStringUniques.isEmpty() == false);
|
||||||
theEntity.setResourceLinks(links);
|
theEntity.setResourceLinks(myLinks);
|
||||||
theEntity.setHasLinks(links.isEmpty() == false);
|
theEntity.setHasLinks(myLinks.isEmpty() == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUpdatedTime(Date theUpdateTime) {
|
public void setUpdatedTime(Date theUpdateTime) {
|
||||||
setUpdatedTime(stringParams, theUpdateTime);
|
setUpdatedTime(myStringParams, theUpdateTime);
|
||||||
setUpdatedTime(numberParams, theUpdateTime);
|
setUpdatedTime(myNumberParams, theUpdateTime);
|
||||||
setUpdatedTime(quantityParams, theUpdateTime);
|
setUpdatedTime(myQuantityParams, theUpdateTime);
|
||||||
setUpdatedTime(dateParams, theUpdateTime);
|
setUpdatedTime(myDateParams, theUpdateTime);
|
||||||
setUpdatedTime(uriParams, theUpdateTime);
|
setUpdatedTime(myUriParams, theUpdateTime);
|
||||||
setUpdatedTime(coordsParams, theUpdateTime);
|
setUpdatedTime(myCoordsParams, theUpdateTime);
|
||||||
setUpdatedTime(tokenParams, theUpdateTime);
|
setUpdatedTime(myTokenParams, theUpdateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
|
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
|
||||||
|
@ -215,7 +215,7 @@ public final class ResourceIndexedSearchParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getPopulatedResourceLinkParameters() {
|
public Set<String> getPopulatedResourceLinkParameters() {
|
||||||
return populatedResourceLinkParameters;
|
return myPopulatedResourceLinkParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matchParam(String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
|
public boolean matchParam(String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
|
||||||
|
@ -225,22 +225,22 @@ public final class ResourceIndexedSearchParams {
|
||||||
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
|
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
|
||||||
switch (theParamDef.getParamType()) {
|
switch (theParamDef.getParamType()) {
|
||||||
case TOKEN:
|
case TOKEN:
|
||||||
resourceParams = tokenParams;
|
resourceParams = myTokenParams;
|
||||||
break;
|
break;
|
||||||
case QUANTITY:
|
case QUANTITY:
|
||||||
resourceParams = quantityParams;
|
resourceParams = myQuantityParams;
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
resourceParams = stringParams;
|
resourceParams = myStringParams;
|
||||||
break;
|
break;
|
||||||
case NUMBER:
|
case NUMBER:
|
||||||
resourceParams = numberParams;
|
resourceParams = myNumberParams;
|
||||||
break;
|
break;
|
||||||
case URI:
|
case URI:
|
||||||
resourceParams = uriParams;
|
resourceParams = myUriParams;
|
||||||
break;
|
break;
|
||||||
case DATE:
|
case DATE:
|
||||||
resourceParams = dateParams;
|
resourceParams = myDateParams;
|
||||||
break;
|
break;
|
||||||
case REFERENCE:
|
case REFERENCE:
|
||||||
return matchResourceLinks(theResourceName, theParamName, theParam, theParamDef.getPath());
|
return matchResourceLinks(theResourceName, theParamName, theParam, theParamDef.getPath());
|
||||||
|
@ -267,7 +267,7 @@ public final class ResourceIndexedSearchParams {
|
||||||
resourceLinkMatches(theResourceName, resourceLink, theParamName, theParamPath)
|
resourceLinkMatches(theResourceName, resourceLink, theParamName, theParamPath)
|
||||||
&& resourceIdMatches(resourceLink, reference);
|
&& resourceIdMatches(resourceLink, reference);
|
||||||
|
|
||||||
return links.stream().anyMatch(namedParamPredicate);
|
return myLinks.stream().anyMatch(namedParamPredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean resourceIdMatches(ResourceLink theResourceLink, ReferenceParam theReference) {
|
private boolean resourceIdMatches(ResourceLink theResourceLink, ReferenceParam theReference) {
|
||||||
|
@ -293,25 +293,25 @@ public final class ResourceIndexedSearchParams {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ResourceIndexedSearchParams{" +
|
return "ResourceIndexedSearchParams{" +
|
||||||
"stringParams=" + stringParams +
|
"stringParams=" + myStringParams +
|
||||||
", tokenParams=" + tokenParams +
|
", tokenParams=" + myTokenParams +
|
||||||
", numberParams=" + numberParams +
|
", numberParams=" + myNumberParams +
|
||||||
", quantityParams=" + quantityParams +
|
", quantityParams=" + myQuantityParams +
|
||||||
", dateParams=" + dateParams +
|
", dateParams=" + myDateParams +
|
||||||
", uriParams=" + uriParams +
|
", uriParams=" + myUriParams +
|
||||||
", coordsParams=" + coordsParams +
|
", coordsParams=" + myCoordsParams +
|
||||||
", compositeStringUniques=" + compositeStringUniques +
|
", compositeStringUniques=" + myCompositeStringUniques +
|
||||||
", links=" + links +
|
", links=" + myLinks +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public void findMissingSearchParams(ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
|
public void findMissingSearchParams(ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, stringParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, myStringParams);
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, numberParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams);
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, quantityParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, dateParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, myDateParams);
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, uriParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
|
||||||
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, tokenParams);
|
findMissingSearchParams(theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, myTokenParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class ResourceLinkExtractor {
|
||||||
extractResourceLinks(theParams, theEntity, theResource, theUpdateTime, theResourceLinkResolver, resourceType, nextSpDef, theFailOnInvalidReference);
|
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) {
|
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)) {
|
if (LogicalReferenceHelper.isLogicalReference(myModelConfig, nextId)) {
|
||||||
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
|
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);
|
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -195,7 +195,7 @@ public class ResourceLinkExtractor {
|
||||||
throw new InvalidRequestException(msg);
|
throw new InvalidRequestException(msg);
|
||||||
} else {
|
} else {
|
||||||
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
|
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);
|
ourLog.debug("Indexing remote resource reference URL: {}", nextId);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -217,7 +217,7 @@ public class ResourceLinkExtractor {
|
||||||
theResourceLinkResolver.validateTypeOrThrowException(type);
|
theResourceLinkResolver.validateTypeOrThrowException(type);
|
||||||
ResourceLink resourceLink = createResourceLink(theEntity, theUpdateTime, theResourceLinkResolver, nextSpDef, theNextPathsUnsplit, nextPathAndRef, nextId, typeString, type, id);
|
ResourceLink resourceLink = createResourceLink(theEntity, theUpdateTime, theResourceLinkResolver, nextSpDef, theNextPathsUnsplit, nextPathAndRef, nextId, typeString, type, id);
|
||||||
if (resourceLink == null) return;
|
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) {
|
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;
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
|
||||||
public void extractFromResource(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource) {
|
public void extractFromResource(ResourceIndexedSearchParams theParams, ResourceTable theEntity, IBaseResource theResource) {
|
||||||
theParams.stringParams.addAll(extractSearchParamStrings(theEntity, theResource));
|
theParams.myStringParams.addAll(extractSearchParamStrings(theEntity, theResource));
|
||||||
theParams.numberParams.addAll(extractSearchParamNumber(theEntity, theResource));
|
theParams.myNumberParams.addAll(extractSearchParamNumber(theEntity, theResource));
|
||||||
theParams.quantityParams.addAll(extractSearchParamQuantity(theEntity, theResource));
|
theParams.myQuantityParams.addAll(extractSearchParamQuantity(theEntity, theResource));
|
||||||
theParams.dateParams.addAll(extractSearchParamDates(theEntity, theResource));
|
theParams.myDateParams.addAll(extractSearchParamDates(theEntity, theResource));
|
||||||
theParams.uriParams.addAll(extractSearchParamUri(theEntity, theResource));
|
theParams.myUriParams.addAll(extractSearchParamUri(theEntity, theResource));
|
||||||
theParams.coordsParams.addAll(extractSearchParamCoords(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)) {
|
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
|
||||||
if (next instanceof ResourceIndexedSearchParamToken) {
|
if (next instanceof ResourceIndexedSearchParamToken) {
|
||||||
theParams.tokenParams.add((ResourceIndexedSearchParamToken) next);
|
theParams.myTokenParams.add((ResourceIndexedSearchParamToken) next);
|
||||||
} else {
|
} 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) {
|
if (next.getCompositeOf() != null) {
|
||||||
next.getCompositeOf().sort(new Comparator<RuntimeSearchParam>() {
|
next.getCompositeOf().sort((theO1, theO2) -> StringUtils.compare(theO1.getName(), theO2.getName()));
|
||||||
@Override
|
|
||||||
public int compare(RuntimeSearchParam theO1, RuntimeSearchParam theO2) {
|
|
||||||
return StringUtils.compare(theO1.getName(), theO2.getName());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (String nextBase : next.getBase()) {
|
for (String nextBase : next.getBase()) {
|
||||||
if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) {
|
if (!activeParamNamesToUniqueSearchParams.containsKey(nextBase)) {
|
||||||
activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<>());
|
activeParamNamesToUniqueSearchParams.put(nextBase, new HashMap<>());
|
||||||
|
|
|
@ -47,7 +47,6 @@ import java.util.function.Predicate;
|
||||||
@Service
|
@Service
|
||||||
public class CriteriaResourceMatcher {
|
public class CriteriaResourceMatcher {
|
||||||
|
|
||||||
private static final String CRITERIA = "CRITERIA";
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private MatchUrlService myMatchUrlService;
|
private MatchUrlService myMatchUrlService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -83,9 +82,9 @@ public class CriteriaResourceMatcher {
|
||||||
return SubscriptionMatchResult.unsupportedFromParameterAndReason(Constants.PARAM_LASTUPDATED, SubscriptionMatchResult.STANDARD_PARAMETER);
|
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();
|
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);
|
SubscriptionMatchResult result = matchIdsWithAndOr(theParamName, theAndOrParams, resourceDefinition, theResource, theSearchParams);
|
||||||
if (!result.matched()){
|
if (!result.matched()){
|
||||||
return result;
|
return result;
|
||||||
|
@ -95,7 +94,7 @@ public class CriteriaResourceMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is modelled from SearchBuilder.searchForIdsWithAndOr()
|
// 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()) {
|
if (theAndOrParams.isEmpty()) {
|
||||||
return SubscriptionMatchResult.successfulMatch();
|
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) {
|
if (theResource == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return theAndOrParams.stream().allMatch(nextAnd -> matchIdsOr(nextAnd, theResource));
|
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) {
|
if (theResource == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +148,7 @@ public class CriteriaResourceMatcher {
|
||||||
return theValue.equals(theId.getValue()) || theValue.equals(theId.getIdPart());
|
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) {
|
if (theParamDef != null) {
|
||||||
switch (theParamDef.getParamType()) {
|
switch (theParamDef.getParamType()) {
|
||||||
case QUANTITY:
|
case QUANTITY:
|
||||||
|
@ -183,15 +182,15 @@ public class CriteriaResourceMatcher {
|
||||||
return theNextAnd.stream().anyMatch(token -> theSearchParams.matchParam(theResourceName, theParamName, paramDef, token));
|
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);
|
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);
|
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 &&
|
Predicate<IQueryParameterType> hasPrefixPredicate = param -> param instanceof BaseParamWithPrefix &&
|
||||||
((BaseParamWithPrefix) param).getPrefix() != null;
|
((BaseParamWithPrefix) param).getPrefix() != null;
|
||||||
return theAndOrParams.stream().flatMap(List::stream).anyMatch(hasPrefixPredicate);
|
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
|
HapiLocalizer can now handle message patterns with braces that aren't a part of a
|
||||||
message format expression. E.g. "Here is an {example}".
|
message format expression. E.g. "Here is an {example}".
|
||||||
</action>
|
</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>
|
||||||
<release version="3.7.0" date="2019-02-06" description="Gale">
|
<release version="3.7.0" date="2019-02-06" description="Gale">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue