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.math.BigDecimal;
import java.util.List; import java.util.List;
import ca.uhn.fhir.model.base.composite.BaseQuantityDt;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -301,4 +302,22 @@ public class QuantityParam extends BaseParamWithPrefix<QuantityParam> implements
return b.toString(); 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: - item:
type: "add" type: "add"
title: "The version of a few dependencies have been bumped to the latest versions title: "The version of a few dependencies have been bumped to the latest versions
(dependent HAPI modules listed in brackets): (dependent HAPI modules listed in brackets):
<ul> * SLF4j (All Modules): 1.7.28 -> 1.7.30
<li>SLF4j (All Modules): 1.7.28 -&gt; 1.7.30</li> * Jackson (All Modules): 2.11.2 -> 2.12.1
<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> * 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)
<li>Spring (JPA): 5.2.3.RELEASE -&gt; 5.2.9.RELEASE</li> * Spring (JPA): 5.2.3.RELEASE -> 5.2.9.RELEASE
<li>Datasource-Proxy (JPA): 1.5.1 -&gt; 1.7</li> * Datasource-Proxy (JPA): 1.5.1 -> 1.7
<li>Jetty (JPA Starter): 9.4.30.v20200611 -&gt; 9.4.35.v20201120</li> * Jetty (JPA Starter): 9.4.30.v20200611 -> 9.4.35.v20201120
<li>Guava (JP): 29.0-jre -&gt; 30.1-jre</li> * Guava (JP): 29.0-jre -> 30.1-jre
<li>Hibernate ORM (JPA Server): 5.4.22.FINAL -&gt; 5.4.26.FINAL</li> * Hibernate ORM (JPA Server): 5.4.22.FINAL -> 5.4.26.FINAL
<li>Spring (JPA Server): 5.2.9.RELEASE -&gt; 5.3.2</li> * Spring (JPA Server): 5.2.9.RELEASE -> 5.3.2
<li>Spring Data (JPA Server): 2.2.0.RELEASE -&gt; 2.4.2</li> * Spring Data (JPA Server): 2.2.0.RELEASE -> 2.4.2
<li>Hibernate Search (JPA Server): 5.11.5.FINAL -&gt; 6.0.0.Final</li> * Hibernate Search (JPA Server): 5.11.5.FINAL -> 6.0.0.Final
<li>Lucene(HAPI FHIR JPA Server): 5.5.5 -&gt; 8.7.0</li> * Lucene(HAPI FHIR JPA Server): 5.5.5 -> 8.7.0
<li>Spring Boot (JPA Starter): 2.2.6.RELEASE -&gt; 2.4.1</li> * Spring Boot (JPA Starter): 2.2.6.RELEASE -> 2.4.1
</ul>" "

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.data;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity; 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.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
public interface IResourceIndexedSearchParamQuantityNormalizedDao extends JpaRepository<ResourceIndexedSearchParamQuantity, Long> { public interface IResourceIndexedSearchParamQuantityNormalizedDao extends JpaRepository<ResourceIndexedSearchParamQuantityNormalized, Long> {
@Modifying @Modifying
@Query("delete from ResourceIndexedSearchParamQuantityNormalized t WHERE t.myResourcePid = :resid") @Query("delete from ResourceIndexedSearchParamQuantityNormalized t WHERE t.myResourcePid = :resid")
void deleteByResourceId(@Param("resid") Long theResourcePid); 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.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; 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.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.BaseJoiningPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CompositeUniqueSearchParameterPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; 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.ResourceLinkPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; 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.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.SourcePredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder; 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.TagPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder; 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.predicate.UriPredicateBuilder;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor; import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -186,14 +188,9 @@ public class QueryStack {
public void addSortOnQuantity(String theResourceName, String theParamName, boolean theAscending) { public void addSortOnQuantity(String theResourceName, String theParamName, boolean theAscending) {
BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder();
QuantityBasePredicateBuilder sortPredicateBuilder = null; QuantityBasePredicateBuilder sortPredicateBuilder;
if (myModelConfig.isNormalizedQuantitySearchSupported()) {
sortPredicateBuilder = mySqlBuilder.addQuantityNormalizedPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
} else {
sortPredicateBuilder = mySqlBuilder.addQuantityPredicateBuilder(firstPredicateBuilder.getResourceIdColumn()); sortPredicateBuilder = mySqlBuilder.addQuantityPredicateBuilder(firstPredicateBuilder.getResourceIdColumn());
}
Condition hashIdentityPredicate = sortPredicateBuilder.createHashIdentityPredicate(theResourceName, theParamName); Condition hashIdentityPredicate = sortPredicateBuilder.createHashIdentityPredicate(theResourceName, theParamName);
mySqlBuilder.addPredicate(hashIdentityPredicate); mySqlBuilder.addPredicate(hashIdentityPredicate);
mySqlBuilder.addSortNumeric(sortPredicateBuilder.getColumnValue(), theAscending); mySqlBuilder.addSortNumeric(sortPredicateBuilder.getColumnValue(), theAscending);
@ -643,19 +640,37 @@ public class QueryStack {
SearchFilterParser.CompareOperation theOperation, SearchFilterParser.CompareOperation theOperation,
RequestPartitionId theRequestPartitionId) { 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) { 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); 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<>(); 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); Condition singleCode = join.createPredicateQuantity(nextOr, theResourceName, theSearchParam.getName(), null, join, theOperation, theRequestPartitionId);
codePredicates.add(singleCode); codePredicates.add(singleCode);
} }

View File

@ -20,27 +20,10 @@ package ca.uhn.fhir.jpa.search.builder.predicate;
* #L% * #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.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser; import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamBaseQuantity; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamBaseQuantity;
import ca.uhn.fhir.jpa.search.builder.QueryStack; import ca.uhn.fhir.jpa.search.builder.QueryStack;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; 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.model.base.composite.BaseQuantityDt;
import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.QuantityParam; 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 { public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredicateBuilder {
@ -60,10 +54,6 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
@Autowired @Autowired
private FhirContext myFhirContext; private FhirContext myFhirContext;
@Autowired
private ModelConfig myModelConfig;
/** /**
* Constructor * Constructor
*/ */
@ -71,37 +61,12 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
super(theSearchSqlBuilder, theTable); super(theSearchSqlBuilder, theTable);
} }
public Condition createPredicateQuantity(IQueryParameterType theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, QuantityBasePredicateBuilder theFrom, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) { public Condition createPredicateQuantity(QuantityParam theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, QuantityBasePredicateBuilder theFrom, SearchFilterParser.CompareOperation theOperation, RequestPartitionId theRequestPartitionId) {
String systemValue; String systemValue = theParam.getSystem();
String unitsValue; String unitsValue = theParam.getUnits();
ParamPrefixEnum cmpValue; ParamPrefixEnum cmpValue = theParam.getPrefix();
BigDecimal valueValue; BigDecimal valueValue = theParam.getValue();
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());
}
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; Condition hashPredicate;
if (!isBlank(systemValue) && !isBlank(unitsValue)) { if (!isBlank(systemValue) && !isBlank(unitsValue)) {
@ -128,4 +93,5 @@ public abstract class QuantityBasePredicateBuilder extends BaseSearchParamPredic
public DbColumn getColumnValue() { public DbColumn getColumnValue() {
return myColumnValue; return myColumnValue;
} }
} }

View File

@ -1,18 +1,17 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import static org.hamcrest.MatcherAssert.assertThat; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import static org.hamcrest.Matchers.contains; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import static org.hamcrest.Matchers.empty; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import static org.hamcrest.Matchers.matchesPattern; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import static org.junit.jupiter.api.Assertions.assertEquals; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import static org.junit.jupiter.api.Assertions.assertTrue; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import static org.junit.jupiter.api.Assertions.fail; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.QuantityParam;
import java.io.IOException; import ca.uhn.fhir.rest.param.StringParam;
import java.math.BigDecimal; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import java.util.Date; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import java.util.List; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -33,15 +32,20 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import java.io.IOException;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import java.math.BigDecimal;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import java.util.Date;
import ca.uhn.fhir.rest.param.QuantityParam; import java.util.List;
import ca.uhn.fhir.rest.param.StringParam; import java.util.stream.Collectors;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import static org.hamcrest.MatcherAssert.assertThat;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import static org.hamcrest.Matchers.contains;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; 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 { public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CreateTest.class); private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4CreateTest.class);
@ -51,7 +55,7 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy()); myDaoConfig.setResourceServerIdStrategy(new DaoConfig().getResourceServerIdStrategy());
myDaoConfig.setResourceClientIdStrategy(new DaoConfig().getResourceClientIdStrategy()); myDaoConfig.setResourceClientIdStrategy(new DaoConfig().getResourceClientIdStrategy());
myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden()); myDaoConfig.setDefaultSearchParamsCanBeOverridden(new DaoConfig().isDefaultSearchParamsCanBeOverridden());
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@Test @Test
@ -337,9 +341,9 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
} }
@Test @Test
public void testCreateWithNormalizedQuantitySearchSupported() { public void testCreateWithNormalizedQuantitySearchSupported_AlreadyCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation(); Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL); obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity(); Quantity q = new Quantity();
@ -353,29 +357,33 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
assertTrue(myObservationDao.create(obs).getCreated()); assertTrue(myObservationDao.create(obs).getCreated());
SearchParameterMap map = new SearchParameterMap(); // Same value should be placed in both quantity tables
map.setLoadSynchronous(true); runInTransaction(()->{
QuantityParam qp = new QuantityParam(); List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL); assertEquals(1, quantityIndexes.size());
qp.setValue(new BigDecimal("0.012")); assertEquals("1.2", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
qp.setUnits("m"); assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("cm", quantityIndexes.get(0).getUnits());
map.add(Observation.SP_VALUE_QUANTITY, qp); 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());
});
IBundleProvider found = myObservationDao.search(map); SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
List<String> ids = toUnqualifiedVersionlessIdValues(found); .setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("0.012"))
List<IBaseResource> resources = found.getResources(0, found.size()); .setUnits("m")
);
assertEquals(1, ids.size()); assertEquals(1, toUnqualifiedVersionlessIdValues(myObservationDao.search(map)).size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0)));
} }
@Test @Test
public void testCreateWithNormalizedQuantitySearchSupportedWithVerySmallNumber() { public void testCreateWithNormalizedQuantitySearchSupported_SmallerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation(); Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL); obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity(); Quantity q = new Quantity();
@ -387,36 +395,76 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.info("Observation1: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
myCaptureQueriesListener.clear();
assertTrue(myObservationDao.create(obs).getCreated()); assertTrue(myObservationDao.create(obs).getCreated());
myCaptureQueriesListener.logInsertQueries();
SearchParameterMap map = new SearchParameterMap(); // Original value should be in Quantity index, normalized should be in normalized table
map.setLoadSynchronous(true); runInTransaction(()->{
QuantityParam qp = new QuantityParam(); List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL); assertEquals(1, quantityIndexes.size());
qp.setValue(new BigDecimal("0.0000000012")); double d = quantityIndexes.get(0).getValue().doubleValue();
qp.setUnits("m"); assertEquals("1.2E-6", Double.toString(d));
assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("mm", quantityIndexes.get(0).getUnits());
map.add(Observation.SP_VALUE_QUANTITY, qp); 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());
});
IBundleProvider found = myObservationDao.search(map); String searchSql;
List<String> ids = toUnqualifiedVersionlessIdValues(found); SearchParameterMap map;
List<String> ids;
List<IBaseResource> resources = found.getResources(0, found.size());
// 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()); assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0))); // 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());
} }
@Test @Test
public void testCreateWithNormalizedQuantitySearchSupportedWithVerySmallNumber2() { public void testCreateWithNormalizedQuantitySearchSupported_SmallerThanCanonicalUnit2() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation(); Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL); obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity(); Quantity q = new Quantity();
q.setValueElement(new DecimalType(149597.870691)); q.setValueElement(new DecimalType("149597.870691"));
q.setUnit("MM"); q.setUnit("MM");
q.setSystem("http://unitsofmeasure.org"); q.setSystem("http://unitsofmeasure.org");
q.setCode("mm"); q.setCode("mm");
@ -426,6 +474,21 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
assertTrue(myObservationDao.create(obs).getCreated()); 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(); SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
QuantityParam qp = new QuantityParam(); QuantityParam qp = new QuantityParam();
@ -438,7 +501,7 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
IBundleProvider found = myObservationDao.search(map); IBundleProvider found = myObservationDao.search(map);
List<String> ids = toUnqualifiedVersionlessIdValues(found); 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()); assertEquals(1, ids.size());
@ -447,13 +510,13 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
} }
@Test @Test
public void testCreateWithNormalizedQuantitySearchSupportedWithLargeNumber() { public void testCreateWithNormalizedQuantitySearchSupported_LargerThanCanonicalUnit() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation(); Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL); obs.setStatus(Observation.ObservationStatus.FINAL);
Quantity q = new Quantity(); Quantity q = new Quantity();
q.setValueElement(new DecimalType(95.7412345)); q.setValueElement(new DecimalType("95.7412345"));
q.setUnit("kg/dL"); q.setUnit("kg/dL");
q.setSystem("http://unitsofmeasure.org"); q.setSystem("http://unitsofmeasure.org");
q.setCode("kg/dL"); q.setCode("kg/dL");
@ -463,23 +526,224 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
assertTrue(myObservationDao.create(obs).getCreated()); assertTrue(myObservationDao.create(obs).getCreated());
SearchParameterMap map = new SearchParameterMap(); // Original value should be in Quantity index, normalized should be in normalized table
map.setLoadSynchronous(true); runInTransaction(()->{
QuantityParam qp = new QuantityParam(); List<ResourceIndexedSearchParamQuantity> quantityIndexes = myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t->t.getParamName().equals("value-quantity")).collect(Collectors.toList());
qp.setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL); assertEquals(1, quantityIndexes.size());
qp.setValue(new BigDecimal("957412345")); assertEquals("95.7412345", Double.toString(quantityIndexes.get(0).getValue().doubleValue()));
qp.setUnits("g.m-3"); assertEquals("http://unitsofmeasure.org", quantityIndexes.get(0).getSystem());
assertEquals("kg/dL", quantityIndexes.get(0).getUnits());
map.add(Observation.SP_VALUE_QUANTITY, qp); 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());
});
IBundleProvider found = myObservationDao.search(map); SearchParameterMap map = SearchParameterMap.newSynchronous(Observation.SP_VALUE_QUANTITY, new QuantityParam()
List<String> ids = toUnqualifiedVersionlessIdValues(found); .setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL)
.setValue(new BigDecimal("957412345"))
.setUnits("g.m-3")
);
assertEquals(1, toUnqualifiedVersionlessIdValues(myObservationDao.search(map)).size());
}
List<IBaseResource> resources = found.getResources(0, found.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://example.com");
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://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()); assertEquals(1, ids.size());
ourLog.info("Observation2: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resources.get(0))); }
@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());
} }
} }

View File

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

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.DaoConfig; 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.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
@ -10,7 +11,16 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import org.hl7.fhir.instance.model.api.IIdType; 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.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -21,8 +31,12 @@ import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.contains;
import static org.junit.jupiter.api.Assertions.*; 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 { public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class); private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class);
@ -34,7 +48,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@AfterEach @AfterEach
public void afterResetSearch() { public void afterResetSearch() {
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@Test @Test
@ -82,7 +96,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() { public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Organization org = new Organization(); Organization org = new Organization();
org.setActive(true); org.setActive(true);
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
@ -99,7 +113,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() { public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED); myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
myModelConfig.setNormalizedQuantityStorageSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Organization org = new Organization(); Organization org = new Organization();
org.setActive(true); org.setActive(true);
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
@ -221,8 +235,8 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue(); 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(); String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
runInTransaction(()->{ runInTransaction(() -> {
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * "))); ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
}); });
{ {
@ -310,7 +324,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test @Test
public void testSearchWithMissingQuantityWithNormalizedQuantitySearchSupported() { public void testSearchWithMissingQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType notMissing; IIdType notMissing;
IIdType missing; IIdType missing;
{ {
@ -324,6 +338,12 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
obs.setValue(new Quantity(123)); obs.setValue(new Quantity(123));
notMissing = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); 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 // Quantity Param
{ {
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
@ -341,7 +361,9 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(true); param.setMissing(true);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
myCaptureQueriesListener.clear();
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
myCaptureQueriesListener.logSelectQueries();
assertThat(patients, containsInRelativeOrder(missing)); assertThat(patients, containsInRelativeOrder(missing));
assertThat(patients, not(containsInRelativeOrder(notMissing))); assertThat(patients, not(containsInRelativeOrder(notMissing)));
} }
@ -351,7 +373,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test @Test
public void testSearchWithMissingQuantityWithNormalizedQuantityStorageSupported() { public void testSearchWithMissingQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
IIdType notMissing; IIdType notMissing;
IIdType missing; IIdType missing;
{ {

View File

@ -34,6 +34,7 @@ import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils; import org.apache.commons.lang3.time.DateUtils;
@ -185,7 +186,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds()); myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@BeforeEach @BeforeEach
@ -1237,7 +1238,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testIndexNoDuplicatesQuantityWithNormalizedQuantitySearchSupported() { public void testIndexNoDuplicatesQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance(); 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);
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(); 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( List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 12300, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm")))); mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 12300, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm"))));
assertThat(actual, contains(id)); assertThat(actual, contains(id));
@ -1269,26 +1256,12 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testQuantityWithNormalizedQuantitySearchSupported_InvalidUCUMCode() { public void testQuantityWithNormalizedQuantitySearchSupported_InvalidUCUMCode() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance(); Substance res = new Substance();
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("FOO").setValue(123); res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless(); 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( List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "FOO")))); mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "FOO"))));
assertThat(actual, contains(id)); assertThat(actual, contains(id));
@ -1298,26 +1271,12 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testQuantityWithNormalizedQuantitySearchSupported_NotUCUM() { public void testQuantityWithNormalizedQuantitySearchSupported_NotUCUM() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance(); Substance res = new Substance();
res.addInstance().getQuantity().setSystem("http://bar").setCode("FOO").setValue(123); res.addInstance().getQuantity().setSystem("http://bar").setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless(); 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( List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://bar", "FOO")))); mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://bar", "FOO"))));
assertThat(actual, contains(id)); assertThat(actual, contains(id));
@ -1327,7 +1286,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testIndexNoDuplicatesQuantityWithNormalizedQuantityStorageSupported() { public void testIndexNoDuplicatesQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Substance res = new Substance(); 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);
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(); 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( List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "m")))); mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "m"))));
assertThat(actual, contains(id)); assertThat(actual, contains(id));
@ -2754,7 +2699,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testSearchByMoneyParamWithNormalizedQuantitySearchSupported() { public void testSearchByMoneyParamWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
ChargeItem ci = new ChargeItem(); ChargeItem ci = new ChargeItem();
ci.getPriceOverride().setValue(123).setCurrency("$"); ci.getPriceOverride().setValue(123).setCurrency("$");
@ -3120,7 +3065,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test @Test
public void testSearchQuantityWithNormalizedQuantitySearchSupported() { public void testSearchQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Condition c1 = new Condition(); Condition c1 = new Condition();
c1.setAbatement(new Range().setLow(new SimpleQuantity().setValue(1L)).setHigh(new SimpleQuantity().setValue(1L))); c1.setAbatement(new Range().setLow(new SimpleQuantity().setValue(1L)).setHigh(new SimpleQuantity().setValue(1L)));
String id1 = myConditionDao.create(c1).getId().toUnqualifiedVersionless().getValue(); 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.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; 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.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
@ -143,7 +144,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum()); myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum());
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches()); myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setDisableHashBasedSearches(false); myDaoConfig.setDisableHashBasedSearches(false);
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@BeforeEach @BeforeEach
@ -1194,7 +1195,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test @Test
public void testComponentQuantityWithNormalizedQuantitySearchSupported() { public void testComponentQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"))) .setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")))
@ -1217,7 +1218,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test @Test
public void testComponentQuantityWithNormalizedQuantityStorageSupported() { public void testComponentQuantityWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"))) .setCode(new CodeableConcept().addCoding(new Coding().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")))
@ -1293,7 +1294,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
@Test @Test
public void testSearchCompositeParamQuantityWithNormalizedQuantitySearchSupported() { public void testSearchCompositeParamQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1"))) .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.BaseHapiFhirResourceDao;
import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao;
import ca.uhn.fhir.jpa.entity.TermConcept; 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.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
@ -161,7 +162,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete()); myDaoConfig.setEnforceReferentialIntegrityOnDelete(new DaoConfig().isEnforceReferentialIntegrityOnDelete());
myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes()); myDaoConfig.setEnforceReferenceTargetTypes(new DaoConfig().isEnforceReferenceTargetTypes());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields()); myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@BeforeEach @BeforeEach
@ -582,7 +583,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test @Test
public void testChoiceParamQuantityWithNormalizedQuantitySearchSupported() { public void testChoiceParamQuantityWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o3 = new Observation(); Observation o3 = new Observation();
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03"); o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
o3.setValue(new Quantity(QuantityComparator.GREATER_THAN, 123.0, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm", "cm")); // 0.0123m o3.setValue(new Quantity(QuantityComparator.GREATER_THAN, 123.0, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm", "cm")); // 0.0123m
@ -683,7 +684,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
@Test @Test
public void testChoiceParamQuantityPrecisionWithNormalizedQuantitySearchSupported() { public void testChoiceParamQuantityPrecisionWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o3 = new Observation(); Observation o3 = new Observation();
o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03"); o3.getCode().addCoding().setSystem("foo").setCode("testChoiceParam03");
o3.setValue(new Quantity(null, 123.01, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm", "cm")); // 0.012301 m 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 @Test
public void testSortByQuantityWithNormalizedQuantitySearchSupported() { @Disabled
public void testSortByQuantityWithNormalizedQuantitySearchFullSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); // myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED);
Observation res; Observation res;
res = new Observation(); 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.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; 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.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; 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.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IAnyResource; 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.Resource;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -110,7 +109,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
public void after() { public void after() {
myDaoConfig.setAllowInlineMatchUrlReferences(false); myDaoConfig.setAllowInlineMatchUrlReferences(false);
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete()); myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@BeforeEach @BeforeEach
@ -3131,7 +3130,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
@Test @Test
public void testTransactionWithConditionalUpdateDoesntUpdateIfNoChangeWithNormalizedQuantitySearchSupported() { public void testTransactionWithConditionalUpdateDoesntUpdateIfNoChangeWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation obs = new Observation(); Observation obs = new Observation();
obs.addIdentifier() obs.addIdentifier()
.setSystem("http://acme.org") .setSystem("http://acme.org")

View File

@ -1,20 +1,29 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import static java.util.Comparator.comparing; import ca.uhn.fhir.context.FhirContext;
import static org.junit.jupiter.api.Assertions.assertEquals; import ca.uhn.fhir.context.FhirVersionEnum;
import static org.junit.jupiter.api.Assertions.assertNotNull; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import java.math.BigDecimal; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
import java.util.ArrayList; import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
import java.util.Collection; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import java.util.HashMap; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import java.util.HashSet; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import java.util.List; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import java.util.Map; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import java.util.Set; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import java.util.stream.Collectors; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import org.hl7.fhir.dstu2.model.SimpleQuantity; 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.BooleanType;
import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding; 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.Observation;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Quantity; 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.Reference;
import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 static java.util.Comparator.comparing;
import ca.uhn.fhir.context.FhirVersionEnum; import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import static org.junit.jupiter.api.Assertions.assertNotNull;
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;
public class SearchParamExtractorR4Test { public class SearchParamExtractorR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParamExtractorR4Test.class); private static final Logger ourLog = LoggerFactory.getLogger(SearchParamExtractorR4Test.class);
private static FhirContext ourCtx = FhirContext.forCached(FhirVersionEnum.R4); private static FhirContext ourCtx = FhirContext.forCached(FhirVersionEnum.R4);
private static IValidationSupport ourValidationSupport;
private MySearchParamRegistry mySearchParamRegistry; private MySearchParamRegistry mySearchParamRegistry;
private PartitionSettings myPartitionSettings; private PartitionSettings myPartitionSettings;
@ -81,7 +73,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE"); 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); Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size()); assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next(); ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -95,7 +87,7 @@ public class SearchParamExtractorR4Test {
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.addUseContext().setCode(new Coding().setSystem("http://system").setCode("code")); 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); Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(sp);
assertEquals(1, tokens.size()); assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next(); ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -109,7 +101,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -138,7 +130,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -162,7 +154,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -191,7 +183,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -216,7 +208,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -246,7 +238,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation(); Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug"); 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) List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream() .stream()
@ -267,7 +259,7 @@ public class SearchParamExtractorR4Test {
Encounter enc = new Encounter(); Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location/123")); 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"); RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Encounter", "location");
assertNotNull(param); assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(enc); ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(enc);
@ -282,7 +274,7 @@ public class SearchParamExtractorR4Test {
Consent consent = new Consent(); Consent consent = new Consent();
consent.setSource(new Reference().setReference("Consent/999")); 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); RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Consent", Consent.SP_SOURCE_REFERENCE);
assertNotNull(param); assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(consent); ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(consent);
@ -297,7 +289,7 @@ public class SearchParamExtractorR4Test {
Patient p = new Patient(); Patient p = new Patient();
p.addIdentifier().setSystem("sys").setValue("val"); 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); RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Patient", Patient.SP_IDENTIFIER);
assertNotNull(param); assertNotNull(param);
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> params = extractor.extractSearchParamTokens(p, param); ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> params = extractor.extractSearchParamTokens(p, param);
@ -319,7 +311,7 @@ public class SearchParamExtractorR4Test {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addExtension("http://patext", new Reference("Organization/AAA")); 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); ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(patient);
assertEquals(1, links.size()); assertEquals(1, links.size());
@ -335,7 +327,7 @@ public class SearchParamExtractorR4Test {
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2"))) .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200)); .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); Set<ResourceIndexedSearchParamQuantity> links = extractor.extractSearchParamQuantity(o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n "))); ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(4, links.size()); assertEquals(4, links.size());
@ -346,14 +338,14 @@ public class SearchParamExtractorR4Test {
ModelConfig modelConfig = new ModelConfig(); ModelConfig modelConfig = new ModelConfig();
modelConfig.setNormalizedQuantitySearchSupported(); modelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1"))) .setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code1")))
.setValue(new Quantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm").setValue(200)); .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); Set<ResourceIndexedSearchParamQuantityNormalized> links = extractor.extractSearchParamQuantityNormalized(o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n "))); ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(2, links.size()); assertEquals(2, links.size());
@ -365,7 +357,7 @@ public class SearchParamExtractorR4Test {
ModelConfig modelConfig = new ModelConfig(); ModelConfig modelConfig = new ModelConfig();
modelConfig.setNormalizedQuantitySearchSupported(); modelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
@ -375,10 +367,10 @@ public class SearchParamExtractorR4Test {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("component-value-quantity"); 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); List<String> list = extractor.extractParamValuesAsStrings(existingCodeSp, o1);
assertEquals(1, list.size()); assertEquals(2, list.size());
} }
private static class MySearchParamRegistry implements ISearchParamRegistry { 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.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; 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.search.PersistedJpaSearchFirstPageBundleProvider;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -62,7 +63,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@AfterEach @AfterEach
public void afterDisableExpunge() { public void afterDisableExpunge() {
myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled()); myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled());
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
@BeforeEach @BeforeEach
@ -402,7 +403,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testExpungeSystemEverythingWithNormalizedQuantitySearchSupported() { public void testExpungeSystemEverythingWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
createStandardPatients(); createStandardPatients();
mySystemDao.expunge(new ExpungeOptions() mySystemDao.expunge(new ExpungeOptions()
@ -424,7 +425,7 @@ public class ExpungeR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testExpungeSystemEverythingWithNormalizedQuantityStorageSupported() { public void testExpungeSystemEverythingWithNormalizedQuantityStorageSupported() {
myModelConfig.setNormalizedQuantityStorageSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
createStandardPatients(); createStandardPatients();
mySystemDao.expunge(new ExpungeOptions() mySystemDao.expunge(new ExpungeOptions()

View File

@ -47,6 +47,7 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
@ -217,7 +218,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE); mySearchCoordinatorSvcRaw.setSyncSizeForUnitTests(SearchCoordinatorSvcImpl.DEFAULT_SYNC_SIZE);
mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false); mySearchCoordinatorSvcRaw.setNeverUseLocalSearchForUnitTests(false);
mySearchCoordinatorSvcRaw.cancelAllActiveSearches(); mySearchCoordinatorSvcRaw.cancelAllActiveSearches();
myDaoConfig.getModelConfig().setNormalizedQuantitySearchNotSupported(); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
myClient.unregisterInterceptor(myCapturingInterceptor); myClient.unregisterInterceptor(myCapturingInterceptor);
} }
@ -4086,7 +4087,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported() throws Exception { public void testSearchWithNormalizedQuantitySearchSupported() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported(); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0; IIdType pid0;
{ {
Patient patient = new Patient(); Patient patient = new Patient();
@ -4168,7 +4169,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported_CombineUCUMOrNonUCUM() throws Exception { public void testSearchWithNormalizedQuantitySearchSupported_CombineUCUMOrNonUCUM() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported(); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0; IIdType pid0;
{ {
Patient patient = new Patient(); Patient patient = new Patient();
@ -4222,11 +4223,17 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.info("Observation: \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
// > 1m String uri;
String uri = ourServerBase + "/Observation?value-quantity=" + UrlUtil.escapeUrlParam("100|http://unitsofmeasure.org|cm,100|http://foo|cm"); List<String> ids;
ourLog.info("uri = " + uri); // With non-normalized
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); 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()); assertEquals(2, ids.size());
} }
@ -6045,7 +6052,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
@Test @Test
public void testUpdateWithNormalizedQuantitySearchSupported() throws Exception { public void testUpdateWithNormalizedQuantitySearchSupported() throws Exception {
myDaoConfig.getModelConfig().setNormalizedQuantitySearchSupported(); myDaoConfig.getModelConfig().setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
IIdType pid0; IIdType pid0;
{ {
Patient patient = new Patient(); 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.config.TestR4Config;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; 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.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
@ -103,7 +104,7 @@ public class InMemorySubscriptionMatcherR4Test {
@AfterEach @AfterEach
public void after() throws Exception { public void after() throws Exception {
myModelConfig.setNormalizedQuantitySearchNotSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
} }
private void assertMatched(Resource resource, SearchParameterMap params) { private void assertMatched(Resource resource, SearchParameterMap params) {
@ -250,7 +251,7 @@ public class InMemorySubscriptionMatcherR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported() { public void testSearchWithNormalizedQuantitySearchSupported() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
@ -282,7 +283,7 @@ public class InMemorySubscriptionMatcherR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported_InvalidUCUMUnit() { public void testSearchWithNormalizedQuantitySearchSupported_InvalidUCUMUnit() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
@ -298,7 +299,7 @@ public class InMemorySubscriptionMatcherR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported_NoSystem() { public void testSearchWithNormalizedQuantitySearchSupported_NoSystem() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()
@ -315,7 +316,7 @@ public class InMemorySubscriptionMatcherR4Test {
@Test @Test
public void testSearchWithNormalizedQuantitySearchSupported_NotUcumSystem() { public void testSearchWithNormalizedQuantitySearchSupported_NotUcumSystem() {
myModelConfig.setNormalizedQuantitySearchSupported(); myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Observation o1 = new Observation(); Observation o1 = new Observation();
o1.addComponent() o1.addComponent()

View File

@ -30,6 +30,6 @@ public enum ColumnTypeEnum {
FLOAT, FLOAT,
INT, INT,
BLOB, BLOB,
CLOB CLOB,
DOUBLE;
} }

View File

@ -49,6 +49,14 @@ public class ColumnTypeToDriverTypeToSqlType {
setColumnType(ColumnTypeEnum.FLOAT, DriverTypeEnum.ORACLE_12C, "float"); setColumnType(ColumnTypeEnum.FLOAT, DriverTypeEnum.ORACLE_12C, "float");
setColumnType(ColumnTypeEnum.FLOAT, DriverTypeEnum.POSTGRES_9_4, "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.H2_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.DERBY_EMBEDDED, "bigint"); setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.DERBY_EMBEDDED, "bigint");
setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.MARIADB_10_1, "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") .addForeignKey("20210111.3", "FKRCJOVMUH5KC0O6FVBLE319PYV")
.toColumn("RES_ID") .toColumn("RES_ID")
.references("HFJ_RESOURCE", "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() { protected void init520() {

View File

@ -576,6 +576,47 @@ public class ModelConfig {
myPeriodIndexEndOfTime = thePeriodIndexEndOfTime; 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) { private static void validateTreatBaseUrlsAsLocal(String theUrl) {
Validate.notBlank(theUrl, "Base URL must not be null or empty"); 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 * The existing non normalized quantity will be not supported
* NOTE this option is not supported in this release * NOTE this option is not supported in this release
*/ */
// When this is enabled, we can enable testSortByQuantityWithNormalizedQuantitySearchFullSupported()
//NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED, //NORMALIZED_QUANTITY_SEARCH_FULL_SUPPORTED,
} }

View File

@ -69,7 +69,7 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
@Column(name = "SP_VALUE", nullable = true) @Column(name = "SP_VALUE", nullable = true)
@ScaledNumberField @ScaledNumberField
public BigDecimal myValue; public Double myValue;
public ResourceIndexedSearchParamQuantity() { public ResourceIndexedSearchParamQuantity() {
super(); super();
@ -99,11 +99,11 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
} }
public BigDecimal getValue() { public BigDecimal getValue() {
return myValue; return myValue != null ? new BigDecimal(myValue) : null;
} }
public ResourceIndexedSearchParamQuantity setValue(BigDecimal theValue) { public ResourceIndexedSearchParamQuantity setValue(BigDecimal theValue) {
myValue = theValue; myValue = theValue != null ? theValue.doubleValue() : null;
return this; 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.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.fhir.ucum.Pair; 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; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField;
@ -84,23 +83,14 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
super(); 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(); this();
setPartitionSettings(thePartitionSettings); setPartitionSettings(thePartitionSettings);
setResourceType(theResourceType); setResourceType(theResourceType);
setParamName(theParamName); setParamName(theParamName);
setSystem(theSystem); 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); setValue(theValue);
setUnits(theUnits); setUnits(theUnits);
}
calculateHashes(); calculateHashes();
} }
@ -124,14 +114,10 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
myValue = theValue; myValue = theValue;
return this; return this;
} }
public void setValue(BigDecimal theValue) {
if (theValue != null) public ResourceIndexedSearchParamQuantityNormalized setValue(double theValue) {
myValue = theValue.doubleValue(); myValue = theValue;
} return this;
public BigDecimal getValueBigDecimal() {
if (myValue == null)
return null;
return new BigDecimal(myValue);
} }
//-- myId //-- myId

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.model.util;
import java.io.InputStream; import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import ca.uhn.fhir.rest.param.QuantityParam;
import org.fhir.ucum.Decimal; import org.fhir.ucum.Decimal;
import org.fhir.ucum.Pair; import org.fhir.ucum.Pair;
import org.fhir.ucum.UcumEssenceService; import org.fhir.ucum.UcumEssenceService;
@ -32,6 +33,8 @@ import org.slf4j.LoggerFactory;
import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.ClasspathUtil;
import javax.annotation.Nullable;
/** /**
* It's a wrapper of UcumEssenceService * It's a wrapper of UcumEssenceService
* *
@ -85,7 +88,7 @@ public class UcumServiceUtil {
return null; return null;
init(); init();
Pair theCanonicalPair = null; Pair theCanonicalPair;
try { try {
Decimal theDecimal = new Decimal(theValue.toPlainString(), theValue.precision()); Decimal theDecimal = new Decimal(theValue.toPlainString(), theValue.precision());
@ -97,4 +100,19 @@ public class UcumServiceUtil {
return theCanonicalPair; 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; package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import org.junit.jupiter.api.Test; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class ResourceIndexedSearchParamQuantityNormalizedTest { 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 @Test
public void testEquals() { public void testEquals() {
@ -46,37 +25,5 @@ public class ResourceIndexedSearchParamQuantityNormalizedTest {
assertNotEquals(val1, ""); 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.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber; 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.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri; 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.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.primitive.BoundCodeDt; 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.ObjectUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; 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.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration; import org.hl7.fhir.instance.model.api.IBaseEnumeration;
@ -78,7 +80,6 @@ import java.util.TreeSet;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; 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.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim; import static org.apache.commons.lang3.StringUtils.trim;
@ -199,12 +200,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
break; break;
case REFERENCE: case REFERENCE:
extractor = createReferenceExtractor(); extractor = createReferenceExtractor();
return extractReferenceParamsAsQueryTokens(theSearchParam, theResource, extractor); break;
case QUANTITY: case QUANTITY:
if (myModelConfig.isNormalizedQuantitySearchSupported()) if (myModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
extractor = createQuantityNormalizedExtractor(theResource); extractor = new CompositeExtractor(
else createQuantityExtractor(theResource),
createQuantityNormalizedExtractor(theResource)
);
} else {
extractor = createQuantityExtractor(theResource); extractor = createQuantityExtractor(theResource);
}
break; break;
case URI: case URI:
extractor = createUriExtractor(theResource); extractor = createUriExtractor(theResource);
@ -216,6 +221,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
default: default:
throw new UnsupportedOperationException("Type " + theSearchParam.getParamType() + " not supported for extraction"); throw new UnsupportedOperationException("Type " + theSearchParam.getParamType() + " not supported for extraction");
} }
return extractParamsAsQueryTokens(theSearchParam, theResource, extractor); return extractParamsAsQueryTokens(theSearchParam, theResource, extractor);
} }
@ -559,9 +565,16 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String system = extractValueAsString(myQuantitySystemValueChild, theValue); String system = extractValueAsString(myQuantitySystemValueChild, theValue);
String code = extractValueAsString(myQuantityCodeValueChild, theValue); String code = extractValueAsString(myQuantityCodeValueChild, theValue);
ResourceIndexedSearchParamQuantityNormalized nextEntity = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, theSearchParam.getName(), nextValueValue, system, code); //-- 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); theParams.add(nextEntity);
} }
}
} }
private void addQuantity_Money(String theResourceType, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) { private void addQuantity_Money(String theResourceType, Set<ResourceIndexedSearchParamQuantity> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
@ -588,7 +601,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String nextValueCode = extractValueAsString(myMoneyCurrencyChild, theValue); String nextValueCode = extractValueAsString(myMoneyCurrencyChild, theValue);
String searchParamName = theSearchParam.getName(); String searchParamName = theSearchParam.getName();
ResourceIndexedSearchParamQuantityNormalized nextEntityNormalized = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, searchParamName, nextValueValue, nextValueString, nextValueCode); ResourceIndexedSearchParamQuantityNormalized nextEntityNormalized = new ResourceIndexedSearchParamQuantityNormalized(myPartitionSettings, theResourceType, searchParamName, nextValueValue.doubleValue(), nextValueString, nextValueCode);
theParams.add(nextEntityNormalized); theParams.add(nextEntityNormalized);
} }
} }
@ -1163,11 +1176,27 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@FunctionalInterface @FunctionalInterface
private interface IExtractor<T> { private interface IExtractor<T> {
void extract(SearchParamSet<T> theParams, RuntimeSearchParam theSearchParam, IBase theValue, String thePath); 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 class ResourceLinkExtractor implements IExtractor<PathAndRef> {
private PathAndRef myPathAndRef = null; 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.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; 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.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam; 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 org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -51,6 +57,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; 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.compare;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -158,21 +165,30 @@ public final class ResourceIndexedSearchParams {
return myPopulatedResourceLinkParameters; 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) { if (theParamDef == null) {
return false; return false;
} }
Collection<? extends BaseResourceIndexedSearchParam> resourceParams; Collection<? extends BaseResourceIndexedSearchParam> resourceParams = null;
IQueryParameterType value = theValue;
switch (theParamDef.getParamType()) { switch (theParamDef.getParamType()) {
case TOKEN: case TOKEN:
resourceParams = myTokenParams; resourceParams = myTokenParams;
break; break;
case QUANTITY: case QUANTITY:
if (theModelConfig.isNormalizedQuantitySearchSupported()) if (theModelConfig.getNormalizedQuantitySearchLevel().equals(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED)) {
QuantityParam quantity = QuantityParam.toQuantityParam(theValue);
QuantityParam normalized = UcumServiceUtil.toCanonicalQuantityOrNull(quantity);
if (normalized != null) {
resourceParams = myQuantityNormalizedParams; resourceParams = myQuantityNormalizedParams;
else value = normalized;
}
}
if (resourceParams == null) {
resourceParams = myQuantityParams; resourceParams = myQuantityParams;
}
break; break;
case STRING: case STRING:
resourceParams = myStringParams; resourceParams = myStringParams;
@ -187,7 +203,7 @@ public final class ResourceIndexedSearchParams {
resourceParams = myDateParams; resourceParams = myDateParams;
break; break;
case REFERENCE: case REFERENCE:
return matchResourceLinks(theModelConfig, theResourceName, theParamName, theParam, theParamDef.getPath()); return matchResourceLinks(theModelConfig, theResourceName, theParamName, value, theParamDef.getPath());
case COMPOSITE: case COMPOSITE:
case HAS: case HAS:
case SPECIAL: case SPECIAL:
@ -197,11 +213,16 @@ public final class ResourceIndexedSearchParams {
if (resourceParams == null) { if (resourceParams == null) {
return false; 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,9 +296,6 @@ public final class ResourceIndexedSearchParams {
public void findMissingSearchParams(PartitionSettings thePartitionSettings, ModelConfig theModelConfig, ResourceTable theEntity, Set<Entry<String, RuntimeSearchParam>> theActiveSearchParams) { 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.STRING, myStringParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.NUMBER, myNumberParams); 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.DATE, myDateParams);
findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams); findMissingSearchParams(thePartitionSettings, theModelConfig, theEntity, theActiveSearchParams, RestSearchParameterTypeEnum.URI, myUriParams);
@ -309,9 +327,6 @@ public final class ResourceIndexedSearchParams {
param = new ResourceIndexedSearchParamNumber(); param = new ResourceIndexedSearchParamNumber();
break; break;
case QUANTITY: case QUANTITY:
if (theModelConfig.isNormalizedQuantitySearchSupported())
param = new ResourceIndexedSearchParamQuantityNormalized();
else
param = new ResourceIndexedSearchParamQuantity(); param = new ResourceIndexedSearchParamQuantity();
break; break;
case STRING: case STRING:

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.searchparam.extractor;
*/ */
import ca.uhn.fhir.context.FhirContext; 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.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -50,9 +49,9 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
// This constructor is used by tests // This constructor is used by tests
@VisibleForTesting @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); super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPathEngine(theValidationSupport); initFhirPathEngine();
start(); start();
} }
@ -75,13 +74,12 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
public void start() { public void start() {
super.start(); super.start();
if (myFhirPathEngine == null) { if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class); initFhirPathEngine();
initFhirPathEngine(support);
} }
} }
public void initFhirPathEngine(IValidationSupport theSupport) { public void initFhirPathEngine() {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport); IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker); 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.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -62,9 +61,9 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
// This constructor is used by tests // This constructor is used by tests
@VisibleForTesting @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); super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theValidationSupport); initFhirPath();
start(); start();
} }
@ -82,13 +81,12 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
public void start() { public void start() {
super.start(); super.start();
if (myFhirPathEngine == null) { if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class); initFhirPath();
initFhirPath(support);
} }
} }
public void initFhirPath(IValidationSupport theSupport) { public void initFhirPath() {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport); IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker); myFhirPathEngine = new FHIRPathEngine(worker);
myFhirPathEngine.setHostServices(new SearchParamExtractorR4HostServices()); myFhirPathEngine.setHostServices(new SearchParamExtractorR4HostServices());
} }
@ -96,7 +94,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
private static class SearchParamExtractorR4HostServices implements FHIRPathEngine.IEvaluationContext { 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 @Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { 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.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.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -58,9 +56,9 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
/** /**
* Constructor for unit tests * 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); super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theDefaultProfileValidationSupport); initFhirPath();
start(); start();
} }
@ -69,13 +67,12 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
public void start() { public void start() {
super.start(); super.start();
if (myFhirPathEngine == null) { if (myFhirPathEngine == null) {
IValidationSupport support = myApplicationContext.getBean(IValidationSupport.class); initFhirPath();
initFhirPath(support);
} }
} }
public void initFhirPath(IValidationSupport theSupport) { public void initFhirPath() {
IWorkerContext worker = new HapiWorkerContext(getContext(), theSupport); IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
myFhirPathEngine = new FHIRPathEngine(worker); myFhirPathEngine = new FHIRPathEngine(worker);
myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices()); myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices());
} }
@ -88,7 +85,7 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
private static class SearchParamExtractorR5HostServices implements FHIRPathEngine.IEvaluationContext { 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 @Override
public Base resolveConstant(Object appContext, String name, boolean beforeContext) throws PathEngineException { 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.cross.IResourceLookup;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
@ -120,7 +121,7 @@ public class SearchParamExtractorService {
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities); handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantities);
theParams.myQuantityParams.addAll(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); ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamQuantityNormalized> quantitiesNormalized = extractSearchParamQuantityNormalized(theResource);
handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized); handleWarnings(theRequestDetails, myInterceptorBroadcaster, quantitiesNormalized);
theParams.myQuantityNormalizedParams.addAll(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.extractor.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.util.StopWatch; 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.hl7.fhir.dstu3.model.Patient;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -23,7 +20,6 @@ import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -43,9 +39,8 @@ public class IndexStressTest {
FhirContext ctx = FhirContext.forDstu3(); FhirContext ctx = FhirContext.forDstu3();
IValidationSupport mockValidationSupport = mock(IValidationSupport.class); IValidationSupport mockValidationSupport = mock(IValidationSupport.class);
when(mockValidationSupport.getFhirContext()).thenReturn(ctx); when(mockValidationSupport.getFhirContext()).thenReturn(ctx);
IValidationSupport validationSupport = new CachingValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(ctx), mockValidationSupport));
ISearchParamRegistry searchParamRegistry = mock(ISearchParamRegistry.class); 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(); extractor.start();
Map<String, RuntimeSearchParam> spMap = ctx Map<String, RuntimeSearchParam> spMap = ctx

View File

@ -1,11 +1,10 @@
package ca.uhn.fhir.jpa.searchparam.extractor; package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; 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.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; 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.Patient;
import org.hl7.fhir.dstu3.model.Questionnaire; import org.hl7.fhir.dstu3.model.Questionnaire;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.text.Normalizer; import java.text.Normalizer;
@ -51,8 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class SearchParamExtractorDstu3Test { public class SearchParamExtractorDstu3Test {
private static FhirContext ourCtx = FhirContext.forDstu3(); private static FhirContext ourCtx = FhirContext.forCached(FhirVersionEnum.DSTU3);
private static IValidationSupport ourValidationSupport;
@Test @Test
public void testParamWithOrInPath() { public void testParamWithOrInPath() {
@ -61,7 +58,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs); Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size()); assertEquals(1, tokens.size());
@ -84,7 +81,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
Set<ResourceIndexedSearchParamString> params = extractor.extractSearchParamStrings(questionnaire); Set<ResourceIndexedSearchParamString> params = extractor.extractSearchParamStrings(questionnaire);
assertEquals(1, params.size()); assertEquals(1, params.size());
@ -102,7 +99,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc); Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size()); assertEquals(1, params.size());
@ -120,7 +117,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc); Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size()); assertEquals(1, params.size());
@ -132,7 +129,7 @@ public class SearchParamExtractorDstu3Test {
public void testEmptyPath() { public void testEmptyPath() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
searchParamRegistry.addSearchParam(new RuntimeSearchParam("foo", "foo", "", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE)); 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() { public void testStringMissingResourceType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); 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)); 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() { public void testInvalidType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
{ {
@ -216,7 +213,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(); 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(); extractor.start();
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> coords = extractor.extractSearchParamTokens(loc); ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> coords = extractor.extractSearchParamTokens(loc);
assertEquals(1, coords.size()); assertEquals(1, coords.size());
@ -227,7 +224,7 @@ public class SearchParamExtractorDstu3Test {
private static class MySearchParamRegistry implements ISearchParamRegistry { private static class MySearchParamRegistry implements ISearchParamRegistry {
private List<RuntimeSearchParam> myAddedSearchParams = new ArrayList<>(); private final List<RuntimeSearchParam> myAddedSearchParams = new ArrayList<>();
public void addSearchParam(RuntimeSearchParam... theSearchParam) { public void addSearchParam(RuntimeSearchParam... theSearchParam) {
myAddedSearchParams.clear(); myAddedSearchParams.clear();
@ -306,9 +303,4 @@ public class SearchParamExtractorDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest(); 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.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; 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.cache.ResourceChangeResult;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.entity.ModelConfig;
@ -66,15 +65,15 @@ public class SearchParamExtractorMegaTest {
ctx = FhirContext.forDstu3(); ctx = FhirContext.forDstu3();
searchParamRegistry = new MySearchParamRegistry(ctx); 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(); ctx = FhirContext.forR4();
searchParamRegistry = new MySearchParamRegistry(ctx); 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(); ctx = FhirContext.forR5();
searchParamRegistry = new MySearchParamRegistry(ctx); 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 { private void process(FhirContext theCtx, BaseSearchParamExtractor theExtractor) throws Exception {

View File

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