diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchParamTextPropertyBinder.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchParamTextPropertyBinder.java index fe80987f3d3..0859b5ba363 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchParamTextPropertyBinder.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/SearchParamTextPropertyBinder.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.model.search; import org.hibernate.search.engine.backend.document.DocumentElement; import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement; import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaObjectField; +import org.hibernate.search.engine.backend.types.Aggregable; import org.hibernate.search.engine.backend.types.ObjectStructure; import org.hibernate.search.engine.backend.types.Projectable; import org.hibernate.search.engine.backend.types.dsl.IndexFieldTypeFactory; @@ -40,11 +41,10 @@ import static ca.uhn.fhir.jpa.model.search.HibernateSearchIndexWriter.IDX_STRING /** * Allows hibernate search to index - * + *

* CodeableConcept.text * Coding.display * Identifier.type.text - * */ public class SearchParamTextPropertyBinder implements PropertyBinder, PropertyBridge { @@ -69,22 +69,24 @@ public class SearchParamTextPropertyBinder implements PropertyBinder, PropertyBr //create them adhoc. https://docs.jboss.org/hibernate/search/6.0/reference/en-US/html_single/#mapper-orm-bridge-index-field-dsl-dynamic //I _think_ im doing the right thing here by indicating that everything matching this template uses this analyzer. IndexFieldTypeFactory indexFieldTypeFactory = thePropertyBindingContext.typeFactory(); - StringIndexFieldTypeOptionsStep standardAnalyzer = - indexFieldTypeFactory.asString() - // TODO mb Once Ken finishes extracting a common base, we can share these constants with HapiElasticsearchAnalysisConfigurer and HapiLuceneAnalysisConfigurer - .analyzer("standardAnalyzer") - .projectable(Projectable.NO); + // TODO mb Once Ken finishes extracting a common base, we can share these constants with HapiElasticsearchAnalysisConfigurer and HapiLuceneAnalysisConfigurer + StringIndexFieldTypeOptionsStep standardAnalyzer = indexFieldTypeFactory.asString() + .analyzer("standardAnalyzer") + .projectable(Projectable.NO); StringIndexFieldTypeOptionsStep exactAnalyzer = indexFieldTypeFactory.asString() .analyzer("exactAnalyzer") // default max-length is 256. Is that enough for code system uris? .projectable(Projectable.NO); - StringIndexFieldTypeOptionsStep normStringAnalyzer = - indexFieldTypeFactory.asString() - .analyzer("normStringAnalyzer") - .projectable(Projectable.NO); + StringIndexFieldTypeOptionsStep normStringAnalyzer = indexFieldTypeFactory.asString() + .analyzer("normStringAnalyzer") + .projectable(Projectable.NO); + // TODO JB: may have to add normalizer to support case insensitive searches depending on token flags + StringIndexFieldTypeOptionsStep keywordFieldType = indexFieldTypeFactory.asString() + .projectable(Projectable.NO) + .aggregable(Aggregable.YES); // the old style for _text and _contains @@ -119,11 +121,14 @@ public class SearchParamTextPropertyBinder implements PropertyBinder, PropertyBr // But the standard tokenizers aren't that flexible. As second best, it would be nice to use elastic multi-fields // to apply three different tokenizers to a single value. // Instead, just be simple and expand into three full fields for now - spfield.objectFieldTemplate("tokenIndex", ObjectStructure.FLATTENED).matchingPathGlob("*.token"); - spfield.fieldTemplate("token-code", exactAnalyzer).matchingPathGlob("*.token.code").multiValued(); - spfield.fieldTemplate("token-code-system", exactAnalyzer).matchingPathGlob("*.token.code-system").multiValued(); - spfield.fieldTemplate("token-system", exactAnalyzer).matchingPathGlob("*.token.system").multiValued(); - spfield.fieldTemplate("reference-value", exactAnalyzer).matchingPathGlob("*.reference.value").multiValued(); + String tokenPathGlob = "*.token"; + spfield.objectFieldTemplate("tokenIndex", ObjectStructure.FLATTENED).matchingPathGlob(tokenPathGlob); + spfield.fieldTemplate("token-code", keywordFieldType).matchingPathGlob(tokenPathGlob + ".code").multiValued(); + spfield.fieldTemplate("token-code-system", keywordFieldType).matchingPathGlob(tokenPathGlob + ".code-system").multiValued(); + spfield.fieldTemplate("token-system", keywordFieldType).matchingPathGlob(tokenPathGlob + ".system").multiValued(); + + // reference + spfield.fieldTemplate("reference-value", keywordFieldType).matchingPathGlob("*.reference.value").multiValued(); // last, since the globs are matched in declaration order, and * matches even nested nodes. spfield.objectFieldTemplate("spObject", ObjectStructure.FLATTENED).matchingPathGlob("*");