mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-25 01:18:37 +00:00
New indexing for number and quantity search parameters (#3464)
* New Add Index forced-INCLUDE columns, and Online option * Remove forced-include. Simpler just to add all columns to index. * New date index definitions. * Oops. Remove test for deleted feature. * Cleanup * Drop IDX_SP_DATE_UPDATED * Add online option to drop index * Update annotations to match and redefine FK * Push sp->resource FK down to control name * remove dead end * Cleanup date tests * New token query test cases for sql extraction * New token search param indexing. * Continue to allow the legacy hibernate names while we update the indexing. * Fix jpa annotations with overloaded field * Decide on token sorting - we do it. * review comments * fixme * disable dead data migration too * review fixups * Test cases for numeric search * add ne and ap * Test for Numeric SP * start * finish number * new indexing for quantity * Move config * cleanup
This commit is contained in:
parent
80d1a5a6f8
commit
03b6c589c8
@ -285,7 +285,7 @@ public class TestUtil {
|
||||
Validate.notNull(fk);
|
||||
Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + theAnnotatedElement + " has no name()");
|
||||
List<String> legacySPHibernateFKNames = Arrays.asList(
|
||||
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKCLTIHNC5TGPRJ9BHPT7XI5OTB", "FKGXSREUTYMMFJUWDSWV3Y887DO");
|
||||
"FKC97MPK37OKWU8QVTCEG2NH9VN", "FKGXSREUTYMMFJUWDSWV3Y887DO");
|
||||
if (legacySPHibernateFKNames.contains(fk.name())) {
|
||||
// wipmb temporarily allow the hibernate legacy sp fk names
|
||||
} else {
|
||||
|
@ -165,7 +165,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||
{
|
||||
Builder.BuilderWithTableName tokenTable = version.onTable("HFJ_SPIDX_TOKEN");
|
||||
|
||||
// replace and drop IDX_SP_DATE_HASH for sorting
|
||||
// replace and drop IDX_SP_TOKEN_HASH for sorting
|
||||
tokenTable
|
||||
.addIndex("20220208.1", "IDX_SP_TOKEN_HASH_V2")
|
||||
.unique(false).online(true)
|
||||
@ -270,6 +270,157 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||
batchChunk.addIndex("20220227.4", "IDX_BT2WC_II_SEQ").unique(false).withColumns("INSTANCE_ID", "SEQ");
|
||||
batchChunk.addForeignKey("20220227.5", "FK_BT2WC_INSTANCE").toColumn("INSTANCE_ID").references("BT2_JOB_INSTANCE", "ID");
|
||||
|
||||
replaceNumericSPIndices(version);
|
||||
replaceQuantitySPIndices(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* new numeric search indexing
|
||||
* @see ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder
|
||||
* @see ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber
|
||||
*/
|
||||
private void replaceNumericSPIndices(Builder theVersion) {
|
||||
Builder.BuilderWithTableName numberTable = theVersion.onTable("HFJ_SPIDX_NUMBER");
|
||||
|
||||
// Main query index
|
||||
numberTable
|
||||
.addIndex("20220304.1", "IDX_SP_NUMBER_HASH_VAL_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
numberTable.dropIndexOnline("20220304.2", "IDX_SP_NUMBER_HASH_VAL");
|
||||
|
||||
// for joining to other queries
|
||||
{
|
||||
numberTable
|
||||
.addIndex("20220304.3", "IDX_SP_NUMBER_RESID_V2")
|
||||
.unique(false).online(true)
|
||||
.withColumns("RES_ID", "HASH_IDENTITY", "SP_VALUE", "PARTITION_ID");
|
||||
|
||||
// some engines tie the FK constraint to a particular index.
|
||||
// So we need to drop and recreate the constraint to drop the old RES_ID index.
|
||||
// Rename it while we're at it. FK7ULX3J1GG3V7MAQREJGC7YBC4 was not a pretty name.
|
||||
numberTable.dropForeignKey("20220304.4", "FKCLTIHNC5TGPRJ9BHPT7XI5OTB", "HFJ_RESOURCE");
|
||||
numberTable.dropIndexOnline("20220304.5", "IDX_SP_NUMBER_RESID");
|
||||
numberTable.dropIndexOnline("20220304.6", "FKCLTIHNC5TGPRJ9BHPT7XI5OTB");
|
||||
|
||||
numberTable.addForeignKey("20220304.7", "FK_SP_NUMBER_RES")
|
||||
.toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID");
|
||||
}
|
||||
// obsolete
|
||||
numberTable.dropIndexOnline("20220304.8", "IDX_SP_NUMBER_UPDATED");
|
||||
}
|
||||
|
||||
/**
|
||||
* new quantity search indexing
|
||||
* @see ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder
|
||||
* @see ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity
|
||||
* @see ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized
|
||||
*/
|
||||
private void replaceQuantitySPIndices(Builder theVersion) {
|
||||
{
|
||||
Builder.BuilderWithTableName quantityTable = theVersion.onTable("HFJ_SPIDX_QUANTITY");
|
||||
|
||||
// bare quantity
|
||||
quantityTable
|
||||
.addIndex("20220304.11", "IDX_SP_QUANTITY_HASH_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityTable.dropIndexOnline("20220304.12", "IDX_SP_QUANTITY_HASH");
|
||||
|
||||
// quantity with system+units
|
||||
quantityTable
|
||||
.addIndex("20220304.13", "IDX_SP_QUANTITY_HASH_SYSUN_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityTable.dropIndexOnline("20220304.14", "IDX_SP_QUANTITY_HASH_SYSUN");
|
||||
|
||||
// quantity with units
|
||||
quantityTable
|
||||
.addIndex("20220304.15", "IDX_SP_QUANTITY_HASH_UN_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY_AND_UNITS", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityTable.dropIndexOnline("20220304.16", "IDX_SP_QUANTITY_HASH_UN");
|
||||
|
||||
// for joining to other queries and sorts
|
||||
{
|
||||
quantityTable
|
||||
.addIndex("20220304.17", "IDX_SP_QUANTITY_RESID_V2")
|
||||
.unique(false).online(true)
|
||||
.withColumns("RES_ID", "HASH_IDENTITY", "HASH_IDENTITY_SYS_UNITS", "HASH_IDENTITY_AND_UNITS", "SP_VALUE", "PARTITION_ID");
|
||||
|
||||
// some engines tie the FK constraint to a particular index.
|
||||
// So we need to drop and recreate the constraint to drop the old RES_ID index.
|
||||
// Rename it while we're at it. FK7ULX3J1GG3V7MAQREJGC7YBC4 was not a pretty name.
|
||||
quantityTable.dropForeignKey("20220304.18", "FKN603WJJOI1A6ASEWXBBD78BI5", "HFJ_RESOURCE");
|
||||
quantityTable.dropIndexOnline("20220304.19", "IDX_SP_QUANTITY_RESID");
|
||||
quantityTable.dropIndexOnline("20220304.20", "FKN603WJJOI1A6ASEWXBBD78BI5");
|
||||
|
||||
quantityTable.addForeignKey("20220304.21", "FK_SP_QUANTITY_RES")
|
||||
.toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID");
|
||||
}
|
||||
// obsolete
|
||||
quantityTable.dropIndexOnline("20220304.22", "IDX_SP_QUANTITY_UPDATED");
|
||||
}
|
||||
|
||||
{
|
||||
Builder.BuilderWithTableName quantityNormTable = theVersion.onTable("HFJ_SPIDX_QUANTITY_NRML");
|
||||
|
||||
// bare quantity
|
||||
quantityNormTable
|
||||
.addIndex("20220304.23", "IDX_SP_QNTY_NRML_HASH_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityNormTable.dropIndexOnline("20220304.24", "IDX_SP_QNTY_NRML_HASH");
|
||||
|
||||
// quantity with system+units
|
||||
quantityNormTable
|
||||
.addIndex("20220304.25", "IDX_SP_QNTY_NRML_HASH_SYSUN_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityNormTable.dropIndexOnline("20220304.26", "IDX_SP_QNTY_NRML_HASH_SYSUN");
|
||||
|
||||
// quantity with units
|
||||
quantityNormTable
|
||||
.addIndex("20220304.27", "IDX_SP_QNTY_NRML_HASH_UN_V2")
|
||||
.unique(false)
|
||||
.online(true)
|
||||
.withColumns("HASH_IDENTITY_AND_UNITS", "SP_VALUE", "RES_ID", "PARTITION_ID");
|
||||
|
||||
quantityNormTable.dropIndexOnline("20220304.28", "IDX_SP_QNTY_NRML_HASH_UN");
|
||||
|
||||
// for joining to other queries and sorts
|
||||
{
|
||||
quantityNormTable
|
||||
.addIndex("20220304.29", "IDX_SP_QNTY_NRML_RESID_V2")
|
||||
.unique(false).online(true)
|
||||
.withColumns("RES_ID", "HASH_IDENTITY", "HASH_IDENTITY_SYS_UNITS", "HASH_IDENTITY_AND_UNITS", "SP_VALUE", "PARTITION_ID");
|
||||
|
||||
// some engines tie the FK constraint to a particular index.
|
||||
// So we need to drop and recreate the constraint to drop the old RES_ID index.
|
||||
// Rename it while we're at it. FK7ULX3J1GG3V7MAQREJGC7YBC4 was not a pretty name.
|
||||
quantityNormTable.dropForeignKey("20220304.30", "FKRCJOVMUH5KC0O6FVBLE319PYV", "HFJ_RESOURCE");
|
||||
quantityNormTable.dropIndexOnline("20220304.31", "IDX_SP_QNTY_NRML_RESID");
|
||||
quantityNormTable.dropIndexOnline("20220304.32", "FKRCJOVMUH5KC0O6FVBLE319PYV");
|
||||
|
||||
quantityNormTable.addForeignKey("20220304.33", "FK_SP_QUANTITYNM_RES")
|
||||
.toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID");
|
||||
}
|
||||
// obsolete
|
||||
quantityNormTable.dropIndexOnline("20220304.34", "IDX_SP_QNTY_NRML_UPDATED");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1263,7 +1414,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||
spidxNumber
|
||||
.addIndex("20180903.13", "IDX_SP_NUMBER_HASH_VAL")
|
||||
.unique(false)
|
||||
.withColumns("HASH_IDENTITY", "SP_VALUE");
|
||||
.withColumns("HASH_IDENTITY", "SP_VALUE")
|
||||
.doNothing();
|
||||
spidxNumber
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.14")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
|
@ -1,23 +0,0 @@
|
||||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.DaoTestDataBuilder;
|
||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class TestDataBuilderConfig {
|
||||
|
||||
@Autowired
|
||||
FhirContext myFhirContext;
|
||||
@Autowired
|
||||
DaoRegistry myDaoRegistry;
|
||||
|
||||
@Bean
|
||||
DaoTestDataBuilder testDataBuilder() {
|
||||
return new DaoTestDataBuilder(myFhirContext, myDaoRegistry, new SystemRequestDetails());
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.method.SortParameter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simplistic implementation of FHIR queries.
|
||||
*/
|
||||
public class TestDaoSearch {
|
||||
@Configuration
|
||||
public static class Config {
|
||||
@Bean
|
||||
TestDaoSearch testDaoSearch(
|
||||
@Autowired FhirContext theFhirContext,
|
||||
@Autowired DaoRegistry theDaoRegistry,
|
||||
@Autowired MatchUrlService theMatchUrlService
|
||||
) {
|
||||
return new TestDaoSearch(theFhirContext, theDaoRegistry, theMatchUrlService);
|
||||
}
|
||||
}
|
||||
|
||||
final MatchUrlService myMatchUrlService;
|
||||
final DaoRegistry myDaoRegistry;
|
||||
final FhirContext myFhirCtx;
|
||||
|
||||
public TestDaoSearch(FhirContext theFhirCtx, DaoRegistry theDaoRegistry, MatchUrlService theMatchUrlService) {
|
||||
myMatchUrlService = theMatchUrlService;
|
||||
myDaoRegistry = theDaoRegistry;
|
||||
myFhirCtx = theFhirCtx;
|
||||
}
|
||||
|
||||
public List<String> searchForIds(String theQueryUrl) {
|
||||
// fake out the server url parsing
|
||||
IBundleProvider result = searchForBundleProvider(theQueryUrl);
|
||||
|
||||
List<String> resourceIds = result.getAllResourceIds();
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
public IBundleProvider searchForBundleProvider(String theQueryUrl) {
|
||||
ResourceSearch search = myMatchUrlService.getResourceSearch(theQueryUrl);
|
||||
SearchParameterMap map = search.getSearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
SystemRequestDetails request = fakeRequestDetailsFromUrl(theQueryUrl);
|
||||
SortSpec sort = (SortSpec) new SortParameter(myFhirCtx).translateQueryParametersIntoServerArgument(request, null);
|
||||
if (sort != null) {
|
||||
map.setSort(sort);
|
||||
}
|
||||
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(search.getResourceName());
|
||||
IBundleProvider result = dao.search(map, request);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private SystemRequestDetails fakeRequestDetailsFromUrl(String theQueryUrl) {
|
||||
SystemRequestDetails request = new SystemRequestDetails();
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(theQueryUrl).build();
|
||||
uriComponents.getQueryParams()
|
||||
.forEach((key, value) -> request.addParameter(key, value.toArray(new String[0])));
|
||||
return request;
|
||||
}
|
||||
}
|
@ -8,38 +8,23 @@ import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.jpa.dao.BaseDateSearchDaoTests;
|
||||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||
import ca.uhn.fhir.jpa.dao.DaoTestDataBuilder;
|
||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||
import ca.uhn.fhir.jpa.dao.TestDaoSearch;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.method.SortParameter;
|
||||
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
|
||||
import org.apache.commons.lang3.tuple.ImmutableTriple;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@ -49,10 +34,14 @@ import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = {TestR4Config.class, TestHibernateSearchAddInConfig.NoFT.class})
|
||||
@ContextConfiguration(classes = {
|
||||
TestR4Config.class,
|
||||
TestHibernateSearchAddInConfig.NoFT.class,
|
||||
DaoTestDataBuilder.Config.class,
|
||||
TestDaoSearch.Config.class
|
||||
})
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4StandardQueriesNoFTTest.class);
|
||||
@Autowired
|
||||
PlatformTransactionManager myTxManager;
|
||||
@Autowired
|
||||
@ -64,6 +53,11 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest {
|
||||
protected DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
MatchUrlService myMatchUrlService;
|
||||
@RegisterExtension
|
||||
@Autowired
|
||||
DaoTestDataBuilder myDataBuilder;
|
||||
@Autowired
|
||||
TestDaoSearch myTestDaoSearch;
|
||||
|
||||
@Override
|
||||
protected PlatformTransactionManager getTxManager() {
|
||||
@ -75,53 +69,17 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest {
|
||||
return myFhirCtx;
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
public class DateSearchTests extends BaseDateSearchDaoTests {
|
||||
@Override
|
||||
protected Fixture constructFixture() {
|
||||
DaoTestDataBuilder testDataBuilder = new DaoTestDataBuilder(myFhirCtx, myDaoRegistry, new SystemRequestDetails());
|
||||
return new TestDataBuilderFixture<>(testDataBuilder, myObservationDao);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TokenTestCase {
|
||||
|
||||
private Consumer<IBaseResource>[] myBuilders;
|
||||
private List<ImmutableTriple<Boolean, String, String>> mySearchCases = new ArrayList<>();
|
||||
|
||||
public static TokenTestCase onObservation(Consumer<IBaseResource>... theBuilders) {
|
||||
TokenTestCase result = new TokenTestCase();
|
||||
result.myBuilders = theBuilders;
|
||||
return result;
|
||||
}
|
||||
|
||||
public TokenTestCase finds(String theMessage, String theQuery) {
|
||||
mySearchCases.add(new ImmutableTriple(true,theMessage, theQuery));
|
||||
return this;
|
||||
}
|
||||
public TokenTestCase doesNotFind(String theMessage, String theQuery) {
|
||||
mySearchCases.add(new ImmutableTriple(false,theMessage, theQuery));
|
||||
return this;
|
||||
return new TestDataBuilderFixture<>(myDataBuilder, myObservationDao);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
public class TokenSearch {
|
||||
// wipmb make this generic and share with ES, and Mongo.
|
||||
/*
|
||||
String criteria = "_has:Condition:subject:code=http://snomed.info/sct|55822003,http://snomed.info/sct|55822005&" +
|
||||
"_has:Condition:asserter:code=http://snomed.info/sct|55822003,http://snomed.info/sct|55822004";
|
||||
*/
|
||||
|
||||
ITestDataBuilder myDataBuilder = new DaoTestDataBuilder(myFhirCtx, myDaoRegistry, new SystemRequestDetails());
|
||||
Set<IIdType> myCreatedIds = new HashSet<>();
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
ourLog.info("cleanup {}", myCreatedIds);
|
||||
myCreatedIds.forEach(myObservationDao::delete);
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
public class Queries {
|
||||
@ -194,59 +152,262 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest {
|
||||
String idExA = withObservation(myDataBuilder.withObservationCode("http://example.org", "AValue")).getIdPart();
|
||||
String idExM = withObservation(myDataBuilder.withObservationCode("http://example.org", "MValue")).getIdPart();
|
||||
|
||||
List<String> allIds = searchForIds("/Observation?_sort=code");
|
||||
List<String> allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code");
|
||||
assertThat(allIds, hasItems(idAlphaA, idAlphaM, idAlphaZ, idExA, idExD, idExM));
|
||||
|
||||
allIds = searchForIds("/Observation?_sort=code&code=http://example.org|");
|
||||
allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code&code=http://example.org|");
|
||||
assertThat(allIds, hasItems(idExA, idExD, idExM));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private IIdType withObservation(Consumer<IBaseResource>... theBuilder) {
|
||||
myObservationId = myDataBuilder.createObservation(theBuilder);
|
||||
myCreatedIds.add(myObservationId);
|
||||
return myObservationId;
|
||||
}
|
||||
|
||||
private void assertFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = searchForIds(theUrl);
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, hasItem(equalTo(myObservationId.getIdPart())));
|
||||
}
|
||||
|
||||
private void assertNotFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = searchForIds(theUrl);
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, not(hasItem(equalTo(myObservationId.getIdPart()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> searchForIds(String theQueryUrl) {
|
||||
// fake out the server url parsing
|
||||
ResourceSearch search = myMatchUrlService.getResourceSearch(theQueryUrl);
|
||||
SearchParameterMap map = search.getSearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
SystemRequestDetails request = fakeRequestDetailsFromUrl(theQueryUrl);
|
||||
SortSpec sort = (SortSpec) new SortParameter(myFhirCtx).translateQueryParametersIntoServerArgument(request, null);
|
||||
if (sort != null) {
|
||||
map.setSort(sort);
|
||||
@Nested
|
||||
public class NumericSearch {
|
||||
IIdType myResourceId;
|
||||
|
||||
@Nested
|
||||
public class Queries {
|
||||
|
||||
@Test
|
||||
public void eq() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertNotFind("when gt", "/RiskAssessment?probability=0.5");
|
||||
// fixme we break the spec here.
|
||||
// assertFind("when a little gt - default is approx", "/RiskAssessment?probability=0.599");
|
||||
// assertFind("when a little lt - default is approx", "/RiskAssessment?probability=0.601");
|
||||
assertFind("when eq", "/RiskAssessment?probability=0.6");
|
||||
assertNotFind("when lt", "/RiskAssessment?probability=0.7");
|
||||
}
|
||||
|
||||
IBundleProvider result = myObservationDao.search(map);
|
||||
@Test
|
||||
public void ne() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertFind("when gt", "/RiskAssessment?probability=ne0.5");
|
||||
assertNotFind("when eq", "/RiskAssessment?probability=ne0.6");
|
||||
assertFind("when lt", "/RiskAssessment?probability=ne0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ap() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertNotFind("when gt", "/RiskAssessment?probability=ap0.5");
|
||||
assertFind("when a little gt", "/RiskAssessment?probability=ap0.58");
|
||||
assertFind("when eq", "/RiskAssessment?probability=ap0.6");
|
||||
assertFind("when a little lt", "/RiskAssessment?probability=ap0.62");
|
||||
assertNotFind("when lt", "/RiskAssessment?probability=ap0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertFind("when gt", "/RiskAssessment?probability=gt0.5");
|
||||
assertNotFind("when eq", "/RiskAssessment?probability=gt0.6");
|
||||
assertNotFind("when lt", "/RiskAssessment?probability=gt0.7");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ge() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertFind("when gt", "/RiskAssessment?probability=ge0.5");
|
||||
assertFind("when eq", "/RiskAssessment?probability=ge0.6");
|
||||
assertNotFind("when lt", "/RiskAssessment?probability=ge0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertNotFind("when gt", "/RiskAssessment?probability=lt0.5");
|
||||
assertNotFind("when eq", "/RiskAssessment?probability=lt0.6");
|
||||
assertFind("when lt", "/RiskAssessment?probability=lt0.7");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void le() {
|
||||
withRiskAssessmentWithProbabilty(0.6);
|
||||
|
||||
assertNotFind("when gt", "/RiskAssessment?probability=le0.5");
|
||||
assertFind("when eq", "/RiskAssessment?probability=le0.6");
|
||||
assertFind("when lt", "/RiskAssessment?probability=le0.7");
|
||||
}
|
||||
|
||||
|
||||
private void assertFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, hasItem(equalTo(myResourceId.getIdPart())));
|
||||
}
|
||||
|
||||
private void assertNotFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, not(hasItem(equalTo(myResourceId.getIdPart()))));
|
||||
}
|
||||
}
|
||||
|
||||
private IIdType withRiskAssessmentWithProbabilty(double theValue) {
|
||||
myResourceId = myDataBuilder.createResource("RiskAssessment", myDataBuilder.withPrimitiveAttribute("prediction.probabilityDecimal", theValue));
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
@Nested
|
||||
public class Sorting {
|
||||
@Test
|
||||
public void sortByNumeric() {
|
||||
String idAlpha7 = withRiskAssessmentWithProbabilty(0.7).getIdPart();
|
||||
String idAlpha2 = withRiskAssessmentWithProbabilty(0.2).getIdPart();
|
||||
String idAlpha5 = withRiskAssessmentWithProbabilty(0.5).getIdPart();
|
||||
|
||||
List<String> allIds = myTestDaoSearch.searchForIds("/RiskAssessment?_sort=probability");
|
||||
assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7));
|
||||
}
|
||||
|
||||
List<String> resourceIds = result.getAllResourceIds();
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private SystemRequestDetails fakeRequestDetailsFromUrl(String theQueryUrl) {
|
||||
SystemRequestDetails request = new SystemRequestDetails();
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(theQueryUrl).build();
|
||||
uriComponents.getQueryParams().entrySet().forEach(nextEntry -> {
|
||||
request.addParameter(nextEntry.getKey(), nextEntry.getValue().toArray(new String[0]));
|
||||
});
|
||||
return request;
|
||||
@Nested
|
||||
public class QuantitySearch {
|
||||
IIdType myResourceId;
|
||||
|
||||
@Nested
|
||||
public class Queries {
|
||||
|
||||
@Test
|
||||
public void eq() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertNotFind("when gt", "/Observation?value-quantity=0.5||mmHg");
|
||||
assertNotFind("when gt unitless", "/Observation?value-quantity=0.5");
|
||||
// fixme we break the spec here.
|
||||
// assertFind("when a little gt - default is approx", "/Observation?value-quantity=0.599");
|
||||
// assertFind("when a little lt - default is approx", "/Observation?value-quantity=0.601");
|
||||
// fixme we don't seem to support "units", only "code".
|
||||
assertFind("when eq with units", "/Observation?value-quantity=0.6||mm[Hg]");
|
||||
assertFind("when eq unitless", "/Observation?value-quantity=0.6");
|
||||
assertNotFind("when lt", "/Observation?value-quantity=0.7||mmHg");
|
||||
assertNotFind("when lt", "/Observation?value-quantity=0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ne() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertFind("when gt", "/Observation?value-quantity=ne0.5");
|
||||
assertNotFind("when eq", "/Observation?value-quantity=ne0.6");
|
||||
assertFind("when lt", "/Observation?value-quantity=ne0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ap() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertNotFind("when gt", "/Observation?value-quantity=ap0.5");
|
||||
assertFind("when a little gt", "/Observation?value-quantity=ap0.58");
|
||||
assertFind("when eq", "/Observation?value-quantity=ap0.6");
|
||||
assertFind("when a little lt", "/Observation?value-quantity=ap0.62");
|
||||
assertNotFind("when lt", "/Observation?value-quantity=ap0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gt() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertFind("when gt", "/Observation?value-quantity=gt0.5");
|
||||
assertNotFind("when eq", "/Observation?value-quantity=gt0.6");
|
||||
assertNotFind("when lt", "/Observation?value-quantity=gt0.7");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ge() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertFind("when gt", "/Observation?value-quantity=ge0.5");
|
||||
assertFind("when eq", "/Observation?value-quantity=ge0.6");
|
||||
assertNotFind("when lt", "/Observation?value-quantity=ge0.7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lt() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertNotFind("when gt", "/Observation?value-quantity=lt0.5");
|
||||
assertNotFind("when eq", "/Observation?value-quantity=lt0.6");
|
||||
assertFind("when lt", "/Observation?value-quantity=lt0.7");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void le() {
|
||||
withObservationWithValueQuantity(0.6);
|
||||
|
||||
assertNotFind("when gt", "/Observation?value-quantity=le0.5");
|
||||
assertFind("when eq", "/Observation?value-quantity=le0.6");
|
||||
assertFind("when lt", "/Observation?value-quantity=le0.7");
|
||||
}
|
||||
|
||||
|
||||
private void assertFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, hasItem(equalTo(myResourceId.getIdPart())));
|
||||
}
|
||||
|
||||
private void assertNotFind(String theMessage, String theUrl) {
|
||||
List<String> resourceIds = myTestDaoSearch.searchForIds(theUrl);
|
||||
assertThat(theMessage, resourceIds, not(hasItem(equalTo(myResourceId.getIdPart()))));
|
||||
}
|
||||
}
|
||||
|
||||
private IIdType withObservationWithValueQuantity(double theValue) {
|
||||
// IBase quantity = myDataBuilder.withElementOfType("Quantity",
|
||||
// myDataBuilder.withPrimitiveAttribute("value", theValue),
|
||||
// myDataBuilder.withPrimitiveAttribute("unit", "mmHg"),
|
||||
// myDataBuilder.withPrimitiveAttribute("system", "http://unitsofmeasure.org"));
|
||||
myResourceId = myDataBuilder.createObservation(myDataBuilder.withAttribute("valueQuantity",
|
||||
myDataBuilder.withPrimitiveAttribute("value", theValue),
|
||||
myDataBuilder.withPrimitiveAttribute("unit", "mmHg"),
|
||||
myDataBuilder.withPrimitiveAttribute("system", "http://unitsofmeasure.org"),
|
||||
myDataBuilder.withPrimitiveAttribute("code", "mm[Hg]")
|
||||
));
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
@Nested
|
||||
public class Sorting {
|
||||
@Test
|
||||
public void sortByNumeric() {
|
||||
String idAlpha7 = withObservationWithValueQuantity(0.7).getIdPart();
|
||||
String idAlpha2 = withObservationWithValueQuantity(0.2).getIdPart();
|
||||
String idAlpha5 = withObservationWithValueQuantity(0.5).getIdPart();
|
||||
|
||||
List<String> allIds = myTestDaoSearch.searchForIds("/Observation?_sort=value-quantity");
|
||||
assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,11 +6,10 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper;
|
||||
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc;
|
||||
import ca.uhn.fhir.jpa.config.TestDataBuilderConfig;
|
||||
import ca.uhn.fhir.jpa.config.TestHibernateSearchAddInConfig;
|
||||
import ca.uhn.fhir.jpa.config.TestR4Config;
|
||||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||
import ca.uhn.fhir.jpa.dao.DaoTestDataBuilder;
|
||||
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
@ -50,7 +49,7 @@ import static org.hamcrest.Matchers.not;
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@RequiresDocker
|
||||
@ContextConfiguration(classes = {
|
||||
TestR4Config.class, TestHibernateSearchAddInConfig.Elasticsearch.class, TestDataBuilderConfig.class
|
||||
TestR4Config.class, TestHibernateSearchAddInConfig.Elasticsearch.class, DaoTestDataBuilder.Config.class
|
||||
})
|
||||
public class TokenAutocompleteElasticsearchIT extends BaseJpaTest{
|
||||
public static final Coding erythrocyte_by_volume = new Coding("http://loinc.org", "789-8", "Erythrocytes [#/volume] in Blood by Automated count");
|
||||
|
@ -20,17 +20,13 @@ package ca.uhn.fhir.jpa.model.entity;
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class ResourceIndexedSearchParamBaseQuantity extends BaseResourceIndexedSearchParam {
|
||||
@ -63,10 +59,6 @@ public abstract class ResourceIndexedSearchParamBaseQuantity extends BaseResourc
|
||||
@Column(name = "HASH_IDENTITY", nullable = true)
|
||||
private Long myHashIdentity;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false)
|
||||
private ResourceTable myResource;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@ -166,15 +158,4 @@ public abstract class ResourceIndexedSearchParamBaseQuantity extends BaseResourc
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) {
|
||||
myResource = theResource;
|
||||
setResourceType(theResource.getResourceType());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +49,8 @@ import java.util.Objects;
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_NUMBER", indexes = {
|
||||
// We used to have an index with name IDX_SP_NUMBER - Dont reuse
|
||||
@Index(name = "IDX_SP_NUMBER_HASH_VAL", columnList = "HASH_IDENTITY,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_NUMBER_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_NUMBER_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_NUMBER_HASH_VAL_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_NUMBER_RESID_V2", columnList = "RES_ID, HASH_IDENTITY, SP_VALUE, PARTITION_ID")
|
||||
})
|
||||
public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam {
|
||||
|
||||
@ -72,7 +71,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
|
||||
private Long myHashIdentity;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinColumn(foreignKey = @ForeignKey(name = "FKCLTIHNC5TGPRJ9BHPT7XI5OTB"),
|
||||
@JoinColumn(foreignKey = @ForeignKey(name = "FK_SP_NUMBER_RES"),
|
||||
name = "RES_ID", referencedColumnName = "RES_ID", nullable = false)
|
||||
private ResourceTable myResource;
|
||||
|
||||
|
@ -20,41 +20,42 @@ package ca.uhn.fhir.jpa.model.entity;
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_QUANTITY", indexes = {
|
||||
// We used to have an index named IDX_SP_QUANTITY - Dont reuse
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH", columnList = "HASH_IDENTITY,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH_UN", columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH_SYSUN", columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QUANTITY_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_QUANTITY_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH_UN_V2", columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QUANTITY_HASH_SYSUN_V2", columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QUANTITY_RESID_V2", columnList = "RES_ID,HASH_IDENTITY,HASH_IDENTITY_SYS_UNITS,HASH_IDENTITY_AND_UNITS,SP_VALUE,PARTITION_ID")
|
||||
})
|
||||
public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchParamBaseQuantity {
|
||||
|
||||
@ -70,6 +71,11 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
|
||||
@ScaledNumberField
|
||||
public Double myValue;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinColumn(foreignKey = @ForeignKey(name = "FK_SP_QUANTITY_RES"),
|
||||
name = "RES_ID", referencedColumnName = "RES_ID", nullable = false)
|
||||
private ResourceTable myResource;
|
||||
|
||||
public ResourceIndexedSearchParamQuantity() {
|
||||
super();
|
||||
}
|
||||
@ -196,4 +202,15 @@ public class ResourceIndexedSearchParamQuantity extends ResourceIndexedSearchPar
|
||||
return retval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) {
|
||||
myResource = theResource;
|
||||
setResourceType(theResource.getResourceType());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -20,43 +20,43 @@ package ca.uhn.fhir.jpa.model.entity;
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.fhir.ucum.Pair;
|
||||
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Objects;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
//@formatter:off
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_SPIDX_QUANTITY_NRML", indexes = {
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH", columnList = "HASH_IDENTITY,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH_UN", columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH_SYSUN", columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_UPDATED", columnList = "SP_UPDATED"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_RESID", columnList = "RES_ID")
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH_UN_V2", columnList = "HASH_IDENTITY_AND_UNITS,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_HASH_SYSUN_V2", columnList = "HASH_IDENTITY_SYS_UNITS,SP_VALUE,RES_ID,PARTITION_ID"),
|
||||
@Index(name = "IDX_SP_QNTY_NRML_RESID_V2", columnList = "RES_ID,HASH_IDENTITY,HASH_IDENTITY_SYS_UNITS,HASH_IDENTITY_AND_UNITS,SP_VALUE,PARTITION_ID")
|
||||
})
|
||||
/**
|
||||
* Support UCUM service
|
||||
@ -79,6 +79,11 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
|
||||
@ScaledNumberField
|
||||
public Double myValue;
|
||||
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinColumn(foreignKey = @ForeignKey(name = "FK_SP_QUANTITYNM_RES"),
|
||||
name = "RES_ID", referencedColumnName = "RES_ID", nullable = false)
|
||||
private ResourceTable myResource;
|
||||
|
||||
public ResourceIndexedSearchParamQuantityNormalized() {
|
||||
super();
|
||||
}
|
||||
@ -222,4 +227,16 @@ public class ResourceIndexedSearchParamQuantityNormalized extends ResourceIndexe
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) {
|
||||
myResource = theResource;
|
||||
setResourceType(theResource.getResourceType());
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -5,13 +5,25 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
public class DaoTestDataBuilder implements ITestDataBuilder, AfterEachCallback {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(DaoTestDataBuilder.class);
|
||||
|
||||
public class DaoTestDataBuilder implements ITestDataBuilder {
|
||||
final FhirContext myFhirCtx;
|
||||
final DaoRegistry myDaoRegistry;
|
||||
SystemRequestDetails mySrd;
|
||||
final SetMultimap<String, IIdType> myIds = HashMultimap.create();
|
||||
|
||||
public DaoTestDataBuilder(FhirContext theFhirCtx, DaoRegistry theDaoRegistry, SystemRequestDetails theSrd) {
|
||||
myFhirCtx = theFhirCtx;
|
||||
@ -24,7 +36,9 @@ public class DaoTestDataBuilder implements ITestDataBuilder {
|
||||
//noinspection rawtypes
|
||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass());
|
||||
//noinspection unchecked
|
||||
return dao.create(theResource, mySrd).getId().toUnqualifiedVersionless();
|
||||
IIdType id = dao.create(theResource, mySrd).getId().toUnqualifiedVersionless();
|
||||
myIds.put(theResource.fhirType(), id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -39,4 +53,34 @@ public class DaoTestDataBuilder implements ITestDataBuilder {
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirCtx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete anything created
|
||||
*/
|
||||
public void cleanup() {
|
||||
ourLog.info("cleanup {}", myIds);
|
||||
|
||||
myIds.keySet().forEach(nextType->{
|
||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(nextType);
|
||||
myIds.get(nextType).forEach(dao::delete);
|
||||
});
|
||||
myIds.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
DaoTestDataBuilder testDataBuilder(
|
||||
@Autowired FhirContext myFhirContext,
|
||||
@Autowired DaoRegistry myDaoRegistry
|
||||
) {
|
||||
return new DaoTestDataBuilder(myFhirContext, myDaoRegistry, new SystemRequestDetails());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,6 +185,39 @@ public interface ITestDataBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
default <T extends IBase> Consumer<T> withPrimitiveAttribute(String thePath, Object theValue) {
|
||||
return t->{
|
||||
FhirTerser terser = getFhirContext().newTerser();
|
||||
terser.addElement(t, thePath, ""+theValue);
|
||||
};
|
||||
}
|
||||
|
||||
default <T extends IBase> Consumer<T> withAttribute(String thePath, Consumer<IBase>... theModifiers) {
|
||||
return t->{
|
||||
FhirTerser terser = getFhirContext().newTerser();
|
||||
IBase element = terser.addElement(t, thePath);
|
||||
applyElementModifiers(element, theModifiers);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Element and apply modifiers
|
||||
* @param theElementType the FHIR Element type to create
|
||||
* @param theModifiers modifiers to apply after construction
|
||||
* @return the Element
|
||||
*/
|
||||
default IBase withElementOfType(String theElementType, Consumer<IBase>... theModifiers) {
|
||||
IBase element = getFhirContext().getElementDefinition(theElementType).newInstance();
|
||||
applyElementModifiers(element, theModifiers);
|
||||
return element;
|
||||
}
|
||||
|
||||
default void applyElementModifiers(IBase element, Consumer<IBase>[] theModifiers) {
|
||||
for (Consumer<IBase> nextModifier : theModifiers) {
|
||||
nextModifier.accept(element);
|
||||
}
|
||||
}
|
||||
|
||||
default Consumer<IBaseResource> withObservationCode(@Nullable String theSystem, @Nullable String theCode) {
|
||||
return withObservationCode(theSystem, theCode, null);
|
||||
}
|
||||
@ -252,5 +285,4 @@ public interface ITestDataBuilder {
|
||||
activeChild.getMutator().addValue(theTarget, booleanType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user