Make quantity indexes use double datatype (#2290)

* Make quantity indexes use double datatype

* Add changelog

* Test fixes

* Test fixes

* Disable test
This commit is contained in:
James Agnew 2021-01-16 18:44:15 -05:00 committed by GitHub
parent 24ac055b50
commit 1dc4fb2206
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 900 additions and 673 deletions

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.param;
import java.math.BigDecimal;
import java.util.List;
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -301,4 +302,22 @@ public class QuantityParam extends BaseParamWithPrefix<QuantityParam> implements
return b.toString();
}
public static QuantityParam toQuantityParam(IQueryParameterType theParam) {
if (theParam instanceof BaseQuantityDt) {
BaseQuantityDt param = (BaseQuantityDt) theParam;
String systemValue = param.getSystemElement().getValueAsString();
String unitsValue = param.getUnitsElement().getValueAsString();
ParamPrefixEnum cmpValue = ParamPrefixEnum.forValue(param.getComparatorElement().getValueAsString());
BigDecimal valueValue = param.getValueElement().getValue();
return new QuantityParam()
.setSystem(systemValue)
.setUnits(unitsValue)
.setPrefix(cmpValue)
.setValue(valueValue);
} else if (theParam instanceof QuantityParam) {
return (QuantityParam) theParam;
} else {
throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass());
}
}
}

View File

@ -0,0 +1,6 @@
---
type: change
issue: 2290
title: "In the JPA server. the SQL datatype used to index quantities has been changed from `NUMBER(19,2)` to
`double precision` (or equivalents depending on platform). This improves the query support for ssearching on
very small quantities."

View File

@ -2,18 +2,18 @@
- item:
type: "add"
title: "The version of a few dependencies have been bumped to the latest versions
(dependent HAPI modules listed in brackets):
<ul>
<li>SLF4j (All Modules): 1.7.28 -&gt; 1.7.30</li>
<li>Woodstox (XML FHIR Parser): 4.4.1 -&gt; 6.2.3 (Note that the Maven groupId has changed from <code>org.codehaus.woodstox</code> to <code>com.fasterxml.woodstox</code> and the Maven artifactId has changed from <code>woodstox-core-asl</code> to <code>woodstox-core</code> for this library)</li>
<li>Spring (JPA): 5.2.3.RELEASE -&gt; 5.2.9.RELEASE</li>
<li>Datasource-Proxy (JPA): 1.5.1 -&gt; 1.7</li>
<li>Jetty (JPA Starter): 9.4.30.v20200611 -&gt; 9.4.35.v20201120</li>
<li>Guava (JP): 29.0-jre -&gt; 30.1-jre</li>
<li>Hibernate ORM (JPA Server): 5.4.22.FINAL -&gt; 5.4.26.FINAL</li>
<li>Spring (JPA Server): 5.2.9.RELEASE -&gt; 5.3.2</li>
<li>Spring Data (JPA Server): 2.2.0.RELEASE -&gt; 2.4.2</li>
<li>Hibernate Search (JPA Server): 5.11.5.FINAL -&gt; 6.0.0.Final</li>
<li>Lucene(HAPI FHIR JPA Server): 5.5.5 -&gt; 8.7.0</li>
<li>Spring Boot (JPA Starter): 2.2.6.RELEASE -&gt; 2.4.1</li>
</ul>"
(dependent HAPI modules listed in brackets):
* SLF4j (All Modules): 1.7.28 -> 1.7.30
* Jackson (All Modules): 2.11.2 -> 2.12.1
* Woodstox (XML FHIR Parser): 4.4.1 -> 6.2.3 (Note that the Maven groupId has changed from `org.codehaus.woodstox` to `com.fasterxml.woodstox` and the Maven artifactId has changed from `woodstox-core-asl` to `woodstox-core` for this library)
* Spring (JPA): 5.2.3.RELEASE -> 5.2.9.RELEASE
* Datasource-Proxy (JPA): 1.5.1 -> 1.7
* Jetty (JPA Starter): 9.4.30.v20200611 -> 9.4.35.v20201120
* Guava (JP): 29.0-jre -> 30.1-jre
* Hibernate ORM (JPA Server): 5.4.22.FINAL -> 5.4.26.FINAL
* Spring (JPA Server): 5.2.9.RELEASE -> 5.3.2
* Spring Data (JPA Server): 2.2.0.RELEASE -> 2.4.2
* Hibernate Search (JPA Server): 5.11.5.FINAL -> 6.0.0.Final
* Lucene(HAPI FHIR JPA Server): 5.5.5 -> 8.7.0
* Spring Boot (JPA Starter): 2.2.6.RELEASE -> 2.4.1
"

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
@ -27,7 +28,7 @@ import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface IResourceIndexedSearchParamQuantityNormalizedDao extends JpaRepository<ResourceIndexedSearchParamQuantity, Long> {
public interface IResourceIndexedSearchParamQuantityNormalizedDao extends JpaRepository<ResourceIndexedSearchParamQuantityNormalized, Long> {
@Modifying
@Query("delete from ResourceIndexedSearchParamQuantityNormalized t WHERE t.myResourcePid = :resid")
void deleteByResourceId(@Param("resid") Long theResourcePid);

View File

@ -31,7 +31,9 @@ import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderToken;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder;
@ -43,12 +45,12 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ResourceIdPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceLinkPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.SearchParamPresentPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.SourcePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.TagPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.UriPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -185,14 +187,9 @@ public class QueryStack {
public void addSortOnQuantity(String theResourceName, String theParamName, boolean theAscending) {
BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder();
QuantityBasePredicateBuilder sortPredicateBuilder = null;
if (myModelConfig.isNormalizedQuantitySearchSupported()) {
sortPredicateBuilder = mySqlBuilder.addQuantityNormalizedPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
} else {
sortPredicateBuilder = mySqlBuilder.addQuantityPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
}
QuantityBasePredicateBuilder sortPredicateBuilder;
sortPredicateBuilder = mySqlBuilder.addQuantityPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
Condition hashIdentityPredicate = sortPredicateBuilder.createHashIdentityPredicate(theResourceName, theParamName);
mySqlBuilder.addPredicate(hashIdentityPredicate);
@ -272,33 +269,33 @@ public class QueryStack {
return new PredicateBuilderCacheLookupResult<>(cacheHit, (T) retVal);
}
private Condition createPredicateComposite(@Nullable DbColumn theSourceJoinColumn, String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
private Condition createPredicateComposite(@Nullable DbColumn theSourceJoinColumn, String theResourceName, RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theNextAnd, RequestPartitionId theRequestPartitionId) {
Condition orCondidtion = null;
Condition orCondidtion = null;
for (IQueryParameterType next : theNextAnd) {
if (!(next instanceof CompositeParam<?, ?>)) {
throw new InvalidRequestException("Invalid type for composite param (must be " + CompositeParam.class.getSimpleName() + ": " + next.getClass());
}
CompositeParam<?, ?> cp = (CompositeParam<?, ?>) next;
RuntimeSearchParam left = theParamDef.getCompositeOf().get(0);
IQueryParameterType leftValue = cp.getLeftValue();
Condition leftPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, left, leftValue, theRequestPartitionId);
RuntimeSearchParam right = theParamDef.getCompositeOf().get(1);
IQueryParameterType rightValue = cp.getRightValue();
Condition rightPredicate = createPredicateCompositePart(theSourceJoinColumn, theResourceName, right, rightValue, theRequestPartitionId);
Condition andCondition = toAndPredicate(leftPredicate, rightPredicate);
if (orCondidtion == null) {
orCondidtion = toOrPredicate(andCondition);
} else {
orCondidtion = toOrPredicate(orCondidtion, andCondition);
}
}
return orCondidtion;
}
@ -414,11 +411,11 @@ public class QueryStack {
param.setValueAsQueryToken(null, null, null, theFilter.getValue());
return theQueryStack3.createPredicateResourceId(null, Collections.singletonList(Collections.singletonList(param)), theResourceName, theFilter.getOperation(), theRequestPartitionId);
} else if (paramName.equals(IAnyResource.SP_RES_LANGUAGE)) {
return theQueryStack3.createPredicateLanguage(Collections.singletonList(Collections.singletonList(new StringParam(theFilter.getValue()))), theFilter.getOperation());
return theQueryStack3.createPredicateLanguage(Collections.singletonList(Collections.singletonList(new StringParam(theFilter.getValue()))), theFilter.getOperation());
} else if (paramName.equals(Constants.PARAM_SOURCE)) {
TokenParam param = new TokenParam();
param.setValueAsQueryToken(null, null, null, theFilter.getValue());
return createPredicateSource(null, Collections.singletonList(param));
TokenParam param = new TokenParam();
param.setValueAsQueryToken(null, null, null, theFilter.getValue());
return createPredicateSource(null, Collections.singletonList(param));
} else {
RuntimeSearchParam searchParam = mySearchParamRegistry.getActiveSearchParam(theResourceName, paramName);
if (searchParam == null) {
@ -642,20 +639,38 @@ public class QueryStack {
List<? extends IQueryParameterType> theList,
SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) {
QuantityBasePredicateBuilder join = null;
if (myModelConfig.isNormalizedQuantitySearchSupported()) {
join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityNormalizedPredicateBuilder(theSourceJoinColumn)).getResult();
} else {
join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
}
if (theList.get(0).getMissing() != null) {
QuantityBasePredicateBuilder join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
return join.createPredicateParamMissingForNonReference(theResourceName, theSearchParam.getName(), theList.get(0).getMissing(), theRequestPartitionId);
}
List<QuantityParam> quantityParams = theList
.stream()
.map(t -> QuantityParam.toQuantityParam(t))
.collect(Collectors.toList());
QuantityBasePredicateBuilder join = null;
boolean normalizedSearchEnabled = myModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
if (normalizedSearchEnabled) {
List<QuantityParam> normalizedQuantityParams = quantityParams
.stream()
.map(t -> UcumServiceUtil.toCanonicalQuantityOrNull(t))
.filter(t -> t != null)
.collect(Collectors.toList());
if (normalizedQuantityParams.size() == quantityParams.size()) {
join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityNormalizedPredicateBuilder(theSourceJoinColumn)).getResult();
quantityParams = normalizedQuantityParams;
}
}
if (join == null) {
join = createOrReusePredicateBuilder(PredicateBuilderTypeEnum.QUANTITY, theSourceJoinColumn, theSearchParam.getName(), () -> mySqlBuilder.addQuantityPredicateBuilder(theSourceJoinColumn)).getResult();
}
List<Condition> codePredicates = new ArrayList<>();
for (IQueryParameterType nextOr : theList) {
for (QuantityParam nextOr : quantityParams) {
Condition singleCode = join.createPredicateQuantity(nextOr, theResourceName, theSearchParam.getName(), null, join, theOperation, theRequestPartitionId);
codePredicates.add(singleCode);
}
@ -923,7 +938,7 @@ public class QueryStack {
@Nullable
public Condition searchForIdsWithAndOr(@Nullable DbColumn theSourceJoinColumn, String theResourceName, String theParamName, List<List<IQueryParameterType>> theAndOrParams, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
if (theAndOrParams.isEmpty()) {
return null;
}
@ -1054,7 +1069,7 @@ public class QueryStack {
return toAndPredicate(andPredicates);
}
}
public void addPredicateCompositeUnique(String theIndexString, RequestPartitionId theRequestPartitionId) {

View File

@ -20,27 +20,10 @@ package ca.uhn.fhir.jpa.search.builder.predicate;
* #L%
*/
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.math.BigDecimal;
import javax.persistence.criteria.CriteriaBuilder;
import org.fhir.ucum.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import com.healthmarketscience.sqlbuilder.BinaryCondition;
import com.healthmarketscience.sqlbuilder.ComboCondition;
import com.healthmarketscience.sqlbuilder.Condition;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamBaseQuantity;
import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
@ -48,7 +31,18 @@ import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import com.healthmarketscience.sqlbuilder.BinaryCondition;
import com.healthmarketscience.sqlbuilder.ComboCondition;
import com.healthmarketscience.sqlbuilder.Condition;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
import org.springframework.beans.factory.annotation.Autowired;
import javax.persistence.criteria.CriteriaBuilder;
import java.math.BigDecimal;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredicateBuilder {
@ -59,11 +53,7 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
@Autowired
private FhirContext myFhirContext;
@Autowired
private ModelConfig myModelConfig;
/**
* Constructor
*/
@ -71,38 +61,13 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
super(theSearchSqlBuilder, theTable);
}
public Condition createPredicateQuantity(IQueryParameterType theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, QuantityBasePredicateBuilder theFrom, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
String systemValue;
String unitsValue;
ParamPrefixEnum cmpValue;
BigDecimal valueValue;
public Condition createPredicateQuantity(QuantityParam theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, QuantityBasePredicateBuilder theFrom, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
if (theParam instanceof BaseQuantityDt) {
BaseQuantityDt param = (BaseQuantityDt) theParam;
systemValue = param.getSystemElement().getValueAsString();
unitsValue = param.getUnitsElement().getValueAsString();
cmpValue = ParamPrefixEnum.forValue(param.getComparatorElement().getValueAsString());
valueValue = param.getValueElement().getValue();
} else if (theParam instanceof QuantityParam) {
QuantityParam param = (QuantityParam) theParam;
systemValue = param.getSystem();
unitsValue = param.getUnits();
cmpValue = param.getPrefix();
valueValue = param.getValue();
} else {
throw new IllegalArgumentException("Invalid quantity type: " + theParam.getClass());
}
String systemValue = theParam.getSystem();
String unitsValue = theParam.getUnits();
ParamPrefixEnum cmpValue = theParam.getPrefix();
BigDecimal valueValue = theParam.getValue();
if (myModelConfig.isNormalizedQuantitySearchSupported()) {
//-- convert the value/unit to the canonical form if any to use by the search
Pair canonicalForm = UcumServiceUtil.getCanonicalForm(systemValue, valueValue, unitsValue);
if (canonicalForm != null) {
valueValue = new BigDecimal(canonicalForm.getValue().asDecimal());
unitsValue = canonicalForm.getCode();
}
}
Condition hashPredicate;
if (!isBlank(systemValue) && !isBlank(unitsValue)) {
long hash = ResourceIndexedSearchParamBaseQuantity.calculateHashSystemAndUnits(getPartitionSettings(), theRequestPartitionId, theResourceName, theParamName, systemValue, unitsValue);
@ -127,5 +92,6 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
public DbColumn getColumnValue() {
return myColumnValue;
}
}
}

View File

@ -1,18 +1,17 @@
package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -33,15 +32,20 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.matchesPattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CreateTest.class);
@ -51,7 +55,7 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
myDaoConfig.setResourceClientIdStrategy(new DaoConfig().getResourceClientIdStrategy());
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
myModelConfig.setNormalizedQuantitySearchNotSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@Test
@ -337,9 +341,9 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
}
@Test
public void testCreateWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
public void testCreateWithNormalizedQuantitySearchSupported_AlreadyCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
@ -348,34 +352,38 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
q.setSystem("http://unitsofmeasure.org");
q.setCode("cm");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
assertTrue(myObservationDao.create(obs).getCreated());
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
QuantityParam qp = new QuantityParam();
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL);
qp.setValue(new BigDecimal("0.012"));
qp.setUnits("m");
map.add(Observation.SP_VALUE_QUANTITY, qp);
IBundleProvider found = myObservationDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(found);
List<IBaseResource> resources = found.getResources(0, found.size());
assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0)));
// Same value should be placed in both quantity tables
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
assertEquals("1.2", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("cm", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, normalizedQuantityIndexes.size());
assertEquals("0.012", Double.toString(normalizedQuantityIndexes.get(0).getValue()));
assertEquals("http://unitsofmeasure.org", normalizedQuantityIndexes.get(0).getSystem());
assertEquals("m", normalizedQuantityIndexes.get(0).getUnits());
});
SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.012"))
.setUnits("m")
);
assertEquals(1, toUnqualifiedVersionlessIdValues(myObservationDao.search(map)).size());
}
@Test
public void testCreateWithNormalizedQuantitySearchSupportedWithVerySmallNumber() {
myModelConfig.setNormalizedQuantitySearchSupported();
public void testCreateWithNormalizedQuantitySearchSupported_SmallerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
@ -384,102 +392,358 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
q.setSystem("http://unitsofmeasure.org");
q.setCode("mm");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
myCaptureQueriesListener.clear();
assertTrue(myObservationDao.create(obs).getCreated());
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
QuantityParam qp = new QuantityParam();
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL);
qp.setValue(new BigDecimal("0.0000000012"));
qp.setUnits("m");
map.add(Observation.SP_VALUE_QUANTITY, qp);
IBundleProvider found = myObservationDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(found);
List<IBaseResource> resources = found.getResources(0, found.size());
myCaptureQueriesListener.logInsertQueries();
// Original value should be in Quantity index, normalized should be in normalized table
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
double d = quantityIndexes.get(0).getValue().doubleValue();
assertEquals("1.2E-6", Double.toString(d));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("mm", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, normalizedQuantityIndexes.size());
assertEquals("1.2E-9", Double.toString(normalizedQuantityIndexes.get(0).getValue()));
assertEquals("http://unitsofmeasure.org", normalizedQuantityIndexes.get(0).getSystem());
assertEquals("m", normalizedQuantityIndexes.get(0).getUnits());
});
String searchSql;
SearchParameterMap map;
List<String> ids;
// Try with normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000000012"))
.setUnits("m")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY_NRML t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '1.2E-9'"));
assertEquals(1, ids.size());
// Try with non-normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000012"))
.setUnits("mm")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY_NRML t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '1.2E-9'"));
assertEquals(1, ids.size());
// Try with no units value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setValue(new BigDecimal("0.0000012"))
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '0.0000012'"));
assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0)));
}
@Test
public void testCreateWithNormalizedQuantitySearchSupportedWithVerySmallNumber2() {
myModelConfig.setNormalizedQuantitySearchSupported();
public void testCreateWithNormalizedQuantitySearchSupported_SmallerThanCanonicalUnit2() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
q.setValueElement(new DecimalType(149597.870691));
q.setValueElement(new DecimalType("149597.870691"));
q.setUnit("MM");
q.setSystem("http://unitsofmeasure.org");
q.setCode("mm");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
assertTrue(myObservationDao.create(obs).getCreated());
// Original value should be in Quantity index, normalized should be in normalized table
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
assertEquals("149597.870691", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("mm", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, normalizedQuantityIndexes.size());
assertEquals("149.597870691", Double.toString(normalizedQuantityIndexes.get(0).getValue()));
assertEquals("http://unitsofmeasure.org", normalizedQuantityIndexes.get(0).getSystem());
assertEquals("m", normalizedQuantityIndexes.get(0).getUnits());
});
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
QuantityParam qp = new QuantityParam();
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL);
qp.setValue(new BigDecimal("149.597870691"));
qp.setUnits("m");
map.add(Observation.SP_VALUE_QUANTITY, qp);
IBundleProvider found = myObservationDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(found);
List<IBaseResource> resources = found.getResources(0, found.size());
List<IBaseResource> resources = found.getResources(0, found.sizeOrThrowNpe());
assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0)));
}
@Test
public void testCreateWithNormalizedQuantitySearchSupportedWithLargeNumber() {
myModelConfig.setNormalizedQuantitySearchSupported();
public void testCreateWithNormalizedQuantitySearchSupported_LargerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
q.setValueElement(new DecimalType("95.7412345"));
q.setUnit("kg/dL");
q.setSystem("http://unitsofmeasure.org");
q.setCode("kg/dL");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
assertTrue(myObservationDao.create(obs).getCreated());
// Original value should be in Quantity index, normalized should be in normalized table
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
assertEquals("95.7412345", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("kg/dL", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, normalizedQuantityIndexes.size());
assertEquals("9.57412345E8", Double.toString(normalizedQuantityIndexes.get(0).getValue()));
assertEquals("http://unitsofmeasure.org", normalizedQuantityIndexes.get(0).getSystem());
assertEquals("g.m-3", normalizedQuantityIndexes.get(0).getUnits());
});
SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("957412345"))
.setUnits("g.m-3")
);
assertEquals(1, toUnqualifiedVersionlessIdValues(myObservationDao.search(map)).size());
}
@Test
public void testCreateWithNormalizedQuantitySearchSupported_NonCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
q.setValueElement(new DecimalType(95.7412345));
q.setUnit("kg/dL");
q.setSystem("http://unitsofmeasure.org");
q.setSystem("http://example.com");
q.setCode("kg/dL");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
assertTrue(myObservationDao.create(obs).getCreated());
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
QuantityParam qp = new QuantityParam();
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL);
qp.setValue(new BigDecimal("957412345"));
qp.setUnits("g.m-3");
map.add(Observation.SP_VALUE_QUANTITY, qp);
IBundleProvider found = myObservationDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(found);
List<IBaseResource> resources = found.getResources(0, found.size());
// Original value should be in Quantity index, normalized should be in normalized table
runInTransaction(() -> {
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
assertEquals("95.7412345", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
assertEquals("http://example.com", quantityIndexes.get(0).getSystem());
assertEquals("kg/dL", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(0, normalizedQuantityIndexes.size());
});
List<String> ids;
// Search should succeed using non-normalized table
myCaptureQueriesListener.clear();
SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem("http://example.com")
.setValue(95.7412345)
.setUnits("kg/dL")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '95.7412345'"));
assertEquals(1, ids.size());
}
@Test
public void testCreateWithNormalizedQuantityStorageSupported_SmallerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
q.setValueElement(new DecimalType(0.0000012));
q.setUnit("MM");
q.setSystem("http://unitsofmeasure.org");
q.setCode("mm");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
myCaptureQueriesListener.clear();
assertTrue(myObservationDao.create(obs).getCreated());
myCaptureQueriesListener.logInsertQueries();
// Original value should be in Quantity index, normalized should be in normalized table
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
double d = quantityIndexes.get(0).getValue().doubleValue();
assertEquals("1.2E-6", Double.toString(d));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("mm", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, normalizedQuantityIndexes.size());
assertEquals("1.2E-9", Double.toString(normalizedQuantityIndexes.get(0).getValue()));
assertEquals("http://unitsofmeasure.org", normalizedQuantityIndexes.get(0).getSystem());
assertEquals("m", normalizedQuantityIndexes.get(0).getUnits());
});
String searchSql;
SearchParameterMap map;
List<String> ids;
// Try with normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000000012"))
.setUnits("m")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '1.2E-9'"));
assertEquals(0, ids.size());
// Try with non-normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000012"))
.setUnits("mm")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '0.0000012'"));
assertEquals(1, ids.size());
// Try with no units value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setValue(new BigDecimal("0.0000012"))
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '0.0000012'"));
assertEquals(1, ids.size());
}
@Test
public void testCreateWithNormalizedQuantitySearchNotSupported_SmallerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity();
q.setValueElement(new DecimalType(0.0000012));
q.setUnit("MM");
q.setSystem("http://unitsofmeasure.org");
q.setCode("mm");
obs.setValue(q);
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
myCaptureQueriesListener.clear();
assertTrue(myObservationDao.create(obs).getCreated());
myCaptureQueriesListener.logInsertQueries();
// Original value should be in Quantity index, no normalized should be in normalized table
runInTransaction(()->{
List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(1, quantityIndexes.size());
double d = quantityIndexes.get(0).getValue().doubleValue();
assertEquals("1.2E-6", Double.toString(d));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("mm", quantityIndexes.get(0).getUnits());
List<ResourceIndexedSearchParamQuantityNormalized> normalizedQuantityIndexes = myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
assertEquals(0, normalizedQuantityIndexes.size());
});
String searchSql;
SearchParameterMap map;
List<String> ids;
// Try with normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000000012"))
.setUnits("m")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '1.2E-9'"));
assertEquals(0, ids.size());
// Try with non-normalized value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.0000012"))
.setUnits("mm")
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '0.0000012'"));
assertEquals(1, ids.size());
// Try with no units value
myCaptureQueriesListener.clear();
map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
.setValue(new BigDecimal("0.0000012"))
);
ids = toUnqualifiedVersionlessIdValues(myObservationDao.search(map));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true,true);
assertThat(searchSql, containsString("HFJ_SPIDX_QUANTITY t0"));
assertThat(searchSql, containsString("t0.SP_VALUE = '0.0000012'"));
assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0)));
}
}

View File

@ -6,6 +6,7 @@ import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -86,8 +87,8 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
@AfterEach
public void after() {
myDaoConfig.setValidateSearchParameterExpressionsOnSave(new DaoConfig().isValidateSearchParameterExpressionsOnSave());
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeDisableResultReuse() {
@ -117,8 +118,8 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
@Test
public void testStoreSearchParamWithBracketsInExpressionNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
myDaoConfig.setMarkResourcesForReindexingUponSearchParameterChange(true);
SearchParameter fooSp = new SearchParameter();
@ -138,8 +139,8 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
@Test
public void testStoreSearchParamWithBracketsInExpressionNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
myDaoConfig.setMarkResourcesForReindexingUponSearchParameterChange(true);
SearchParameter fooSp = new SearchParameter();

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateParam;
@ -10,7 +11,16 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.DecimalType;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Task;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -21,8 +31,12 @@ import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class);
@ -31,10 +45,10 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
public void beforeResetMissing() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
}
@AfterEach
public void afterResetSearch() {
myModelConfig.setNormalizedQuantitySearchNotSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@Test
@ -80,9 +94,9 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Organization org = new Organization();
org.setActive(true);
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
@ -94,12 +108,12 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
assertThat(myResourceIndexedSearchParamQuantityDao.findAll(), empty());
}
@Test
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Organization org = new Organization();
org.setActive(true);
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
@ -111,7 +125,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
assertThat(myResourceIndexedSearchParamQuantityDao.findAll(), empty());
}
@SuppressWarnings("unused")
@Test
public void testSearchResourceReferenceMissingChain() {
@ -221,8 +235,8 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue();
String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
runInTransaction(()->{
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
{
@ -309,8 +323,8 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test
public void testSearchWithMissingQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType notMissing;
IIdType missing;
{
@ -324,6 +338,12 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
obs.setValue(new Quantity(123));
notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
}
runInTransaction(() -> {
ourLog.info("Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * ")));
ourLog.info("Normalized Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// Quantity Param
{
SearchParameterMap params = new SearchParameterMap();
@ -341,17 +361,19 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam();
param.setMissing(true);
params.add(Observation.SP_VALUE_QUANTITY, param);
myCaptureQueriesListener.clear();
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
myCaptureQueriesListener.logSelectQueries();
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
IIdType notMissing;
IIdType missing;
{
@ -386,10 +408,10 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing)));
}
}
@Test
public void testSearchWithMissingReference() {
IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId().toUnqualifiedVersionless();

View File

@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
@ -185,8 +186,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeDisableCacheReuse() {
@ -1236,8 +1237,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testIndexNoDuplicatesQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("m").setValue(123);
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("m").setValue(123);
@ -1246,20 +1247,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantity> type = ResourceIndexedSearchParamQuantity.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
});
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantityNormalized> type = ResourceIndexedSearchParamQuantityNormalized.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
});
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 12300, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm"))));
assertThat(actual, contains(id));
@ -1268,26 +1255,12 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testQuantityWithNormalizedQuantitySearchSupported_InvalidUCUMCode() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantity> type = ResourceIndexedSearchParamQuantity.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(1, results.size());
});
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantityNormalized> type = ResourceIndexedSearchParamQuantityNormalized.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(1, results.size());
});
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "FOO"))));
@ -1297,27 +1270,13 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testQuantityWithNormalizedQuantitySearchSupported_NotUCUM() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem("http://bar").setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantity> type = ResourceIndexedSearchParamQuantity.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(1, results.size());
});
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantityNormalized> type = ResourceIndexedSearchParamQuantityNormalized.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(1, results.size());
});
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://bar", "FOO"))));
assertThat(actual, contains(id));
@ -1326,8 +1285,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testIndexNoDuplicatesQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("m").setValue(123);
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("m").setValue(123);
@ -1336,20 +1295,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantity> type = ResourceIndexedSearchParamQuantity.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
});
runInTransaction(() -> {
Class<ResourceIndexedSearchParamQuantityNormalized> type = ResourceIndexedSearchParamQuantityNormalized.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
});
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "m"))));
assertThat(actual, contains(id));
@ -2753,8 +2698,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testSearchByMoneyParamWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
ChargeItem ci = new ChargeItem();
ci.getPriceOverride().setValue(123).setCurrency("$");
@ -3119,8 +3064,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testSearchQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Condition c1 = new Condition();
c1.setAbatement(new Range().setLow(new SimpleQuantity().setValue(1L)).setHigh(new SimpleQuantity().setValue(1L)));
String id1 = myConditionDao.create(c1).getId().toUnqualifiedVersionless().getValue();

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
@ -143,8 +144,8 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setDisableHashBasedSearches(false);
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeInitialize() {
@ -1193,8 +1194,8 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test
public void testComponentQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")))
@ -1216,8 +1217,8 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test
public void testComponentQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")))
@ -1292,8 +1293,8 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test
public void testSearchCompositeParamQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
@ -161,8 +162,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void before() {
@ -581,8 +582,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test
public void testChoiceParamQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o3 = new Observation();
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
o3.setValue(new Quantity(QuantityComparator.GREATER_THAN, 123.0, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm", "cm")); // 0.0123m
@ -682,8 +683,8 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test
public void testChoiceParamQuantityPrecisionWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o3 = new Observation();
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
o3.setValue(new Quantity(null, 123.01, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm", "cm")); // 0.012301 m
@ -3500,9 +3501,10 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
}
@Test
public void testSortByQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
@Disabled
public void testSortByQuantityWithNormalizedQuantitySearchFullSupported() {
// myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED);
Observation res;
res = new Observation();

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
@ -23,7 +24,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -61,7 +61,6 @@ import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@ -110,8 +109,8 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
public void after() {
myDaoConfig.setAllowInlineMatchUrlReferences(false);
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeDisableResultReuse() {
@ -3131,7 +3130,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
@Test
public void testTransactionWithConditionalUpdateDoesntUpdateIfNoChangeWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation();
obs.addIdentifier()
.setSystem("http://acme.org")

View File

@ -1,20 +1,29 @@
package ca.uhn.fhir.jpa.dao.r4;
import static java.util.Comparator.comparing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.hl7.fhir.dstu2.model.SimpleQuantity;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.ReadOnlySearchParamCache;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.collect.Sets;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
@ -24,47 +33,30 @@ import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Range;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorR4;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.searchparam.registry.ReadOnlySearchParamCache;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import static java.util.Comparator.comparing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class SearchParamExtractorR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParamExtractorR4Test.class);
private static FhirContext ourCtx = FhirContext.forCached(FhirVersionEnum.R4);
private static IValidationSupport ourValidationSupport;
private MySearchParamRegistry mySearchParamRegistry;
private PartitionSettings myPartitionSettings;
@ -81,7 +73,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -95,7 +87,7 @@ public class SearchParamExtractorR4Test {
SearchParameter sp = new SearchParameter();
sp.addUseContext().setCode(new Coding().setSystem("http://system").setCode("code"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(sp);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -109,7 +101,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -138,7 +130,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -162,7 +154,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -191,7 +183,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -216,7 +208,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -246,7 +238,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
@ -267,7 +259,7 @@ public class SearchParamExtractorR4Test {
Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location/123"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Encounter", "location");
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(enc);
@ -282,7 +274,7 @@ public class SearchParamExtractorR4Test {
Consent consent = new Consent();
consent.setSource(new Reference().setReference("Consent/999"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Consent", Consent.SP_SOURCE_REFERENCE);
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(consent);
@ -297,7 +289,7 @@ public class SearchParamExtractorR4Test {
Patient p = new Patient();
p.addIdentifier().setSystem("sys").setValue("val");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Patient", Patient.SP_IDENTIFIER);
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> params = extractor.extractSearchParamTokens(p, param);
@ -319,7 +311,7 @@ public class SearchParamExtractorR4Test {
Patient patient = new Patient();
patient.addExtension("http://patext", new Reference("Organization/AAA"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(patient);
assertEquals(1, links.size());
@ -335,7 +327,7 @@ public class SearchParamExtractorR4Test {
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, mySearchParamRegistry);
Set<ResourceIndexedSearchParamQuantity> links = extractor.extractSearchParamQuantity(o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(4, links.size());
@ -343,44 +335,44 @@ public class SearchParamExtractorR4Test {
@Test
public void testExtractComponentQuantityWithNormalizedQuantitySearchSupported() {
ModelConfig modelConfig = new ModelConfig();
modelConfig.setNormalizedQuantitySearchSupported();
modelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm").setValue(200));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, new PartitionSettings(), ourCtx, mySearchParamRegistry);
Set<ResourceIndexedSearchParamQuantityNormalized> links = extractor.extractSearchParamQuantityNormalized(o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(2, links.size());
}
@Test
public void testExtractComponentQuantityValueWithNormalizedQuantitySearchSupported() {
ModelConfig modelConfig = new ModelConfig();
modelConfig.setNormalizedQuantitySearchSupported();
modelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm").setValue(200));
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("component-value-quantity");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, new PartitionSettings(), ourCtx, mySearchParamRegistry);
List<String> list = extractor.extractParamValuesAsStrings(existingCodeSp, o1);
assertEquals(1, list.size());
assertEquals(2, list.size());
}
private static class MySearchParamRegistry implements ISearchParamRegistry {
@ -457,9 +449,4 @@ public class SearchParamExtractorR4Test {
}
}
@BeforeAll
public static void beforeClass() {
ourValidationSupport = new DefaultProfileValidationSupport(ourCtx);
}
}

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -62,8 +63,8 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@AfterEach
public void afterDisableExpunge() {
myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled());
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeEnableExpunge() {
@ -402,7 +403,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@Test
public void testExpungeSystemEverythingWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
createStandardPatients();
mySystemDao.expunge(new ExpungeOptions()
@ -424,7 +425,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@Test
public void testExpungeSystemEverythingWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
createStandardPatients();
mySystemDao.expunge(new ExpungeOptions()

View File

@ -47,6 +47,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
@ -217,9 +218,9 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false);
mySearchCoordinatorSvcRaw.cancelAllActiveSearches();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchNotSupported();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
myClient.unregisterInterceptor(myCapturingInterceptor);
myClient.unregisterInterceptor(myCapturingInterceptor);
}
@BeforeEach
@ -4085,8 +4086,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0;
{
Patient patient = new Patient();
@ -4167,8 +4168,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported_CombineUCUMOrNonUCUM() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0;
{
Patient patient = new Patient();
@ -4222,11 +4223,17 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
}
// > 1m
String uri = ourServerBase + "/Observation?value-quantity=" + UrlUtil.escapeUrlParam("100|http://unitsofmeasure.org|cm,100|http://foo|cm");
ourLog.info("uri = " + uri);
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
String uri;
List<String> ids;
// With non-normalized
uri = ourServerBase + "/Observation?value-quantity=" + UrlUtil.escapeUrlParam("100|http://unitsofmeasure.org|cm,100|http://foo|cm");
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertEquals(1, ids.size());
// With normalized
uri = ourServerBase + "/Observation?value-quantity=" + UrlUtil.escapeUrlParam("1|http://unitsofmeasure.org|m,100|http://foo|cm");
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertEquals(2, ids.size());
}
@ -6044,8 +6051,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test
public void testUpdateWithNormalizedQuantitySearchSupported() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0;
{
Patient patient = new Patient();

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.TestR4Config;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
@ -103,8 +104,8 @@ public class InMemorySubscriptionMatcherR4Test {
@AfterEach
public void after() throws Exception {
myModelConfig.setNormalizedQuantitySearchNotSupported();
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
private void assertMatched(Resource resource, SearchParameterMap params) {
InMemoryMatchResult result = match(resource, params);
@ -249,8 +250,8 @@ public class InMemorySubscriptionMatcherR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
@ -282,8 +283,8 @@ public class InMemorySubscriptionMatcherR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported_InvalidUCUMUnit() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://bar").setCode("foo")))
@ -298,8 +299,8 @@ public class InMemorySubscriptionMatcherR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported_NoSystem() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://bar").setCode("foo")))
@ -314,9 +315,9 @@ public class InMemorySubscriptionMatcherR4Test {
@Test
public void testSearchWithNormalizedQuantitySearchSupported_NotUcumSystem() {
myModelConfig.setNormalizedQuantitySearchSupported();
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation();
o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("cm")))

View File

@ -22,14 +22,14 @@ package ca.uhn.fhir.jpa.migrate.taskdef;
public enum ColumnTypeEnum {
LONG,
STRING,
DATE_ONLY,
DATE_TIMESTAMP,
BOOLEAN,
FLOAT,
INT,
BLOB,
CLOB
LONG,
STRING,
DATE_ONLY,
DATE_TIMESTAMP,
BOOLEAN,
FLOAT,
INT,
BLOB,
CLOB,
DOUBLE;
}

View File

@ -49,6 +49,14 @@ public class ColumnTypeToDriverTypeToSqlType {
setColumnType(ColumnTypeEnum.FLOAT, DriverTypeEnum.ORACLE_12C, "float");
setColumnType(ColumnTypeEnum.FLOAT, DriverTypeEnum.POSTGRES_9_4, "float");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.H2_EMBEDDED, "double");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.DERBY_EMBEDDED, "double");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.MARIADB_10_1, "double precision");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.MYSQL_5_7, "double precision");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.MSSQL_2012, "double precision");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.ORACLE_12C, "double precision");
setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.POSTGRES_9_4, "float8");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.H2_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.DERBY_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MARIADB_10_1, "bigint");

View File

@ -119,6 +119,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.addForeignKey("20210111.3", "FKRCJOVMUH5KC0O6FVBLE319PYV")
.toColumn("RES_ID")
.references("HFJ_RESOURCE", "RES_ID");
Builder.BuilderWithTableName quantityTable = version.onTable("HFJ_SPIDX_QUANTITY");
quantityTable.modifyColumn("20210116.1", "SP_VALUE").nullable().failureAllowed().withType(ColumnTypeEnum.DOUBLE);
}
protected void init520() {

View File

@ -90,7 +90,7 @@ public class ModelConfig {
private IPrimitiveType<Date> myPeriodIndexEndOfTime;
private NormalizedQuantitySearchLevel myNormalizedQuantitySearchLevel;
/**
* Constructor
*/
@ -576,6 +576,47 @@ public class ModelConfig {
myPeriodIndexEndOfTime = thePeriodIndexEndOfTime;
}
/**
* Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
*
* <p>
* The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
* </p>
* <p>
* Here is the UCUM service support level
* <ul>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
* </ul>
* </p>
*
* @since 5.3.0
*/
public NormalizedQuantitySearchLevel getNormalizedQuantitySearchLevel() {
return myNormalizedQuantitySearchLevel;
}
/**
* Toggles whether Quantity searches support value normalization when using valid UCUM coded values.
*
* <p>
* The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
* </p>
* <p>
* Here is the UCUM service support level
* <ul>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
* </ul>
* </p>
*
* @since 5.3.0
*/
public void setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel theNormalizedQuantitySearchLevel) {
myNormalizedQuantitySearchLevel = theNormalizedQuantitySearchLevel;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty");
@ -589,43 +630,4 @@ public class ModelConfig {
}
/**
* Set the UCUM service support level
*
* <p>
* The default value is {@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED} which is current behavior.
* </p>
* <p>
* Here is the UCUM service support level
* <ul>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED}, default, Quantity is stored in {@link ResourceIndexedSearchParamQuantity} only and it is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_STORAGE_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, but {@link ResourceIndexedSearchParamQuantity} is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_SUPPORTED}, Quantity is stored in both {@link ResourceIndexedSearchParamQuantity} and {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching.</li>
* <li>{@link NormalizedQuantitySearchLevel#NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED}, Quantity is stored in only in {@link ResourceIndexedSearchParamQuantityNormalized}, {@link ResourceIndexedSearchParamQuantityNormalized} is used by searching. NOTE this option is not supported yet.</li>
* </ul>
* </p>
*
* @since 5.3.0
*/
public NormalizedQuantitySearchLevel getNormalizedQuantitySearchLevel() {
return myNormalizedQuantitySearchLevel;
}
public void setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel theNormalizedQuantitySearchLevel) {
myNormalizedQuantitySearchLevel = theNormalizedQuantitySearchLevel;
}
public boolean isNormalizedQuantitySearchSupported() {
return myNormalizedQuantitySearchLevel.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
}
public boolean isNormalizedQuantityStorageSupported() {
return myNormalizedQuantitySearchLevel.equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
}
public void setNormalizedQuantitySearchNotSupported() {
myNormalizedQuantitySearchLevel = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED;
}
public void setNormalizedQuantityStorageSupported() {
myNormalizedQuantitySearchLevel = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED;
}
public void setNormalizedQuantitySearchSupported() {
myNormalizedQuantitySearchLevel = NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED;
}
}

View File

@ -53,5 +53,6 @@ public enum NormalizedQuantitySearchLevel {
* The existing non normalized quantity will be not supported
* NOTE this option is not supported in this release
*/
// When this is enabled, we can enable testSortByQuantityWithNormalizedQuantitySearchFullSupported()
//NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED,
}

View File

@ -69,7 +69,7 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
@Column(name = "SP_VALUE", nullable = true)
@ScaledNumberField
public BigDecimal myValue;
public Double myValue;
public ResourceIndexedSearchParamQuantity() {
super();
@ -99,11 +99,11 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
}
public BigDecimal getValue() {
return myValue;
return myValue != null ? new BigDecimal(myValue) : null;
}
public ResourceIndexedSearchParamQuantity setValue(BigDecimal theValue) {
myValue = theValue;
myValue = theValue != null ? theValue.doubleValue() : null;
return this;
}

View File

@ -40,7 +40,6 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.fhir.ucum.Pair;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField;
@ -84,23 +83,14 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
super();
}
public ResourceIndexedSearchParamQuantityNormalized(PartitionSettings thePartitionSettings, String theResourceType, String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
public ResourceIndexedSearchParamQuantityNormalized(PartitionSettings thePartitionSettings, String theResourceType, String theParamName, double theValue, String theSystem, String theUnits) {
this();
setPartitionSettings(thePartitionSettings);
setResourceType(theResourceType);
setParamName(theParamName);
setSystem(theSystem);
//-- convert the value/unit to the canonical form if any, otherwise store the original value/units pair
Pair canonicalForm = UcumServiceUtil.getCanonicalForm(theSystem, theValue, theUnits);
if (canonicalForm != null) {
setValue(Double.parseDouble(canonicalForm.getValue().asDecimal()));
setUnits(canonicalForm.getCode());
} else {
setValue(theValue);
setUnits(theUnits);
}
setValue(theValue);
setUnits(theUnits);
calculateHashes();
}
@ -123,17 +113,13 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
public ResourceIndexedSearchParamQuantityNormalized setValue(Double theValue) {
myValue = theValue;
return this;
}
public void setValue(BigDecimal theValue) {
if (theValue != null)
myValue = theValue.doubleValue();
}
public BigDecimal getValueBigDecimal() {
if (myValue == null)
return null;
return new BigDecimal(myValue);
public ResourceIndexedSearchParamQuantityNormalized setValue(double theValue) {
myValue = theValue;
return this;
}
//-- myId
@Override
public Long getId() {

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.model.util;
import java.io.InputStream;
import java.math.BigDecimal;
import ca.uhn.fhir.rest.param.QuantityParam;
import org.fhir.ucum.Decimal;
import org.fhir.ucum.Pair;
import org.fhir.ucum.UcumEssenceService;
@ -32,6 +33,8 @@ import org.slf4j.LoggerFactory;
import ca.uhn.fhir.util.ClasspathUtil;
import javax.annotation.Nullable;
/**
* It's a wrapper of UcumEssenceService
*
@ -85,7 +88,7 @@ public class UcumServiceUtil {
return null;
init();
Pair theCanonicalPair = null;
Pair theCanonicalPair;
try {
Decimal theDecimal = new Decimal(theValue.toPlainString(), theValue.precision());
@ -97,4 +100,19 @@ public class UcumServiceUtil {
return theCanonicalPair;
}
@Nullable
public static QuantityParam toCanonicalQuantityOrNull(QuantityParam theQuantityParam) {
Pair canonicalForm = getCanonicalForm(theQuantityParam.getSystem(), theQuantityParam.getValue(), theQuantityParam.getUnits());
if (canonicalForm != null) {
BigDecimal valueValue = new BigDecimal(canonicalForm.getValue().asDecimal());
String unitsValue = canonicalForm.getCode();
return new QuantityParam()
.setSystem(theQuantityParam.getSystem())
.setValue(valueValue)
.setUnits(unitsValue)
.setPrefix(theQuantityParam.getPrefix());
} else {
return null;
}
}
}

View File

@ -1,34 +1,13 @@
package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class ResourceIndexedSearchParamQuantityNormalizedTest {
private ResourceIndexedSearchParamQuantityNormalized createParam(String theParamName, String theValue, String theSystem, String theUnits) {
ResourceIndexedSearchParamQuantityNormalized token = new ResourceIndexedSearchParamQuantityNormalized(new PartitionSettings(), "Observation", theParamName, new BigDecimal(theValue), theSystem, theUnits);
token.setResource(new ResourceTable().setResourceType("Patient"));
return token;
}
@Test
public void testHashFunctions() {
ResourceIndexedSearchParamQuantityNormalized token = createParam("Quanity", "123.001", UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm");
token.calculateHashes();
// Make sure our hashing function gives consistent results
assertEquals(5219730978980909111L, token.getHashIdentity().longValue());
assertEquals(-2454931617586657338L, token.getHashIdentityAndUnits().longValue());
assertEquals(878263047209296227L, token.getHashIdentitySystemAndUnits().longValue());
}
@Test
public void testEquals() {
@ -46,37 +25,5 @@ public class ResourceIndexedSearchParamQuantityNormalizedTest {
assertNotEquals(val1, "");
}
@Test
public void testUcum() {
//-- system is ucum
ResourceIndexedSearchParamQuantityNormalized token1 = createParam("Quanity", "123.001", UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm");
token1.calculateHashes();
assertEquals("m", token1.getUnits());
assertEquals(Double.parseDouble("1.23001"), token1.getValue());
//-- small number
token1 = createParam("Quanity", "0.000001", UcumServiceUtil.UCUM_CODESYSTEM_URL, "mm");
token1.calculateHashes();
assertEquals("m", token1.getUnits());
assertEquals(Double.parseDouble("0.000000001"), token1.getValue());
// -- non ucum system
ResourceIndexedSearchParamQuantityNormalized token2 = createParam("Quanity", "123.001", "http://abc.org", "cm");
token2.calculateHashes();
assertEquals("cm", token2.getUnits());
assertEquals(Double.parseDouble("123.001"), token2.getValue());
// -- unsupported ucum code
ResourceIndexedSearchParamQuantityNormalized token3 = createParam("Quanity", "123.001", UcumServiceUtil.UCUM_CODESYSTEM_URL, "unknown");
token3.calculateHashes();
assertEquals("unknown", token3.getUnits());
assertEquals(Double.parseDouble("123.001"), token3.getValue());
}
}

View File

@ -30,6 +30,7 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@ -38,6 +39,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
@ -49,7 +51,7 @@ import com.google.common.collect.Sets;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hibernate.search.engine.spatial.GeoPoint;
import org.fhir.ucum.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
@ -78,7 +80,6 @@ import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.searchparam.extractor.GeopointNormalizer.normalizeLongitude;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
@ -199,12 +200,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
break;
case REFERENCE:
extractor = createReferenceExtractor();
return extractReferenceParamsAsQueryTokens(theSearchParam, theResource, extractor);
break;
case QUANTITY:
if (myModelConfig.isNormalizedQuantitySearchSupported())
extractor = createQuantityNormalizedExtractor(theResource);
else
if (myModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
extractor = new CompositeExtractor(
createQuantityExtractor(theResource),
createQuantityNormalizedExtractor(theResource)
);
} else {
extractor = createQuantityExtractor(theResource);
}
break;
case URI:
extractor = createUriExtractor(theResource);
@ -216,6 +221,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
default:
throw new UnsupportedOperationException("Type " + theSearchParam.getParamType() + " not supported for extraction");
}
return extractParamsAsQueryTokens(theSearchParam, theResource, extractor);
}
@ -377,14 +383,14 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY);
}
@Override
public SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> extractSearchParamQuantityNormalized(IBaseResource theResource) {
IExtractor<ResourceIndexedSearchParamQuantityNormalized> extractor = createQuantityNormalizedExtractor(theResource);
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.QUANTITY);
}
private IExtractor<ResourceIndexedSearchParamQuantity> createQuantityExtractor(IBaseResource theResource) {
private IExtractor<ResourceIndexedSearchParamQuantity> createQuantityExtractor(IBaseResource theResource) {
return (params, searchParam, value, path) -> {
if (value.getClass().equals(myLocationPositionDefinition.getImplementingClass())) {
return;
@ -394,7 +400,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String resourceType = toRootTypeName(theResource);
switch (nextType) {
case "Quantity":
addQuantity_Quantity(resourceType, params, searchParam, value);
addQuantity_Quantity(resourceType, params, searchParam, value);
break;
case "Money":
addQuantity_Money(resourceType, params, searchParam, value);
@ -406,11 +412,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
addUnexpectedDatatypeWarning(params, searchParam, value);
break;
}
};
};
}
private IExtractor<ResourceIndexedSearchParamQuantityNormalized> createQuantityNormalizedExtractor(IBaseResource theResource) {
return (params, searchParam, value, path) -> {
if (value.getClass().equals(myLocationPositionDefinition.getImplementingClass())) {
return;
@ -434,7 +440,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
};
}
@Override
public SearchParamSet<ResourceIndexedSearchParamString> extractSearchParamStrings(IBaseResource theResource) {
IExtractor<ResourceIndexedSearchParamString> extractor = createStringExtractor(theResource);
@ -546,24 +552,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String code = extractValueAsString(myQuantityCodeValueChild, theValue);
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(myPartitionSettings, theResourceType, theSearchParam.getName(), nextValueValue, system, code);
theParams.add(nextEntity);
theParams.add(nextEntity);
}
}
private void addQuantity_QuantityNormalized(String theResourceType, Set<ResourceIndexedSearchParamQuantityNormalized> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
private void addQuantity_QuantityNormalized(String theResourceType, Set<ResourceIndexedSearchParamQuantityNormalized> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
Optional<IPrimitiveType<BigDecimal>> valueField = myQuantityValueValueChild.getAccessor().getFirstValueOrNull(theValue);
if (valueField.isPresent() && valueField.get().getValue() != null) {
BigDecimal nextValueValue = valueField.get().getValue();
String system = extractValueAsString(myQuantitySystemValueChild, theValue);
String code = extractValueAsString(myQuantityCodeValueChild, theValue);
ResourceIndexedSearchParamQuantityNormalized nextEntity = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, theSearchParam.getName(), nextValueValue, system, code);
theParams.add(nextEntity);
//-- convert the value/unit to the canonical form if any
Pair canonicalForm = UcumServiceUtil.getCanonicalForm(system, nextValueValue, code);
if (canonicalForm != null) {
double canonicalValue = Double.parseDouble(canonicalForm.getValue().asDecimal());
String canonicalUnits = canonicalForm.getCode();
ResourceIndexedSearchParamQuantityNormalized nextEntity = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, theSearchParam.getName(), canonicalValue, system, canonicalUnits);
theParams.add(nextEntity);
}
}
}
private void addQuantity_Money(String theResourceType, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
Optional<IPrimitiveType<BigDecimal>> valueField = myMoneyValueChild.getAccessor().getFirstValueOrNull(theValue);
if (valueField.isPresent() && valueField.get().getValue() != null) {
@ -572,14 +585,14 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String nextValueString = "urn:iso:std:iso:4217";
String nextValueCode = extractValueAsString(myMoneyCurrencyChild, theValue);
String searchParamName = theSearchParam.getName();
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(myPartitionSettings, theResourceType, searchParamName, nextValueValue, nextValueString, nextValueCode);
theParams.add(nextEntity);
}
}
}
private void addQuantity_MoneyNormalized(String theResourceType, Set<ResourceIndexedSearchParamQuantityNormalized> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
private void addQuantity_MoneyNormalized(String theResourceType, Set<ResourceIndexedSearchParamQuantityNormalized> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
Optional<IPrimitiveType<BigDecimal>> valueField = myMoneyValueChild.getAccessor().getFirstValueOrNull(theValue);
if (valueField.isPresent() && valueField.get().getValue() != null) {
BigDecimal nextValueValue = valueField.get().getValue();
@ -587,22 +600,22 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String nextValueString = "urn:iso:std:iso:4217";
String nextValueCode = extractValueAsString(myMoneyCurrencyChild, theValue);
String searchParamName = theSearchParam.getName();
ResourceIndexedSearchParamQuantityNormalized nextEntityNormalized = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, searchParamName, nextValueValue, nextValueString, nextValueCode);
theParams.add(nextEntityNormalized);
ResourceIndexedSearchParamQuantityNormalized nextEntityNormalized = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, searchParamName, nextValueValue.doubleValue(), nextValueString, nextValueCode);
theParams.add(nextEntityNormalized);
}
}
private void addQuantity_Range(String theResourceType, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
Optional<IBase> low = myRangeLowValueChild.getAccessor().getFirstValueOrNull(theValue);
low.ifPresent(theIBase -> addQuantity_Quantity(theResourceType, theParams, theSearchParam, theIBase));
Optional<IBase> high = myRangeHighValueChild.getAccessor().getFirstValueOrNull(theValue);
high.ifPresent(theIBase -> addQuantity_Quantity(theResourceType, theParams, theSearchParam, theIBase));
high.ifPresent(theIBase -> addQuantity_Quantity(theResourceType, theParams, theSearchParam, theIBase));
}
private void addQuantity_RangeNormalized(String theResourceType, Set<ResourceIndexedSearchParamQuantityNormalized> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
Optional<IBase> low = myRangeLowValueChild.getAccessor().getFirstValueOrNull(theValue);
low.ifPresent(theIBase -> addQuantity_QuantityNormalized(theResourceType, theParams, theSearchParam, theIBase));
@ -610,7 +623,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
Optional<IBase> high = myRangeHighValueChild.getAccessor().getFirstValueOrNull(theValue);
high.ifPresent(theIBase -> addQuantity_QuantityNormalized(theResourceType, theParams, theSearchParam, theIBase));
}
private void addToken_Identifier(String theResourceType, Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
String system = extractValueAsString(myIdentifierSystemValueChild, theValue);
String value = extractValueAsString(myIdentifierValueValueChild, theValue);
@ -1163,11 +1176,27 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@FunctionalInterface
private interface IExtractor<T> {
void extract(SearchParamSet<T> theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath);
}
private static class CompositeExtractor<T> implements IExtractor<T> {
private final IExtractor<T> myExtractor0;
private final IExtractor<T> myExtractor1;
private CompositeExtractor(IExtractor<T> theExtractor0, IExtractor<T> theExtractor1) {
myExtractor0 = theExtractor0;
myExtractor1 = theExtractor1;
}
@Override
public void extract(SearchParamSet<T> theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath) {
myExtractor0.extract(theParams, theSearchParam, theValue, thePath);
myExtractor1.extract(theParams, theSearchParam, theValue, thePath);
}
}
private class ResourceLinkExtractor implements IExtractor<PathAndRef> {
private PathAndRef myPathAndRef = null;

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
@ -36,9 +37,14 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
@ -51,6 +57,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static org.apache.commons.lang3.StringUtils.compare;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -158,21 +165,30 @@ public final class ResourceIndexedSearchParams {
return myPopulatedResourceLinkParameters;
}
public boolean matchParam(ModelConfig theModelConfig, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theParam) {
public boolean matchParam(ModelConfig theModelConfig, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, IQueryParameterType theValue) {
if (theParamDef == null) {
return false;
}
Collection<? extends BaseResourceIndexedSearchParam> resourceParams;
Collection<? extends BaseResourceIndexedSearchParam> resourceParams = null;
IQueryParameterType value = theValue;
switch (theParamDef.getParamType()) {
case TOKEN:
resourceParams = myTokenParams;
break;
case QUANTITY:
if (theModelConfig.isNormalizedQuantitySearchSupported())
resourceParams = myQuantityNormalizedParams;
else
resourceParams = myQuantityParams;
if (theModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
QuantityParam quantity = QuantityParam.toQuantityParam(theValue);
QuantityParam normalized = UcumServiceUtil.toCanonicalQuantityOrNull(quantity);
if (normalized != null) {
resourceParams = myQuantityNormalizedParams;
value = normalized;
}
}
if (resourceParams == null) {
resourceParams = myQuantityParams;
}
break;
case STRING:
resourceParams = myStringParams;
@ -187,7 +203,7 @@ public final class ResourceIndexedSearchParams {
resourceParams = myDateParams;
break;
case REFERENCE:
return matchResourceLinks(theModelConfig, theResourceName, theParamName, theParam, theParamDef.getPath());
return matchResourceLinks(theModelConfig, theResourceName, theParamName, value, theParamDef.getPath());
case COMPOSITE:
case HAS:
case SPECIAL:
@ -197,11 +213,16 @@ public final class ResourceIndexedSearchParams {
if (resourceParams == null) {
return false;
}
Predicate<BaseResourceIndexedSearchParam> namedParamPredicate = param ->
param.getParamName().equalsIgnoreCase(theParamName) &&
param.matches(theParam);
return resourceParams.stream().anyMatch(namedParamPredicate);
for (BaseResourceIndexedSearchParam nextParam : resourceParams) {
if (nextParam.getParamName().equalsIgnoreCase(theParamName)) {
if (nextParam.matches(value)) {
return true;
}
}
}
return false;
}
/**
@ -275,10 +296,7 @@ public final class ResourceIndexedSearchParams {
public void findMissingSearchParams(PartitionSettings thePartitionSettings, ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) {
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.STRING, myStringParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams);
if (theModelConfig.isNormalizedQuantitySearchSupported())
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityNormalizedParams);
else
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.QUANTITY, myQuantityParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.DATE, myDateParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.TOKEN, myTokenParams);
@ -309,10 +327,7 @@ public final class ResourceIndexedSearchParams {
param = new ResourceIndexedSearchParamNumber();
break;
case QUANTITY:
if (theModelConfig.isNormalizedQuantitySearchSupported())
param = new ResourceIndexedSearchParamQuantityNormalized();
else
param = new ResourceIndexedSearchParamQuantity();
param = new ResourceIndexedSearchParamQuantity();
break;
case STRING:
param = new ResourceIndexedSearchParamString()

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -50,9 +49,9 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
// This constructor is used by tests
@VisibleForTesting
public SearchParamExtractorDstu3(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
public SearchParamExtractorDstu3(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPathEngine(theValidationSupport);
initFhirPathEngine();
start();
}
@ -75,13 +74,12 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
public void start() {
super.start();
if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class);
initFhirPathEngine(support);
initFhirPathEngine();
}
}
public void initFhirPathEngine(IValidationSupport theSupport) {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport);
public void initFhirPathEngine() {
IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker);
}

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -62,9 +61,9 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
// This constructor is used by tests
@VisibleForTesting
public SearchParamExtractorR4(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
public SearchParamExtractorR4(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theValidationSupport);
initFhirPath();
start();
}
@ -82,13 +81,12 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
public void start() {
super.start();
if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class);
initFhirPath(support);
initFhirPath();
}
}
public void initFhirPath(IValidationSupport theSupport) {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport);
public void initFhirPath() {
IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker);
myFhirPathEngine.setHostServices(new SearchParamExtractorR4HostServices());
}
@ -96,7 +94,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
private static class SearchParamExtractorR4HostServices implements FHIRPathEngine.IEvaluationContext {
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {

View File

@ -21,8 +21,6 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -58,9 +56,9 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
/**
* Constructor for unit tests
*/
public SearchParamExtractorR5(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, DefaultProfileValidationSupport theDefaultProfileValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
public SearchParamExtractorR5(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theDefaultProfileValidationSupport);
initFhirPath();
start();
}
@ -69,13 +67,12 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
public void start() {
super.start();
if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class);
initFhirPath(support);
initFhirPath();
}
}
public void initFhirPath(IValidationSupport theSupport) {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport);
public void initFhirPath() {
IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker);
myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices());
}
@ -88,7 +85,7 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
private static class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext {
private Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Base> myResourceTypeToStub = Collections.synchronizedMap(new HashMap<>());
@Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException {

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@ -119,8 +120,8 @@ public class SearchParamExtractorService {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantity> quantities = extractSearchParamQuantity(theResource);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities);
theParams.myQuantityParams.addAll(quantities);
if (myModelConfig.isNormalizedQuantityStorageSupported()|| myModelConfig.isNormalizedQuantitySearchSupported()) {
if (myModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED) || myModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized = extractSearchParamQuantityNormalized(theResource);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
theParams.myQuantityNormalizedParams.addAll(quantitiesNormalized);

View File

@ -9,9 +9,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.util.StopWatch;
import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@ -23,7 +20,6 @@ import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -43,9 +39,8 @@ public class IndexStressTest {
FhirContext ctx = FhirContext.forDstu3();
IValidationSupport mockValidationSupport = mock(IValidationSupport.class);
when(mockValidationSupport.getFhirContext()).thenReturn(ctx);
IValidationSupport validationSupport = new CachingValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(ctx), mockValidationSupport));
ISearchParamRegistry searchParamRegistry = mock(ISearchParamRegistry.class);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ctx, validationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ctx, searchParamRegistry);
extractor.start();
Map<String, RuntimeSearchParam> spMap = ctx

View File

@ -1,11 +1,10 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
@ -33,7 +32,6 @@ import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Questionnaire;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.text.Normalizer;
@ -51,8 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class SearchParamExtractorDstu3Test {
private static FhirContext ourCtx = FhirContext.forDstu3();
private static IValidationSupport ourValidationSupport;
private static FhirContext ourCtx = FhirContext.forCached(FhirVersionEnum.DSTU3);
@Test
public void testParamWithOrInPath() {
@ -61,7 +58,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size());
@ -84,7 +81,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamString> params = extractor.extractSearchParamStrings(questionnaire);
assertEquals(1, params.size());
@ -102,7 +99,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size());
@ -120,7 +117,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size());
@ -132,7 +129,7 @@ public class SearchParamExtractorDstu3Test {
public void testEmptyPath() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
searchParamRegistry.addSearchParam(new RuntimeSearchParam("foo", "foo", "", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE));
@ -148,7 +145,7 @@ public class SearchParamExtractorDstu3Test {
public void testStringMissingResourceType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
searchParamRegistry.addSearchParam(new RuntimeSearchParam("foo", "foo", "communication.language.coding.system | communication.language.coding.code", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE));
@ -165,7 +162,7 @@ public class SearchParamExtractorDstu3Test {
public void testInvalidType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
{
@ -216,7 +213,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, searchParamRegistry);
extractor.start();
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> coords = extractor.extractSearchParamTokens(loc);
assertEquals(1, coords.size());
@ -227,7 +224,7 @@ public class SearchParamExtractorDstu3Test {
private static class MySearchParamRegistry implements ISearchParamRegistry {
private List<RuntimeSearchParam> myAddedSearchParams = new ArrayList<>();
private final List<RuntimeSearchParam> myAddedSearchParams = new ArrayList<>();
public void addSearchParam(RuntimeSearchParam... theSearchParam) {
myAddedSearchParams.clear();
@ -306,9 +303,4 @@ public class SearchParamExtractorDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@BeforeAll
public static void beforeClass() {
ourValidationSupport = new DefaultProfileValidationSupport(ourCtx);
}
}

View File

@ -17,7 +17,6 @@ import ca.uhn.fhir.context.RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
@ -66,15 +65,15 @@ public class SearchParamExtractorMegaTest {
ctx = FhirContext.forDstu3();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorDstu3(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
process(ctx, new SearchParamExtractorDstu3(new ModelConfig(), partitionSettings, ctx, searchParamRegistry));
ctx = FhirContext.forR4();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorR4(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
process(ctx, new SearchParamExtractorR4(new ModelConfig(), partitionSettings, ctx, searchParamRegistry));
ctx = FhirContext.forR5();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorR5(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
process(ctx, new SearchParamExtractorR5(new ModelConfig(), partitionSettings, ctx, searchParamRegistry));
}
private void process(FhirContext theCtx, BaseSearchParamExtractor theExtractor) throws Exception {

View File

@ -1956,7 +1956,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<version>0.8.6</version>
<configuration>
<excludes>
<exclude>ca/uhn/fhir/model/dstu2/**/*.class</exclude>