Allow disabling text modifier indexing (#1831)
* Allow disabling text modifier indexing * Add changelog * Address review comment * Resolve merge conflicts
This commit is contained in:
parent
dada217966
commit
35c2b7a2c1
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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."
|
|
@ -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"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue