Allow disabling text modifier indexing (#1831)

* Allow disabling text modifier indexing

* Add changelog

* Address review comment

* Resolve merge conflicts
This commit is contained in:
James Agnew 2020-05-05 12:16:55 -04:00 committed by GitHub
parent dada217966
commit 35c2b7a2c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 422 additions and 82 deletions

View File

@ -5,7 +5,6 @@ import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IIdType;
@ -105,19 +104,30 @@ public class RuntimeSearchParam {
}
}
/**
* Constructor
*/
public RuntimeSearchParam(String theName, String theDescription, String thePath, RestSearchParameterTypeEnum theParamType, Set<String> theProvidesMembershipInCompartments, Set<String> theTargets, RuntimeSearchParamStatusEnum theStatus) {
this(null, null, theName, theDescription, thePath, theParamType, null, theProvidesMembershipInCompartments, theTargets, theStatus);
}
/**
* Retrieve user data - This can be used to store any application-specific data
*
* @return
* Copy constructor
*/
public RuntimeSearchParam(RuntimeSearchParam theSp) {
this(theSp.getId(), theSp.getUri(), theSp.getName(), theSp.getDescription(), theSp.getPath(), theSp.getParamType(), theSp.getCompositeOf(), theSp.getProvidesMembershipInCompartments(), theSp.getTargets(), theSp.getStatus(), theSp.getBase());
}
/**
* Retrieve user data - This can be used to store any application-specific data
*/
@Nonnull
public List<IBaseExtension<?, ?>> getExtensions(String theKey) {
List<IBaseExtension<?, ?>> retVal = myExtensions.get(theKey);
if (retVal != null) {
retVal = Collections.unmodifiableList(retVal);
} else {
retVal = Collections.emptyList();
}
return retVal;
}

View File

@ -122,6 +122,9 @@ ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.matchesFound=Matches found!
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.noMatchesFound=No matches found!
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1}
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderToken.textModifierDisabledForSearchParam=The :text modifier is disabled for this search parameter
ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderToken.textModifierDisabledForServer=The :text modifier is disabled on this server
ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor.successMsg=Cascaded delete to {0} resources: {1}
ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor.noParam=Note that cascading deletes are not active for this request. You can enable cascading deletes by using the "_cascade=delete" URL parameter.

View File

@ -0,0 +1,5 @@
---
type: add
issue: 1831
title: "Indexing for the :text modifier can now be globally or selectively disabled in the JPA server. This can have a measurable
impact on index sizes and write speed in servers with large numbers of token indexes."

View File

@ -9,3 +9,27 @@ On servers where a large amount of data will be ingested, the following consider
* Optimize your database thread pool count and HTTP client thread count: Every environment will have a different optimal setting for the number of concurrent writes that are permitted, and the maximum number of database connections allowed.
* Disable deletes: If the JPA server is configured to have the FHIR delete operation disabled, it is able to skip some resource reference deletion checks during resource creation, which can have a measurable improvement to performance over large datasets.
# Disabling :text Indexing
On servers storing large numbers of Codings and CodeableConcepts (as well as any other token SearchParameter target where the `:text` modifier is supported), the indexes required to support the `:text` modifier can consume a large amount of index space, and cause a masurable impact on write times.
This modifier can be disabled globally by using the ModelConfig#setSuppressStringIndexingInTokens setting.
It can also be disabled at a more granular level (or selectively re-enabled if it disabled globally) by using an extension on individual SearchParameter resources. For example, the following SearchParameter disables text indexing on the Observation:code parameter:
```json
{
"resourceType": "SearchParameter",
"id": "observation-code",
"extension": [ {
"url": "http://hapifhir.io/fhir/StructureDefinition/searchparameter-token-suppress-text-index",
"valueBoolean": true
} ],
"status": "active",
"code": "code",
"base": [ "Observation" ],
"type": "token",
"expression": "Observation.code"
}
```

View File

@ -28,8 +28,10 @@ import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.extractor.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
import ca.uhn.fhir.model.api.IQueryParameterType;
@ -39,6 +41,7 @@ import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.VersionIndependentConcept;
import com.google.common.collect.Sets;
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
@ -72,6 +75,8 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
private ITermReadSvc myTerminologySvc;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private ModelConfig myModelConfig;
PredicateBuilderToken(SearchBuilder theSearchBuilder, PredicateBuilder thePredicateBuilder) {
super(theSearchBuilder);
@ -99,6 +104,20 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
if (nextOr instanceof TokenParam) {
TokenParam id = (TokenParam) nextOr;
if (id.isText()) {
// Check whether the :text modifier is actually enabled here
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam(theResourceName, theParamName);
boolean tokenTextIndexingEnabled = BaseSearchParamExtractor.tokenTextIndexingEnabledForSearchParam(myModelConfig, param);
if (!tokenTextIndexingEnabled) {
String msg;
if (myModelConfig.isSuppressStringIndexingInTokens()) {
msg = myContext.getLocalizer().getMessage(PredicateBuilderToken.class, "textModifierDisabledForServer");
}else{
msg = myContext.getLocalizer().getMessage(PredicateBuilderToken.class, "textModifierDisabledForSearchParam");
}
throw new MethodNotAllowedException(msg);
}
myPredicateBuilder.addPredicateString(theResourceName, theParamName, theList, theRequestPartitionId);
break;
}

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
@ -16,6 +17,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
@ -108,6 +110,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Before
public void beforeDisableCacheReuse() {
myModelConfig.setSuppressStringIndexingInTokens(new ModelConfig().isSuppressStringIndexingInTokens());
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
}
@ -4355,6 +4358,50 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertEquals(2, mySearchEntityDao.count());
}
@Test
public void testTokenTextDisabled_Global() {
myModelConfig.setSuppressStringIndexingInTokens(true);
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add(Observation.SP_CODE, new TokenParam("hello").setModifier(TokenParamModifier.TEXT));
try {
myObservationDao.search(map);
} catch (MethodNotAllowedException e) {
assertEquals("The :text modifier is disabled on this server", e.getMessage());
}
}
@Test
public void testTokenTextDisabled_ForSearchParam() {
{
SearchParameter sp = new SearchParameter();
sp.setId("observation-code");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Observation");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setCode("code");
sp.setExpression("Observation.code");
sp.addExtension()
.setUrl(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING)
.setValue(new BooleanType(true));
ourLog.info("SP:\n{}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(sp));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
}
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add(Observation.SP_CODE, new TokenParam("hello").setModifier(TokenParamModifier.TEXT));
try {
myObservationDao.search(map);
} catch (MethodNotAllowedException e) {
assertEquals("The :text modifier is disabled for this search parameter", e.getMessage());
}
}
@Test
public void testDateSearchParametersShouldBeTimezoneIndependent() {

View File

@ -9,7 +9,9 @@ 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.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef;
@ -18,10 +20,12 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.TestUtil;
import com.google.common.collect.Sets;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Consent;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Quantity;
@ -43,6 +47,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static java.util.Comparator.comparing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -52,11 +57,13 @@ public class SearchParamExtractorR4Test {
private static FhirContext ourCtx = FhirContext.forR4();
private static IValidationSupport ourValidationSupport;
private MySearchParamRegistry mySearchParamRegistry;
private PartitionSettings myPartitionSettings;
@Before
public void before() {
mySearchParamRegistry = new MySearchParamRegistry();
myPartitionSettings = new PartitionSettings();
}
@ -65,8 +72,7 @@ public class SearchParamExtractorR4Test {
Observation obs = new Observation();
obs.addCategory().addCoding().setSystem("SYSTEM").setCode("CODE");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -80,8 +86,7 @@ public class SearchParamExtractorR4Test {
SearchParameter sp = new SearchParameter();
sp.addUseContext().setCode(new Coding().setSystem("http://system").setCode("code"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(sp);
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.iterator().next();
@ -90,12 +95,170 @@ public class SearchParamExtractorR4Test {
assertEquals("code", token.getValue());
}
@Test
public void testTokenText_Enabled_Coding() {
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("code"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(2, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("code", token.getParamName());
assertEquals("http://system", token.getSystem());
assertEquals("code", token.getValue());
ResourceIndexedSearchParamString string = (ResourceIndexedSearchParamString) tokens.get(1);
assertEquals("code", string.getParamName());
assertEquals("Help Im a Bug", string.getValueExact());
}
@Test
public void testTokenText_DisabledInSearchParam_Coding() {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("code");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("code"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("code", token.getParamName());
assertEquals("http://system", token.getSystem());
assertEquals("code", token.getValue());
}
@Test
public void testTokenText_DisabledInModelConfig_Coding() {
ModelConfig modelConfig = new ModelConfig();
modelConfig.setSuppressStringIndexingInTokens(true);
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("code"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("code", token.getParamName());
assertEquals("http://system", token.getSystem());
assertEquals("code", token.getValue());
}
@Test
public void testTokenText_DisabledInModelConfigButForcedInSearchParam_Coding() {
ModelConfig modelConfig = new ModelConfig();
modelConfig.setSuppressStringIndexingInTokens(true);
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("code");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(false)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
Observation obs = new Observation();
obs.getCode().addCoding().setSystem("http://system").setCode("code").setDisplay("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(modelConfig, myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("code"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(2, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("code", token.getParamName());
assertEquals("http://system", token.getSystem());
assertEquals("code", token.getValue());
ResourceIndexedSearchParamString string = (ResourceIndexedSearchParamString) tokens.get(1);
assertEquals("code", string.getParamName());
assertEquals("Help Im a Bug", string.getValueExact());
}
@Test
public void testTokenText_Enabled_Identifier() {
Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("identifier"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(2, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("identifier", token.getParamName());
assertEquals("sys", token.getSystem());
assertEquals("val", token.getValue());
ResourceIndexedSearchParamString string = (ResourceIndexedSearchParamString) tokens.get(1);
assertEquals("identifier", string.getParamName());
assertEquals("Help Im a Bug", string.getValueExact());
}
@Test
public void testTokenText_DisabledInSearchParam_Identifier() {
RuntimeSearchParam existingCodeSp = mySearchParamRegistry.getActiveSearchParams("Observation").get("identifier");
RuntimeSearchParam codeSearchParam = new RuntimeSearchParam(existingCodeSp);
codeSearchParam.addExtension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new Extension(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING, new BooleanType(true)));
mySearchParamRegistry.addSearchParam(codeSearchParam);
Observation obs = new Observation();
obs.addIdentifier().setSystem("sys").setValue("val").getType().setText("Help Im a Bug");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), myPartitionSettings, ourCtx, ourValidationSupport, mySearchParamRegistry);
List<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs)
.stream()
.filter(t -> t.getParamName().equals("identifier"))
.sorted(comparing(o -> o.getClass().getName()).reversed())
.collect(Collectors.toList());
assertEquals(1, tokens.size());
ResourceIndexedSearchParamToken token = (ResourceIndexedSearchParamToken) tokens.get(0);
assertEquals("identifier", token.getParamName());
assertEquals("sys", token.getSystem());
assertEquals("val", token.getValue());
}
@Test
public void testReferenceWithResolve() {
Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location/123"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Encounter", "location");
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(enc);
@ -110,8 +273,7 @@ public class SearchParamExtractorR4Test {
Consent consent = new Consent();
consent.setSource(new Reference().setReference("Consent/999"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Consent", Consent.SP_SOURCE_REFERENCE);
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(consent);
@ -126,8 +288,7 @@ public class SearchParamExtractorR4Test {
Patient p = new Patient();
p.addIdentifier().setSystem("sys").setValue("val");
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
RuntimeSearchParam param = mySearchParamRegistry.getActiveSearchParam("Patient", Patient.SP_IDENTIFIER);
assertNotNull(param);
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> params = extractor.extractSearchParamTokens(p, param);
@ -149,7 +310,7 @@ public class SearchParamExtractorR4Test {
Patient patient = new Patient();
patient.addExtension("http://patext", new Reference("Organization/AAA"));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
ISearchParamExtractor.SearchParamSet<PathAndRef> links = extractor.extractResourceLinks(patient);
assertEquals(1, links.size());
@ -165,7 +326,7 @@ public class SearchParamExtractorR4Test {
.setCode(new CodeableConcept().addCoding(new Coding().setSystem("http://foo").setCode("code2")))
.setValue(new Quantity().setSystem("http://bar").setCode("code2").setValue(200));
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), ourCtx, ourValidationSupport, mySearchParamRegistry, new PartitionSettings());
SearchParamExtractorR4 extractor = new SearchParamExtractorR4(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, mySearchParamRegistry);
Set<ResourceIndexedSearchParamQuantity> links = extractor.extractSearchParamQuantity(o1);
ourLog.info("Links:\n {}", links.stream().map(t -> t.toString()).collect(Collectors.joining("\n ")));
assertEquals(4, links.size());

View File

@ -65,6 +65,7 @@ public class ModelConfig {
* Update setter javadoc if default changes.
*/
private boolean myUseOrdinalDatesForDayPrecisionSearches = true;
private boolean mySuppressStringIndexingInTokens = false;
/**
* Constructor
@ -318,7 +319,6 @@ public class ModelConfig {
/**
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
* to the server matching these types will be activated.
*
*/
public ModelConfig addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) {
mySupportedSubscriptionTypes.add(theSubscriptionChannelType);
@ -328,7 +328,6 @@ public class ModelConfig {
/**
* This setting indicates which subscription channel types are supported by the server. Any subscriptions submitted
* to the server matching these types will be activated.
*
*/
public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() {
return Collections.unmodifiableSet(mySupportedSubscriptionTypes);
@ -376,18 +375,18 @@ public class ModelConfig {
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* <p>
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 5.0
* Default is {@literal true} beginning in HAPI FHIR 5.0.0
* </p>
*
* @since 5.0
* @since 5.0.0
*/
public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
public boolean getUseOrdinalDatesForDayPrecisionSearches() {
return myUseOrdinalDatesForDayPrecisionSearches;
}
/**
@ -395,18 +394,48 @@ public class ModelConfig {
* Should searches use the integer field {@code SP_VALUE_LOW_DATE_ORDINAL} and {@code SP_VALUE_HIGH_DATE_ORDINAL} in
* {@link ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate} when resolving searches where all predicates are using
* precision of {@link ca.uhn.fhir.model.api.TemporalPrecisionEnum#DAY}.
*
* <p>
* For example, if enabled, the search of {@code Observation?date=2020-02-25} will cause the date to be collapsed down to an
* integer representing the ordinal date {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* ordinal {@code 20200225}. It would then be compared against {@link ResourceIndexedSearchParamDate#getValueLowDateOrdinal()}
* and {@link ResourceIndexedSearchParamDate#getValueHighDateOrdinal()}
* </p>
* Default is {@literal true} beginning in HAPI FHIR 5.0
* Default is {@literal true} beginning in HAPI FHIR 5.0.0
* </p>
*
* @since 5.0
* @since 5.0.0
*/
public boolean getUseOrdinalDatesForDayPrecisionSearches() {
return myUseOrdinalDatesForDayPrecisionSearches;
public void setUseOrdinalDatesForDayPrecisionSearches(boolean theUseOrdinalDates) {
myUseOrdinalDatesForDayPrecisionSearches = theUseOrdinalDates;
}
/**
* If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
* the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
* will not be indexed for tokens:
* <ul>
* <li>CodeableConcept.text</li>
* <li>Coding.display</li>
* <li>Identifier.use.text</li>
* </ul>
* @since 5.0.0
*/
public boolean isSuppressStringIndexingInTokens() {
return mySuppressStringIndexingInTokens;
}
/**
* If set to <code>true</code> (default is <code>false</code>), when indexing SearchParameter values for token SearchParameter,
* the string component to support the <code>:text</code> modifier will be disabled. This means that the following fields
* will not be indexed for tokens:
* <ul>
* <li>CodeableConcept.text</li>
* <li>Coding.display</li>
* <li>Identifier.use.text</li>
* </ul>
* @since 5.0.0
*/
public void setSuppressStringIndexingInTokens(boolean theSuppressStringIndexingInTokens) {
mySuppressStringIndexingInTokens = theSuppressStringIndexingInTokens;
}
private static void validateTreatBaseUrlsAsLocal(String theUrl) {

View File

@ -238,6 +238,11 @@ public class JpaConstants {
*/
public static final String PARAM_EXPORT_TYPE_FILTER = "_typeFilter";
/**
* Extension URL for extension on a SearchParameter indicating that text values should not be indexed
*/
public static final String EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-token-suppress-text-index";
/**
* Non-instantiable
*/

View File

@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.model.util.StringNormalizer;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
@ -45,6 +46,7 @@ import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.hibernate.search.spatial.impl.Point;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
@ -131,6 +133,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private BaseRuntimeChildDefinition myCodingDisplayValueChild;
private BaseRuntimeChildDefinition myContactPointSystemValueChild;
private BaseRuntimeChildDefinition myPatientCommunicationLanguageValueChild;
/**
* Constructor
*/
@ -141,17 +144,15 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
/**
* UNIT TEST constructor
*/
BaseSearchParamExtractor(FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry, ModelConfig theModelConfig, PartitionSettings thePartitionSettings) {
BaseSearchParamExtractor(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
Validate.notNull(theModelConfig);
Validate.notNull(theCtx);
Validate.notNull(theSearchParamRegistry);
myModelConfig = theModelConfig;
myContext = theCtx;
mySearchParamRegistry = theSearchParamRegistry;
myPartitionSettings = thePartitionSettings;
myModelConfig = theModelConfig;
}
@VisibleForTesting
public BaseSearchParamExtractor setPartitionConfigForUnitTest(PartitionSettings thePartitionSettings) {
myPartitionSettings = thePartitionSettings;
return this;
}
@Override
@ -533,23 +534,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
createTokenIndexIfNotBlank(theResourceType, theParams, theSearchParam, system, value);
}
Optional<IBase> type = myIdentifierTypeValueChild.getAccessor().getFirstValueOrNull(theValue);
if (type.isPresent()) {
String text = extractValueAsString(myIdentifierTypeTextValueChild, type.get());
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
if (shouldIndexTextComponentOfToken(theSearchParam)) {
Optional<IBase> type = myIdentifierTypeValueChild.getAccessor().getFirstValueOrNull(theValue);
if (type.isPresent()) {
String text = extractValueAsString(myIdentifierTypeTextValueChild, type.get());
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
}
}
}
protected boolean shouldIndexTextComponentOfToken(RuntimeSearchParam theSearchParam) {
return tokenTextIndexingEnabledForSearchParam(myModelConfig, theSearchParam);
}
private void addToken_CodeableConcept(String theResourceType, Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
List<IBase> codings = myCodeableConceptCodingValueChild.getAccessor().getValues(theValue);
for (IBase nextCoding : codings) {
addToken_Coding(theResourceType, theParams, theSearchParam, nextCoding);
}
String text = extractValueAsString(myCodeableConceptTextValueChild, theValue);
if (isNotBlank(text)) {
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
if (shouldIndexTextComponentOfToken(theSearchParam)) {
String text = extractValueAsString(myCodeableConceptTextValueChild, theValue);
if (isNotBlank(text)) {
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
}
}
}
@ -558,8 +567,10 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String code = extractValueAsString(myCodingCodeValueChild, theValue);
createTokenIndexIfNotBlank(theResourceType, theParams, theSearchParam, system, code);
String text = extractValueAsString(myCodingDisplayValueChild, theValue);
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
if (shouldIndexTextComponentOfToken(theSearchParam)) {
String text = extractValueAsString(myCodingDisplayValueChild, theValue);
createStringIndexIfNotBlank(theResourceType, theParams, theSearchParam, text);
}
}
private void addToken_ContactPoint(String theResourceType, Set<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
@ -589,7 +600,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
String endAsString = extractValueAsString(myPeriodEndValueChild, theValue);
if (start != null || end != null) {
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionSettings ,theResourceType, theSearchParam.getName(), start, startAsString, end, endAsString, startAsString);
ResourceIndexedSearchParamDate nextEntity = new ResourceIndexedSearchParamDate(myPartitionSettings, theResourceType, theSearchParam.getName(), start, startAsString, end, endAsString, startAsString);
theParams.add(nextEntity);
}
}
@ -773,7 +784,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
private <T> SearchParamSet<T> extractSearchParams(IBaseResource theResource, IExtractor<T> theExtractor, RestSearchParameterTypeEnum theSearchParamType) {
SearchParamSet<T> retVal = new SearchParamSet<>();
@ -823,12 +833,11 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private void addDateTimeTypes(String theResourceType, Set<ResourceIndexedSearchParamDate> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
IPrimitiveType<Date> nextBaseDateTime = (IPrimitiveType<Date>) theValue;
if (nextBaseDateTime.getValue() != null) {
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(myPartitionSettings ,theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValueAsString());
ResourceIndexedSearchParamDate param = new ResourceIndexedSearchParamDate(myPartitionSettings, theResourceType, theSearchParam.getName(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValue(), nextBaseDateTime.getValueAsString(), nextBaseDateTime.getValueAsString());
theParams.add(param);
}
}
private void addUri_Uri(String theResourceType, Set<ResourceIndexedSearchParamUri> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
IPrimitiveType<?> value = (IPrimitiveType<?>) theValue;
String valueAsString = value.getValueAsString();
@ -1114,6 +1123,25 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
}
}
public static boolean tokenTextIndexingEnabledForSearchParam(ModelConfig theModelConfig, RuntimeSearchParam theSearchParam) {
Optional<Boolean> noSuppressForSearchParam = theSearchParam.getExtensions(JpaConstants.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING).stream()
.map(IBaseExtension::getValue)
.map(val -> (IPrimitiveType<?>) val)
.map(IPrimitiveType::getValueAsString)
.map(Boolean::parseBoolean)
.findFirst();
//if the SP doesn't care, use the system default.
if (!noSuppressForSearchParam.isPresent()) {
return !theModelConfig.isSuppressStringIndexingInTokens();
//If the SP does care, use its value.
} else {
boolean suppressForSearchParam = noSuppressForSearchParam.get();
ourLog.trace("Text indexing for SearchParameter {}: {}", theSearchParam.getName(), suppressForSearchParam);
return !suppressForSearchParam;
}
}
private static void addIgnoredType(FhirContext theCtx, String theType, Set<String> theIgnoredTypes) {
BaseRuntimeElementDefinition<?> elementDefinition = theCtx.getElementDefinition(theType);
if (elementDefinition != null) {

View File

@ -42,8 +42,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/**
* Constructor for unit tests
*/
SearchParamExtractorDstu2(FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry, ModelConfig theModelConfig, PartitionSettings thePartitionSettings) {
super(theCtx, theSearchParamRegistry, theModelConfig, thePartitionSettings);
SearchParamExtractorDstu2(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
start();
}

View File

@ -50,8 +50,8 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
// This constructor is used by tests
@VisibleForTesting
public SearchParamExtractorDstu3(ModelConfig theModelConfig, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry, PartitionSettings thePartitionSettings) {
super(theCtx, theSearchParamRegistry, theModelConfig, thePartitionSettings);
public SearchParamExtractorDstu3(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPathEngine(theValidationSupport);
start();
}

View File

@ -62,8 +62,8 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
// This constructor is used by tests
@VisibleForTesting
public SearchParamExtractorR4(ModelConfig theModelConfig, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry, PartitionSettings thePartitionSettings) {
super(theCtx, theSearchParamRegistry,theModelConfig, thePartitionSettings);
public SearchParamExtractorR4(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, IValidationSupport theValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theValidationSupport);
start();
}
@ -77,10 +77,6 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
}
@Override
@PostConstruct
public void start() {

View File

@ -58,8 +58,8 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
/**
* Constructor for unit tests
*/
public SearchParamExtractorR5(FhirContext theCtx, DefaultProfileValidationSupport theDefaultProfileValidationSupport, ISearchParamRegistry theSearchParamRegistry, ModelConfig theModelConfig, PartitionSettings thePartitionSettings) {
super(theCtx, theSearchParamRegistry, theModelConfig, thePartitionSettings);
public SearchParamExtractorR5(ModelConfig theModelConfig, PartitionSettings thePartitionSettings, FhirContext theCtx, DefaultProfileValidationSupport theDefaultProfileValidationSupport, ISearchParamRegistry theSearchParamRegistry) {
super(theModelConfig, thePartitionSettings, theCtx, theSearchParamRegistry);
initFhirPath(theDefaultProfileValidationSupport);
start();
}

View File

@ -45,7 +45,7 @@ public class IndexStressTest {
when(mockValidationSupport.getFhirContext()).thenReturn(ctx);
IValidationSupport validationSupport = new CachingValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(ctx), mockValidationSupport));
ISearchParamRegistry searchParamRegistry = mock(ISearchParamRegistry.class);
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ctx, validationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ctx, validationSupport, searchParamRegistry);
extractor.start();
Map<String, RuntimeSearchParam> spMap = ctx

View File

@ -58,8 +58,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
Set<BaseResourceIndexedSearchParam> tokens = extractor.extractSearchParamTokens(obs);
assertEquals(1, tokens.size());
@ -82,7 +81,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamString> params = extractor.extractSearchParamStrings(questionnaire);
assertEquals(1, params.size());
@ -100,7 +99,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size());
@ -118,7 +117,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
Set<ResourceIndexedSearchParamNumber> params = extractor.extractSearchParamNumber(enc);
assertEquals(1, params.size());
@ -130,7 +129,7 @@ public class SearchParamExtractorDstu3Test {
public void testEmptyPath() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
searchParamRegistry.addSearchParam(new RuntimeSearchParam("foo", "foo", "", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE));
@ -146,7 +145,7 @@ public class SearchParamExtractorDstu3Test {
public void testStringMissingResourceType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
searchParamRegistry.addSearchParam(new RuntimeSearchParam("foo", "foo", "communication.language.coding.system | communication.language.coding.code", RestSearchParameterTypeEnum.STRING, Sets.newHashSet(), Sets.newHashSet(), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE));
@ -163,8 +162,7 @@ public class SearchParamExtractorDstu3Test {
public void testInvalidType() {
MySearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
extractor.setPartitionConfigForUnitTest(new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
{
@ -215,7 +213,7 @@ public class SearchParamExtractorDstu3Test {
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry();
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), ourCtx, ourValidationSupport, searchParamRegistry, new PartitionSettings());
SearchParamExtractorDstu3 extractor = new SearchParamExtractorDstu3(new ModelConfig(), new PartitionSettings(), ourCtx, ourValidationSupport, searchParamRegistry);
extractor.start();
ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> coords = extractor.extractSearchParamTokens(loc);
assertEquals(1, coords.size());

View File

@ -1,11 +1,26 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import ca.uhn.fhir.context.*;
import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildContainedResources;
import ca.uhn.fhir.context.RuntimeChildDirectResource;
import ca.uhn.fhir.context.RuntimeChildExtension;
import ca.uhn.fhir.context.RuntimeChildResourceBlockDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -30,29 +45,30 @@ public class SearchParamExtractorMegaTest {
/**
* This test is my magnum opus :P
*
* <p>
* It navigates almost every possible path in every FHIR resource in every version of FHIR,
* and creates a resource with that path populated, just to ensure that we can index it
* without generating any warnings.
*/
@Test
public void testAllCombinations() throws Exception {
PartitionSettings partitionSettings = new PartitionSettings();
FhirContext ctx = FhirContext.forDstu2();
ISearchParamRegistry searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorDstu2(ctx, searchParamRegistry, new ModelConfig(), new PartitionSettings()).setPartitionConfigForUnitTest(new PartitionSettings()));
process(ctx, new SearchParamExtractorDstu2(new ModelConfig(), partitionSettings, ctx, searchParamRegistry));
ctx = FhirContext.forDstu3();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorDstu3(new ModelConfig(), ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry, new PartitionSettings()).setPartitionConfigForUnitTest(new PartitionSettings()));
process(ctx, new SearchParamExtractorDstu3(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
ctx = FhirContext.forR4();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorR4(new ModelConfig(), ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry, new PartitionSettings()).setPartitionConfigForUnitTest(new PartitionSettings()));
process(ctx, new SearchParamExtractorR4(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
ctx = FhirContext.forR5();
searchParamRegistry = new MySearchParamRegistry(ctx);
process(ctx, new SearchParamExtractorR5(ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry, new ModelConfig(), new PartitionSettings()).setPartitionConfigForUnitTest(new PartitionSettings()));
process(ctx, new SearchParamExtractorR5(new ModelConfig(), partitionSettings, ctx, new DefaultProfileValidationSupport(ctx), searchParamRegistry));
}
private void process(FhirContext theCtx, BaseSearchParamExtractor theExtractor) throws Exception {
@ -76,7 +92,6 @@ public class SearchParamExtractorMegaTest {
}
theElementStack.add(theElementDef);
if (theElementDef instanceof BaseRuntimeElementCompositeDefinition) {
@ -137,7 +152,7 @@ public class SearchParamExtractorMegaTest {
BaseRuntimeElementDefinition nextElement = theElementStack.get(i);
if (i > 0) {
previousChildArguments = theChildStack.get(i-1).getInstanceConstructorArguments();
previousChildArguments = theChildStack.get(i - 1).getInstanceConstructorArguments();
}
IBase nextObject = nextElement.newInstance(previousChildArguments);