Performance enhancements to JPA searching
This commit is contained in:
parent
a46b4a4637
commit
99f80eef88
|
@ -910,7 +910,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
||||||
param = new ResourceIndexedSearchParamQuantity();
|
param = new ResourceIndexedSearchParamQuantity();
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
param = new ResourceIndexedSearchParamString();
|
param = new ResourceIndexedSearchParamString()
|
||||||
|
.setDaoConfig(myConfig);
|
||||||
break;
|
break;
|
||||||
case TOKEN:
|
case TOKEN:
|
||||||
param = new ResourceIndexedSearchParamToken();
|
param = new ResourceIndexedSearchParamToken();
|
||||||
|
@ -2057,6 +2058,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao,
|
||||||
if (thePerformIndexing) {
|
if (thePerformIndexing) {
|
||||||
|
|
||||||
for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) {
|
for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) {
|
||||||
|
next.setDaoConfig(myConfig);
|
||||||
myEntityManager.remove(next);
|
myEntityManager.remove(next);
|
||||||
theEntity.getParamsString().remove(next);
|
theEntity.getParamsString().remove(next);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -20,45 +20,43 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import java.util.Collection;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import java.util.Collections;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import java.util.List;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import java.util.regex.Pattern;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import java.util.Collections;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import java.util.List;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import java.util.regex.Pattern;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
|
||||||
|
|
||||||
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
|
public abstract class BaseSearchParamExtractor implements ISearchParamExtractor {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
|
|
||||||
public static final Pattern SPLIT = Pattern.compile("\\||( or )");
|
|
||||||
|
|
||||||
|
public static final Pattern SPLIT = Pattern.compile("\\||( or )");
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamRegistry mySearchParamRegistry;
|
private ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
|
||||||
public BaseSearchParamExtractor() {
|
public BaseSearchParamExtractor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseSearchParamExtractor(FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
|
public BaseSearchParamExtractor(DaoConfig theDaoConfig, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
|
||||||
myContext = theCtx;
|
myContext = theCtx;
|
||||||
mySearchParamRegistry = theSearchParamRegistry;
|
mySearchParamRegistry = theSearchParamRegistry;
|
||||||
|
myDaoConfig = theDaoConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
public List<PathAndRef> extractResourceLinks(IBaseResource theResource, RuntimeSearchParam theNextSpDef) {
|
||||||
List<PathAndRef> refs = new ArrayList<PathAndRef>();
|
List<PathAndRef> refs = new ArrayList<PathAndRef>();
|
||||||
|
@ -95,20 +93,24 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||||
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: {}", new Object[] { nextPathTrimmed, def.getName(), e.toString(), e } );
|
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: {}", new Object[] {nextPathTrimmed, def.getName(), e.toString(), e});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected FhirContext getContext() {
|
protected FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DaoConfig getDaoConfig() {
|
||||||
|
return myDaoConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
|
public Collection<RuntimeSearchParam> getSearchParams(IBaseResource theResource) {
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||||
Collection<RuntimeSearchParam> retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
|
Collection<RuntimeSearchParam> retVal = mySearchParamRegistry.getActiveSearchParams(def.getName()).values();
|
||||||
List<RuntimeSearchParam> defaultList= Collections.emptyList();
|
List<RuntimeSearchParam> defaultList = Collections.emptyList();
|
||||||
retVal = ObjectUtils.defaultIfNull(retVal, defaultList);
|
retVal = ObjectUtils.defaultIfNull(retVal, defaultList);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@ import java.util.*;
|
||||||
* 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.
|
||||||
|
@ -89,7 +89,7 @@ public class DaoConfig {
|
||||||
/**
|
/**
|
||||||
* update setter javadoc if default changes
|
* update setter javadoc if default changes
|
||||||
*/
|
*/
|
||||||
private boolean myAllowContainsSearches = true;
|
private boolean myAllowContainsSearches = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update setter javadoc if default changes
|
* update setter javadoc if default changes
|
||||||
|
@ -754,7 +754,15 @@ public class DaoConfig {
|
||||||
* If enabled, the server will support the use of :contains searches,
|
* If enabled, the server will support the use of :contains searches,
|
||||||
* which are helpful but can have adverse effects on performance.
|
* which are helpful but can have adverse effects on performance.
|
||||||
* <p>
|
* <p>
|
||||||
* Default is <code>true</code>
|
* Default is <code>false</code> (Note that prior to HAPI FHIR
|
||||||
|
* 3.5.0 the default was <code>true</code>)
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Note: If you change this value after data already has
|
||||||
|
* already been stored in the database, you must for a reindexing
|
||||||
|
* of all data in the database or resources may not be
|
||||||
|
* searchable.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public boolean isAllowContainsSearches() {
|
public boolean isAllowContainsSearches() {
|
||||||
return myAllowContainsSearches;
|
return myAllowContainsSearches;
|
||||||
|
@ -764,12 +772,21 @@ public class DaoConfig {
|
||||||
* If enabled, the server will support the use of :contains searches,
|
* If enabled, the server will support the use of :contains searches,
|
||||||
* which are helpful but can have adverse effects on performance.
|
* which are helpful but can have adverse effects on performance.
|
||||||
* <p>
|
* <p>
|
||||||
* Default is <code>true</code>
|
* Default is <code>false</code> (Note that prior to HAPI FHIR
|
||||||
|
* 3.5.0 the default was <code>true</code>)
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Note: If you change this value after data already has
|
||||||
|
* already been stored in the database, you must for a reindexing
|
||||||
|
* of all data in the database or resources may not be
|
||||||
|
* searchable.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setAllowContainsSearches(boolean theAllowContainsSearches) {
|
public void setAllowContainsSearches(boolean theAllowContainsSearches) {
|
||||||
this.myAllowContainsSearches = theAllowContainsSearches;
|
this.myAllowContainsSearches = theAllowContainsSearches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
* If set to <code>true</code> (default is <code>false</code>) the server will allow
|
||||||
* resources to have references to external servers. For example if this server is
|
* resources to have references to external servers. For example if this server is
|
||||||
|
|
|
@ -61,6 +61,8 @@ import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.hibernate.ScrollMode;
|
import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.ScrollableResults;
|
import org.hibernate.ScrollableResults;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
|
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
|
||||||
|
import org.hibernate.query.criteria.internal.predicate.BooleanStaticAssertionPredicate;
|
||||||
import org.hl7.fhir.dstu3.model.BaseResource;
|
import org.hl7.fhir.dstu3.model.BaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -594,7 +596,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
List<Predicate> codePredicates = new ArrayList<>();
|
||||||
for (IQueryParameterType nextOr : theList) {
|
for (IQueryParameterType nextOr : theList) {
|
||||||
IQueryParameterType theParameter = nextOr;
|
IQueryParameterType theParameter = nextOr;
|
||||||
Predicate singleCode = createPredicateString(theParameter, theResourceName, theParamName, myBuilder, join);
|
Predicate singleCode = createPredicateString(theParameter, theResourceName, theParamName, myBuilder, join);
|
||||||
|
@ -742,7 +744,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
List<Predicate> codePredicates = new ArrayList<>();
|
||||||
for (IQueryParameterType nextOr : theList) {
|
for (IQueryParameterType nextOr : theList) {
|
||||||
|
|
||||||
if (nextOr instanceof TokenParam) {
|
if (nextOr instanceof TokenParam) {
|
||||||
|
@ -1087,6 +1089,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
private Predicate createPredicateString(IQueryParameterType theParameter, String theResourceName, String theParamName, CriteriaBuilder theBuilder,
|
private Predicate createPredicateString(IQueryParameterType theParameter, String theResourceName, String theParamName, CriteriaBuilder theBuilder,
|
||||||
From<?, ResourceIndexedSearchParamString> theFrom) {
|
From<?, ResourceIndexedSearchParamString> theFrom) {
|
||||||
String rawSearchTerm;
|
String rawSearchTerm;
|
||||||
|
DaoConfig daoConfig = myCallingDao.getConfig();
|
||||||
if (theParameter instanceof TokenParam) {
|
if (theParameter instanceof TokenParam) {
|
||||||
TokenParam id = (TokenParam) theParameter;
|
TokenParam id = (TokenParam) theParameter;
|
||||||
if (!id.isText()) {
|
if (!id.isText()) {
|
||||||
|
@ -1097,7 +1100,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
StringParam id = (StringParam) theParameter;
|
StringParam id = (StringParam) theParameter;
|
||||||
rawSearchTerm = id.getValue();
|
rawSearchTerm = id.getValue();
|
||||||
if (id.isContains()) {
|
if (id.isContains()) {
|
||||||
if (!myCallingDao.getConfig().isAllowContainsSearches()) {
|
if (!daoConfig.isAllowContainsSearches()) {
|
||||||
throw new MethodNotAllowedException(":contains modifier is disabled on this server");
|
throw new MethodNotAllowedException(":contains modifier is disabled on this server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1113,22 +1116,34 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
+ ResourceIndexedSearchParamString.MAX_LENGTH + "): " + rawSearchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
String likeExpression = BaseHapiFhirDao.normalizeString(rawSearchTerm);
|
boolean exactMatch = theParameter instanceof StringParam && ((StringParam) theParameter).isExact();
|
||||||
if (theParameter instanceof StringParam &&
|
if (exactMatch) {
|
||||||
((StringParam) theParameter).isContains() &&
|
|
||||||
myCallingDao.getConfig().isAllowContainsSearches()) {
|
// Exact match
|
||||||
likeExpression = createLeftAndRightMatchLikeExpression(likeExpression);
|
|
||||||
|
Long hash = ResourceIndexedSearchParamString.calculateHashExact(theResourceName, theParamName, rawSearchTerm);
|
||||||
|
return theBuilder.equal(theFrom.get("myHashExact").as(Long.class), hash);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
likeExpression = createLeftMatchLikeExpression(likeExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
// Normalized Match
|
||||||
if (theParameter instanceof StringParam && ((StringParam) theParameter).isExact()) {
|
|
||||||
Predicate exactCode = theBuilder.equal(theFrom.get("myValueExact"), rawSearchTerm);
|
|
||||||
singleCode = theBuilder.and(singleCode, exactCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
String normalizedString = BaseHapiFhirDao.normalizeString(rawSearchTerm);
|
||||||
|
String likeExpression;
|
||||||
|
if (theParameter instanceof StringParam &&
|
||||||
|
((StringParam) theParameter).isContains() &&
|
||||||
|
daoConfig.isAllowContainsSearches()) {
|
||||||
|
likeExpression = createLeftAndRightMatchLikeExpression(normalizedString);
|
||||||
|
} else {
|
||||||
|
likeExpression = createLeftMatchLikeExpression(normalizedString);
|
||||||
|
}
|
||||||
|
|
||||||
|
Long hash = ResourceIndexedSearchParamString.calculateHashNormalized(daoConfig, theResourceName, theParamName, normalizedString);
|
||||||
|
Predicate hashCode = theBuilder.equal(theFrom.get("myHashNormalizedPrefix").as(Long.class), hash);
|
||||||
|
Predicate singleCode = theBuilder.like(theFrom.get("myValueNormalized").as(String.class), likeExpression);
|
||||||
|
return theBuilder.and(hashCode, singleCode);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
private List<Predicate> createPredicateTagList(Path<TagDefinition> theDefJoin, CriteriaBuilder theBuilder, TagTypeEnum theTagType, List<Pair<String, String>> theTokens) {
|
||||||
|
@ -1183,7 +1198,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
* Process token modifiers (:in, :below, :above)
|
* Process token modifiers (:in, :below, :above)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
List<VersionIndependentConcept> codes = null;
|
List<VersionIndependentConcept> codes;
|
||||||
if (modifier == TokenParamModifier.IN) {
|
if (modifier == TokenParamModifier.IN) {
|
||||||
codes = myTerminologySvc.expandValueSet(code);
|
codes = myTerminologySvc.expandValueSet(code);
|
||||||
} else if (modifier == TokenParamModifier.ABOVE) {
|
} else if (modifier == TokenParamModifier.ABOVE) {
|
||||||
|
@ -1192,81 +1207,53 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
} else if (modifier == TokenParamModifier.BELOW) {
|
} else if (modifier == TokenParamModifier.BELOW) {
|
||||||
system = determineSystemIfMissing(theParamName, code, system);
|
system = determineSystemIfMissing(theParamName, code, system);
|
||||||
codes = myTerminologySvc.findCodesBelow(system, code);
|
codes = myTerminologySvc.findCodesBelow(system, code);
|
||||||
}
|
|
||||||
|
|
||||||
ArrayList<Predicate> singleCodePredicates = new ArrayList<>();
|
|
||||||
if (codes != null) {
|
|
||||||
|
|
||||||
if (codes.isEmpty()) {
|
|
||||||
|
|
||||||
// This will never match anything
|
|
||||||
Predicate codePredicate = theBuilder.isNull(theFrom.get("myMissing"));
|
|
||||||
singleCodePredicates.add(codePredicate);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
List<Predicate> orPredicates = new ArrayList<Predicate>();
|
|
||||||
Map<String, List<VersionIndependentConcept>> map = new HashMap<String, List<VersionIndependentConcept>>();
|
|
||||||
for (VersionIndependentConcept nextCode : codes) {
|
|
||||||
List<VersionIndependentConcept> systemCodes = map.get(nextCode.getSystem());
|
|
||||||
if (null == systemCodes) {
|
|
||||||
systemCodes = new ArrayList<>();
|
|
||||||
map.put(nextCode.getSystem(), systemCodes);
|
|
||||||
}
|
|
||||||
systemCodes.add(nextCode);
|
|
||||||
}
|
|
||||||
// Use "in" in case of large numbers of codes due to param modifiers
|
|
||||||
final Path<String> systemExpression = theFrom.get("mySystem");
|
|
||||||
final Path<String> valueExpression = theFrom.get("myValue");
|
|
||||||
for (Map.Entry<String, List<VersionIndependentConcept>> entry : map.entrySet()) {
|
|
||||||
Predicate systemPredicate = theBuilder.equal(systemExpression, entry.getKey());
|
|
||||||
In<String> codePredicate = theBuilder.in(valueExpression);
|
|
||||||
for (VersionIndependentConcept nextCode : entry.getValue()) {
|
|
||||||
codePredicate.value(nextCode.getCode());
|
|
||||||
}
|
|
||||||
orPredicates.add(theBuilder.and(systemPredicate, codePredicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
singleCodePredicates.add(theBuilder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
codes = Collections.singletonList(new VersionIndependentConcept(system, code));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
if (codes.isEmpty()) {
|
||||||
* Ok, this is a normal query
|
// This will never match anything
|
||||||
*/
|
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) theBuilder, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(system)) {
|
/*
|
||||||
if (modifier != null && modifier == TokenParamModifier.NOT) {
|
* Note: A null system value means "match any system", but
|
||||||
singleCodePredicates.add(theBuilder.notEqual(theFrom.get("mySystem"), system));
|
* an empty-string system value means "match values that
|
||||||
} else {
|
* explicitly have no system".
|
||||||
singleCodePredicates.add(theBuilder.equal(theFrom.get("mySystem"), system));
|
*/
|
||||||
}
|
boolean haveSystem = codes.get(0).getSystem() != null;
|
||||||
} else if (system == null) {
|
boolean haveCode = isNotBlank(codes.get(0).getCode());
|
||||||
// don't check the system
|
Expression<Long> hashField;
|
||||||
|
if (!haveSystem && !haveCode) {
|
||||||
|
// If we have neither, this isn't actually an expression so
|
||||||
|
// just return 1=1
|
||||||
|
return new BooleanStaticAssertionPredicate((CriteriaBuilderImpl) theBuilder, true);
|
||||||
|
} else if (haveSystem && haveCode) {
|
||||||
|
hashField = theFrom.get("myHashSystemAndValue").as(Long.class);
|
||||||
|
} else if (haveSystem) {
|
||||||
|
hashField = theFrom.get("myHashSystem").as(Long.class);
|
||||||
|
} else {
|
||||||
|
hashField = theFrom.get("myHashValue").as(Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> values = new ArrayList<>(codes.size());
|
||||||
|
for (VersionIndependentConcept next : codes) {
|
||||||
|
if (haveSystem && haveCode) {
|
||||||
|
values.add(ResourceIndexedSearchParamToken.calculateHashSystemAndValue(theResourceName, theParamName, next.getSystem(), next.getCode()));
|
||||||
|
} else if (haveSystem) {
|
||||||
|
values.add(ResourceIndexedSearchParamToken.calculateHashSystem(theResourceName, theParamName, next.getSystem()));
|
||||||
} else {
|
} else {
|
||||||
// If the system is "", we only match on null systems
|
values.add(ResourceIndexedSearchParamToken.calculateHashValue(theResourceName, theParamName, next.getCode()));
|
||||||
singleCodePredicates.add(theBuilder.isNull(theFrom.get("mySystem")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StringUtils.isNotBlank(code)) {
|
|
||||||
if (modifier != null && modifier == TokenParamModifier.NOT) {
|
|
||||||
singleCodePredicates.add(theBuilder.notEqual(theFrom.get("myValue"), code));
|
|
||||||
} else {
|
|
||||||
singleCodePredicates.add(theBuilder.equal(theFrom.get("myValue"), code));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* As of HAPI FHIR 1.5, if the client searched for a token with a system but no specified value this means to
|
|
||||||
* match all tokens with the given value.
|
|
||||||
*
|
|
||||||
* I'm not sure I agree with this, but hey.. FHIR-I voted and this was the result :)
|
|
||||||
*/
|
|
||||||
// singleCodePredicates.add(theBuilder.isNull(theFrom.get("myValue")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Predicate singleCode = theBuilder.and(toArray(singleCodePredicates));
|
Predicate predicate = hashField.in(values);
|
||||||
return combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, theFrom, singleCode);
|
if (modifier == TokenParamModifier.NOT) {
|
||||||
|
Predicate identityPredicate = theBuilder.equal(theFrom.get("myHashIdentity").as(Long.class), ResourceIndexedSearchParamToken.calculateHashIdentity(theResourceName, theParamName));
|
||||||
|
Predicate disjunctionPredicate = theBuilder.not(predicate);
|
||||||
|
predicate = theBuilder.and(identityPredicate, disjunctionPredicate);
|
||||||
|
}
|
||||||
|
return predicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
|
||||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchParamExtractorDstu3(FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
|
public SearchParamExtractorDstu3(DaoConfig theDaoConfig, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
|
||||||
super(theCtx, theSearchParamRegistry);
|
super(theDaoConfig, theCtx, theSearchParamRegistry);
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
|
||||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,8 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchParamExtractorR4(FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
|
public SearchParamExtractorR4(DaoConfig theDaoConfig, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
|
||||||
super(theCtx, theSearchParamRegistry);
|
super(theDaoConfig, theCtx, theSearchParamRegistry);
|
||||||
myValidationSupport = theValidationSupport;
|
myValidationSupport = theValidationSupport;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
searchTerm = searchTerm.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), resourceName, BaseHapiFhirDao.normalizeString(searchTerm), searchTerm);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
|
||||||
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
|
||||||
}
|
}
|
||||||
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(getDaoConfig(), nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
|
||||||
nextEntity.setResource(theEntity);
|
nextEntity.setResource(theEntity);
|
||||||
retVal.add(nextEntity);
|
retVal.add(nextEntity);
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IBaseResource theResource) {
|
||||||
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
|
HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<>();
|
||||||
|
|
||||||
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
Collection<RuntimeSearchParam> searchParams = getSearchParams(theResource);
|
||||||
for (RuntimeSearchParam nextSpDef : searchParams) {
|
for (RuntimeSearchParam nextSpDef : searchParams) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.Date;
|
||||||
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
public abstract class BaseResourceIndexedSearchParam implements Serializable {
|
||||||
/** Don't change this without careful consideration. You will break existing hashes! */
|
/** Don't change this without careful consideration. You will break existing hashes! */
|
||||||
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128(0);
|
||||||
/** Don't make this public 'cause nobody better touch it! */
|
/** Don't make this public 'cause nobody better be able to modify it! */
|
||||||
private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8);
|
private static final byte[] DELIMITER_BYTES = "|".getBytes(Charsets.UTF_8);
|
||||||
|
|
||||||
static final int MAX_SP_NAME = 100;
|
static final int MAX_SP_NAME = 100;
|
||||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* 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.
|
||||||
|
@ -33,7 +33,6 @@ import org.hibernate.search.annotations.NumericField;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@Embeddable
|
@Embeddable
|
||||||
|
@ -66,15 +65,15 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
/**
|
/**
|
||||||
* @since 3.4.0 - At some point this should be made not-null
|
* @since 3.5.0 - At some point this should be made not-null
|
||||||
*/
|
*/
|
||||||
@Column(name = "HASH_UNITS_AND_VALPREFIX", nullable = true)
|
@Column(name = "HASH_IDENTITY_AND_UNITS", nullable = true)
|
||||||
private Long myHashUnitsAndValPrefix;
|
private Long myHashIdentityAndUnits;
|
||||||
/**
|
/**
|
||||||
* @since 3.4.0 - At some point this should be made not-null
|
* @since 3.5.0 - At some point this should be made not-null
|
||||||
*/
|
*/
|
||||||
@Column(name = "HASH_VALPREFIX", nullable = true)
|
@Column(name = "HASH_IDENTITY", nullable = true)
|
||||||
private Long myHashValPrefix;
|
private Long myHashIdentity;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamQuantity() {
|
public ResourceIndexedSearchParamQuantity() {
|
||||||
// nothing
|
// nothing
|
||||||
|
@ -89,16 +88,16 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
public void calculateHashes() {
|
public void calculateHashes() {
|
||||||
if (myHashUnitsAndValPrefix == null) {
|
if (myHashIdentity == null) {
|
||||||
setHashUnitsAndValPrefix(hash(getResourceType(), getParamName(), getSystem(), getUnits(), toTruncatedString(getValue())));
|
setHashIdentity(hash(getResourceType(), getParamName()));
|
||||||
setHashValPrefix(hash(getResourceType(), getParamName(), toTruncatedString(getValue())));
|
setHashIdentityAndUnits(hash(getResourceType(), getParamName(), getSystem(), getUnits()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearHashes() {
|
protected void clearHashes() {
|
||||||
myHashUnitsAndValPrefix = null;
|
myHashIdentity = null;
|
||||||
myHashValPrefix = null;
|
myHashIdentityAndUnits = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,27 +118,25 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
b.append(getSystem(), obj.getSystem());
|
b.append(getSystem(), obj.getSystem());
|
||||||
b.append(getUnits(), obj.getUnits());
|
b.append(getUnits(), obj.getUnits());
|
||||||
b.append(getValue(), obj.getValue());
|
b.append(getValue(), obj.getValue());
|
||||||
b.append(getHashUnitsAndValPrefix(), obj.getHashUnitsAndValPrefix());
|
|
||||||
b.append(getHashValPrefix(), obj.getHashValPrefix());
|
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getHashUnitsAndValPrefix() {
|
public Long getHashIdentity() {
|
||||||
calculateHashes();
|
calculateHashes();
|
||||||
return myHashUnitsAndValPrefix;
|
return myHashIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHashUnitsAndValPrefix(Long theHashUnitsAndValPrefix) {
|
public void setHashIdentity(Long theHashIdentity) {
|
||||||
myHashUnitsAndValPrefix = theHashUnitsAndValPrefix;
|
myHashIdentity = theHashIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getHashValPrefix() {
|
public Long getHashIdentityAndUnits() {
|
||||||
calculateHashes();
|
calculateHashes();
|
||||||
return myHashValPrefix;
|
return myHashIdentityAndUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHashValPrefix(Long theHashValPrefix) {
|
public void setHashIdentityAndUnits(Long theHashIdentityAndUnits) {
|
||||||
myHashValPrefix = theHashValPrefix;
|
myHashIdentityAndUnits = theHashIdentityAndUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -176,14 +173,13 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
calculateHashes();
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
|
b.append(getResourceType());
|
||||||
b.append(getParamName());
|
b.append(getParamName());
|
||||||
b.append(getResource());
|
|
||||||
b.append(getSystem());
|
b.append(getSystem());
|
||||||
b.append(getUnits());
|
b.append(getUnits());
|
||||||
b.append(getValue());
|
b.append(getValue());
|
||||||
b.append(getHashUnitsAndValPrefix());
|
|
||||||
b.append(getHashValPrefix());
|
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,11 +200,4 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toTruncatedString(BigDecimal theValue) {
|
|
||||||
if (theValue == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return theValue.setScale(0, RoundingMode.FLOOR).toPlainString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* 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.
|
||||||
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -38,7 +39,14 @@ import static org.apache.commons.lang3.StringUtils.left;
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_STRING", indexes = {
|
@Table(name = "HFJ_SPIDX_STRING", indexes = {
|
||||||
@Index(name = "IDX_SP_STRING", columnList = "RES_TYPE,SP_NAME,SP_VALUE_NORMALIZED"),
|
/*
|
||||||
|
* Note: We previously had indexes with the following names,
|
||||||
|
* do not reuse these names:
|
||||||
|
* IDX_SP_STRING
|
||||||
|
*/
|
||||||
|
@Index(name = "IDX_SP_STRING_HASH_NRM", columnList = "HASH_NORM_PREFIX,SP_VALUE_NORMALIZED"),
|
||||||
|
@Index(name = "IDX_SP_STRING_HASH_EXCT", columnList = "HASH_EXACT"),
|
||||||
|
|
||||||
@Index(name = "IDX_SP_STRING_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_STRING_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
|
@Index(name = "IDX_SP_STRING_RESID", columnList = "RES_ID")
|
||||||
})
|
})
|
||||||
|
@ -127,13 +135,16 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
*/
|
*/
|
||||||
@Column(name = "HASH_EXACT", nullable = true)
|
@Column(name = "HASH_EXACT", nullable = true)
|
||||||
private Long myHashExact;
|
private Long myHashExact;
|
||||||
|
@Transient
|
||||||
|
private transient DaoConfig myDaoConfig;
|
||||||
|
|
||||||
public ResourceIndexedSearchParamString() {
|
public ResourceIndexedSearchParamString() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ResourceIndexedSearchParamString(String theName, String theValueNormalized, String theValueExact) {
|
public ResourceIndexedSearchParamString(DaoConfig theDaoConfig, String theName, String theValueNormalized, String theValueExact) {
|
||||||
|
setDaoConfig(theDaoConfig);
|
||||||
setParamName(theName);
|
setParamName(theName);
|
||||||
setValueNormalized(theValueNormalized);
|
setValueNormalized(theValueNormalized);
|
||||||
setValueExact(theValueExact);
|
setValueExact(theValueExact);
|
||||||
|
@ -142,8 +153,12 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
@PrePersist
|
@PrePersist
|
||||||
public void calculateHashes() {
|
public void calculateHashes() {
|
||||||
if (myHashNormalizedPrefix == null) {
|
if (myHashNormalizedPrefix == null) {
|
||||||
setHashNormalizedPrefix(hash(getResourceType(), getParamName(), left(getValueNormalized(), HASH_PREFIX_LENGTH)));
|
String resourceType = getResourceType();
|
||||||
setHashExact(hash(getResourceType(), getParamName(), getValueExact()));
|
String paramName = getParamName();
|
||||||
|
String valueNormalized = getValueNormalized();
|
||||||
|
String valueExact = getValueExact();
|
||||||
|
setHashNormalizedPrefix(calculateHashNormalized(myDaoConfig, resourceType, paramName, valueNormalized));
|
||||||
|
setHashExact(calculateHashExact(resourceType, paramName, valueExact));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +184,6 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
b.append(getParamName(), obj.getParamName());
|
b.append(getParamName(), obj.getParamName());
|
||||||
b.append(getResource(), obj.getResource());
|
b.append(getResource(), obj.getResource());
|
||||||
b.append(getValueExact(), obj.getValueExact());
|
b.append(getValueExact(), obj.getValueExact());
|
||||||
b.append(getHashNormalizedPrefix(), obj.getHashNormalizedPrefix());
|
|
||||||
b.append(getHashExact(), obj.getHashExact());
|
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +192,11 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
return myHashExact;
|
return myHashExact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BaseResourceIndexedSearchParam setDaoConfig(DaoConfig theDaoConfig) {
|
||||||
|
myDaoConfig = theDaoConfig;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHashExact(Long theHashExact) {
|
public void setHashExact(Long theHashExact) {
|
||||||
myHashExact = theHashExact;
|
myHashExact = theHashExact;
|
||||||
}
|
}
|
||||||
|
@ -221,12 +239,11 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
calculateHashes();
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
b.append(getParamName());
|
b.append(getParamName());
|
||||||
b.append(getResource());
|
b.append(getResource());
|
||||||
b.append(getValueExact());
|
b.append(getValueExact());
|
||||||
b.append(getHashNormalizedPrefix());
|
|
||||||
b.append(getHashExact());
|
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,4 +261,23 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long calculateHashExact(String theResourceType, String theParamName, String theValueExact) {
|
||||||
|
return hash(theResourceType, theParamName, theValueExact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateHashNormalized(DaoConfig theDaoConfig, String theResourceType, String theParamName, String theValueNormalized) {
|
||||||
|
/*
|
||||||
|
* If we're not allowing contained searches, we'll add the first
|
||||||
|
* bit of the normalized value to the hash. This helps to
|
||||||
|
* make the hash even more unique, which will be good for
|
||||||
|
* performance.
|
||||||
|
*/
|
||||||
|
int hashPrefixLength = HASH_PREFIX_LENGTH;
|
||||||
|
if (theDaoConfig.isAllowContainsSearches()) {
|
||||||
|
hashPrefixLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash(theResourceType, theParamName, left(theValueNormalized, hashPrefixLength));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* 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.
|
||||||
|
@ -31,11 +31,23 @@ import org.hibernate.search.annotations.Field;
|
||||||
|
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.trim;
|
||||||
|
|
||||||
@Embeddable
|
@Embeddable
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_TOKEN", indexes = {
|
@Table(name = "HFJ_SPIDX_TOKEN", indexes = {
|
||||||
@Index(name = "IDX_SP_TOKEN", columnList = "RES_TYPE,SP_NAME,SP_SYSTEM,SP_VALUE"),
|
/*
|
||||||
@Index(name = "IDX_SP_TOKEN_UNQUAL", columnList = "RES_TYPE,SP_NAME,SP_VALUE"),
|
* Note: We previously had indexes with the following names,
|
||||||
|
* do not reuse these names:
|
||||||
|
* IDX_SP_TOKEN
|
||||||
|
* IDX_SP_TOKEN_UNQUAL
|
||||||
|
*/
|
||||||
|
@Index(name = "IDX_SP_TOKEN_HASH", columnList = "HASH_IDENTITY"),
|
||||||
|
@Index(name = "IDX_SP_TOKEN_HASH_S", columnList = "HASH_SYS"),
|
||||||
|
@Index(name = "IDX_SP_TOKEN_HASH_SV", columnList = "HASH_SYS_AND_VALUE"),
|
||||||
|
@Index(name = "IDX_SP_TOKEN_HASH_V", columnList = "HASH_VALUE"),
|
||||||
|
|
||||||
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
@Index(name = "IDX_SP_TOKEN_UPDATED", columnList = "SP_UPDATED"),
|
||||||
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
@Index(name = "IDX_SP_TOKEN_RESID", columnList = "RES_ID")
|
||||||
})
|
})
|
||||||
|
@ -56,6 +68,11 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_TOKEN")
|
||||||
@Column(name = "SP_ID")
|
@Column(name = "SP_ID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
/**
|
||||||
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
|
*/
|
||||||
|
@Column(name = "HASH_IDENTITY", nullable = true)
|
||||||
|
private Long myHashIdentity;
|
||||||
/**
|
/**
|
||||||
* @since 3.4.0 - At some point this should be made not-null
|
* @since 3.4.0 - At some point this should be made not-null
|
||||||
*/
|
*/
|
||||||
|
@ -90,17 +107,20 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
setValue(theValue);
|
setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PrePersist
|
@PrePersist
|
||||||
public void calculateHashes() {
|
public void calculateHashes() {
|
||||||
if (myHashSystem == null) {
|
if (myHashSystem == null) {
|
||||||
setHashSystem(hash(getResourceType(), getParamName(), getSystem()));
|
String resourceType = getResourceType();
|
||||||
setHashSystemAndValue(hash(getResourceType(), getParamName(), getSystem(), getValue()));
|
String paramName = getParamName();
|
||||||
setHashValue(hash(getResourceType(), getParamName(), getValue()));
|
String system = getSystem();
|
||||||
|
String value = getValue();
|
||||||
|
setHashIdentity(calculateHashIdentity(resourceType, paramName));
|
||||||
|
setHashSystem(calculateHashSystem(resourceType, paramName, system));
|
||||||
|
setHashSystemAndValue(calculateHashSystemAndValue(resourceType, paramName, system, value));
|
||||||
|
setHashValue(calculateHashValue(resourceType, paramName, value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void clearHashes() {
|
protected void clearHashes() {
|
||||||
myHashSystem = null;
|
myHashSystem = null;
|
||||||
|
@ -125,9 +145,6 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
b.append(getResource(), obj.getResource());
|
b.append(getResource(), obj.getResource());
|
||||||
b.append(getSystem(), obj.getSystem());
|
b.append(getSystem(), obj.getSystem());
|
||||||
b.append(getValue(), obj.getValue());
|
b.append(getValue(), obj.getValue());
|
||||||
b.append(getHashSystem(), obj.getHashSystem());
|
|
||||||
b.append(getHashSystemAndValue(), obj.getHashSystemAndValue());
|
|
||||||
b.append(getHashValue(), obj.getHashValue());
|
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +153,15 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
return myHashSystem;
|
return myHashSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getHashIdentity() {
|
||||||
|
calculateHashes();
|
||||||
|
return myHashIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHashIdentity(Long theHashIdentity) {
|
||||||
|
myHashIdentity = theHashIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
public void setHashSystem(Long theHashSystem) {
|
public void setHashSystem(Long theHashSystem) {
|
||||||
myHashSystem = theHashSystem;
|
myHashSystem = theHashSystem;
|
||||||
}
|
}
|
||||||
|
@ -184,18 +210,15 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
|
calculateHashes();
|
||||||
HashCodeBuilder b = new HashCodeBuilder();
|
HashCodeBuilder b = new HashCodeBuilder();
|
||||||
b.append(getParamName());
|
b.append(getParamName());
|
||||||
b.append(getResource());
|
b.append(getResource());
|
||||||
b.append(getSystem());
|
b.append(getSystem());
|
||||||
b.append(getValue());
|
b.append(getValue());
|
||||||
b.append(getHashSystem());
|
|
||||||
b.append(getHashSystemAndValue());
|
|
||||||
b.append(getHashValue());
|
|
||||||
return b.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IQueryParameterType toQueryParameterType() {
|
public IQueryParameterType toQueryParameterType() {
|
||||||
return new TokenParam(getSystem(), getValue());
|
return new TokenParam(getSystem(), getValue());
|
||||||
|
@ -210,4 +233,20 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
||||||
b.append("value", getValue());
|
b.append("value", getValue());
|
||||||
return b.build();
|
return b.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long calculateHashSystem(String theResourceType, String theParamName, String theSystem) {
|
||||||
|
return hash(theResourceType, theParamName, trim(theSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateHashIdentity(String theResourceType, String theParamName) {
|
||||||
|
return hash(theResourceType, theParamName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateHashSystemAndValue(String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||||
|
return hash(theResourceType, theParamName, defaultString(trim(theSystem)), trim(theValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateHashValue(String theResourceType, String theParamName, String theValue) {
|
||||||
|
return hash(theResourceType, theParamName, trim(theValue));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,14 +66,12 @@ public class ResourceLink implements Serializable {
|
||||||
|
|
||||||
@ManyToOne(optional = false, fetch=FetchType.LAZY)
|
@ManyToOne(optional = false, fetch=FetchType.LAZY)
|
||||||
@JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey=@ForeignKey(name="FK_RESLINK_SOURCE"))
|
@JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey=@ForeignKey(name="FK_RESLINK_SOURCE"))
|
||||||
// @ContainedIn()
|
|
||||||
private ResourceTable mySourceResource;
|
private ResourceTable mySourceResource;
|
||||||
|
|
||||||
@Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
|
@Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
|
||||||
private Long mySourceResourcePid;
|
private Long mySourceResourcePid;
|
||||||
|
|
||||||
@Column(name = "SOURCE_RESOURCE_TYPE", nullable=false, length=ResourceTable.RESTYPE_LEN)
|
@Column(name = "SOURCE_RESOURCE_TYPE", nullable=false, length=ResourceTable.RESTYPE_LEN)
|
||||||
@ColumnDefault("''") // TODO: remove this (it's only here for simplifying upgrades of 1.3 -> 1.4)
|
|
||||||
@Field()
|
@Field()
|
||||||
private String mySourceResourceType;
|
private String mySourceResourceType;
|
||||||
|
|
||||||
|
@ -86,7 +84,6 @@ public class ResourceLink implements Serializable {
|
||||||
private Long myTargetResourcePid;
|
private Long myTargetResourcePid;
|
||||||
|
|
||||||
@Column(name = "TARGET_RESOURCE_TYPE", nullable=false, length=ResourceTable.RESTYPE_LEN)
|
@Column(name = "TARGET_RESOURCE_TYPE", nullable=false, length=ResourceTable.RESTYPE_LEN)
|
||||||
@ColumnDefault("''") // TODO: remove this (it's only here for simplifying upgrades of 1.3 -> 1.4)
|
|
||||||
@Field()
|
@Field()
|
||||||
private String myTargetResourceType;
|
private String myTargetResourceType;
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
|
|
||||||
DataSource dataSource = ProxyDataSourceBuilder
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
.create(retVal)
|
.create(retVal)
|
||||||
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||||
.countQuery(new ThreadQueryCountHolder())
|
.countQuery(new ThreadQueryCountHolder())
|
||||||
.build();
|
.build();
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
|
||||||
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
import org.hl7.fhir.dstu3.hapi.validation.DefaultProfileValidationSupport;
|
||||||
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
|
||||||
|
@ -81,7 +82,7 @@ public class SearchParamExtractorDstu3Test {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(ourCtx, ourValidationSupport, searchParamRegistry);
|
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new DaoConfig(), ourCtx, ourValidationSupport, searchParamRegistry);
|
||||||
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
|
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
|
||||||
assertEquals(1, tokens.size());
|
assertEquals(1, tokens.size());
|
||||||
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
|
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
|
||||||
|
|
|
@ -257,6 +257,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis());
|
myDaoConfig.setExpireSearchResultsAfterMillis(new DaoConfig().getExpireSearchResultsAfterMillis());
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||||
myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange());
|
myDaoConfig.setSuppressUpdatesWithNoChange(new DaoConfig().isSuppressUpdatesWithNoChange());
|
||||||
|
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
|
|
@ -2261,6 +2261,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithContains() {
|
public void testSearchWithContains() {
|
||||||
|
myDaoConfig.setAllowContainsSearches(true);
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("ABCDEFGHIJK");
|
pt1.addName().setFamily("ABCDEFGHIJK");
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
|
||||||
|
@ -80,7 +81,7 @@ public class SearchParamExtractorR4Test {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(ourCtx, ourValidationSupport, searchParamRegistry);
|
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new DaoConfig(), ourCtx, ourValidationSupport, searchParamRegistry);
|
||||||
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
|
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(new ResourceTable(), obs);
|
||||||
assertEquals(1, tokens.size());
|
assertEquals(1, tokens.size());
|
||||||
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
|
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
|
||||||
|
|
|
@ -19,19 +19,8 @@ public class ResourceIndexedSearchParamQuantityTest {
|
||||||
ResourceIndexedSearchParamQuantity token = createParam("NAME", "123.001", "value", "VALUE");
|
ResourceIndexedSearchParamQuantity token = createParam("NAME", "123.001", "value", "VALUE");
|
||||||
|
|
||||||
// Make sure our hashing function gives consistent results
|
// Make sure our hashing function gives consistent results
|
||||||
assertEquals(945335027461836896L, token.getHashUnitsAndValPrefix().longValue());
|
assertEquals(834432764963581074L, token.getHashIdentity().longValue());
|
||||||
assertEquals(5549105497508660145L, token.getHashValPrefix().longValue());
|
assertEquals(3029184989308001259L, token.getHashIdentityAndUnits().longValue());
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValueTrimming() {
|
|
||||||
assertEquals(7265149425397186226L, createParam("NAME", "401.001", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
|
||||||
assertEquals(7265149425397186226L, createParam("NAME", "401.99999", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
|
||||||
assertEquals(7265149425397186226L, createParam("NAME", "401", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
|
||||||
// Should be different
|
|
||||||
assertEquals(-8387917096585386046L, createParam("NAME", "400.9999999", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
|
||||||
// Should be different
|
|
||||||
assertEquals(8819656626732693650L, createParam("NAME", "402.000000", "value", "VALUE").getHashUnitsAndValPrefix().longValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
package ca.uhn.fhir.jpa.entity;
|
package ca.uhn.fhir.jpa.entity;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@SuppressWarnings("SpellCheckingInspection")
|
||||||
public class ResourceIndexedSearchParamStringTest {
|
public class ResourceIndexedSearchParamStringTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHashFunctions() {
|
public void testHashFunctions() {
|
||||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "value", "VALUE");
|
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new DaoConfig(), "NAME", "value", "VALUE");
|
||||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
// Make sure our hashing function gives consistent results
|
// Make sure our hashing function gives consistent results
|
||||||
|
@ -18,7 +20,7 @@ public class ResourceIndexedSearchParamStringTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHashFunctionsPrefixOnly() {
|
public void testHashFunctionsPrefixOnly() {
|
||||||
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString("NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ");
|
ResourceIndexedSearchParamString token = new ResourceIndexedSearchParamString(new DaoConfig(), "NAME", "vZZZZZZZZZZZZZZZZ", "VZZZZZZzzzZzzzZ");
|
||||||
token.setResource(new ResourceTable().setResourceType("Patient"));
|
token.setResource(new ResourceTable().setResourceType("Patient"));
|
||||||
|
|
||||||
// Should be the same as in testHashFunctions()
|
// Should be the same as in testHashFunctions()
|
||||||
|
|
|
@ -3159,16 +3159,13 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
testSearchWithEmptyParameter("/Observation?code=bar&value-concept=");
|
testSearchWithEmptyParameter("/Observation?code=bar&value-concept=");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSearchWithEmptyParameter(String url) throws IOException {
|
private void testSearchWithEmptyParameter(String theUrl) throws IOException {
|
||||||
HttpGet get = new HttpGet(ourServerBase + url);
|
HttpGet get = new HttpGet(ourServerBase + theUrl);
|
||||||
CloseableHttpResponse resp = ourHttpClient.execute(get);
|
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
|
||||||
try {
|
|
||||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||||
String respString = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8);
|
String respString = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8);
|
||||||
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, respString);
|
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, respString);
|
||||||
assertEquals(1, bundle.getEntry().size());
|
assertEquals(1, bundle.getEntry().size());
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(resp.getEntity().getContent());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3571,14 +3571,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
private void testSearchWithEmptyParameter(String url) throws IOException {
|
private void testSearchWithEmptyParameter(String url) throws IOException {
|
||||||
HttpGet get = new HttpGet(ourServerBase + url);
|
HttpGet get = new HttpGet(ourServerBase + url);
|
||||||
CloseableHttpResponse resp = ourHttpClient.execute(get);
|
try (CloseableHttpResponse resp = ourHttpClient.execute(get)) {
|
||||||
try {
|
|
||||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||||
String respString = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8);
|
String respString = IOUtils.toString(resp.getEntity().getContent(), Constants.CHARSET_UTF8);
|
||||||
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, respString);
|
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, respString);
|
||||||
assertEquals(1, bundle.getEntry().size());
|
assertEquals(1, bundle.getEntry().size());
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(resp.getEntity().getContent());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue