Fast-path for lucene queries that skip the database (#3468)
* Store resource data inline with lucene/es resourcetable index. * Use fulltextsearch to load resources if resource inlined in ES/Lucene index Co-authored-by: Jaison Baskaran <jaisonb@gmail.com>
This commit is contained in:
parent
23cb65af25
commit
1dcc64ee44
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: perf
|
||||
issue: 3468
|
||||
title: "The resource JSON can now be stored and retrieved in the Lucene/Elasticsearch index. This enables some queries
|
||||
to provide results without using the database. This is enabled via `DaoConfig.setStoreResourceInLuceneIndex()`"
|
|
@ -40,6 +40,8 @@ will return a ValueSet containing the most common values indexed under `Observat
|
|||
display text contains a word starting with "press", such as `http://loinc.org|8478-0` - "Mean blood pressure".
|
||||
This extension is only valid at the type level, and requires that Extended Lucene Indexing be enabled.
|
||||
|
||||
## Resource Storage
|
||||
|
||||
|
||||
|
||||
As an experimental feature with the extended indexing, the full resource can be stored in the
|
||||
search index. This allows some queries to return results without using the relational database.
|
||||
Note: This does not support the $meta-add or $meta-delete operations.
|
||||
|
|
|
@ -1750,9 +1750,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
if (myDaoConfig.isAdvancedLuceneIndexing()) {
|
||||
ExtendedLuceneIndexData luceneIndexData = myFulltextSearchSvc.extractLuceneIndexData(theResource, theNewParams);
|
||||
theEntity.setLuceneIndexData(luceneIndexData);
|
||||
if(myDaoConfig.isStoreResourceInLuceneIndex()) {
|
||||
theEntity.setRawResourceData(theContext.newJsonParser().encodeResourceToString(theResource));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -814,6 +814,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource theEntity, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||
|
||||
// wipmb mb update hibernate search index if we are storing resources - it assumes inline tags.
|
||||
IBaseResource oldVersion = toResource(theEntity, false);
|
||||
|
||||
List<TagDefinition> tags = toTagList(theMetaDel);
|
||||
|
|
|
@ -58,6 +58,7 @@ import javax.persistence.EntityManager;
|
|||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -94,7 +95,7 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
|||
public ExtendedLuceneIndexData extractLuceneIndexData(IBaseResource theResource, ResourceIndexedSearchParams theNewParams) {
|
||||
String resourceType = myFhirContext.getResourceType(theResource);
|
||||
ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(resourceType);
|
||||
ExtendedLuceneIndexExtractor extractor = new ExtendedLuceneIndexExtractor(myFhirContext, activeSearchParams, mySearchParamExtractor);
|
||||
ExtendedLuceneIndexExtractor extractor = new ExtendedLuceneIndexExtractor(myDaoConfig, myFhirContext, activeSearchParams, mySearchParamExtractor);
|
||||
return extractor.extract(theResource,theNewParams);
|
||||
}
|
||||
|
||||
|
@ -259,6 +260,9 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
|
|||
|
||||
@Override
|
||||
public List<IBaseResource> getResources(Collection<Long> thePids) {
|
||||
if (thePids.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
SearchSession session = getSearchSession();
|
||||
List<ExtendedLuceneResourceProjection> rawResourceDataList = session.search(ResourceTable.class)
|
||||
.select(
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.search;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.search.ExtendedLuceneIndexData;
|
||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||
|
@ -47,11 +48,13 @@ import java.util.Map;
|
|||
*/
|
||||
public class ExtendedLuceneIndexExtractor {
|
||||
|
||||
private final DaoConfig myDaoConfig;
|
||||
private final FhirContext myContext;
|
||||
private final ResourceSearchParams myParams;
|
||||
private final ISearchParamExtractor mySearchParamExtractor;
|
||||
|
||||
public ExtendedLuceneIndexExtractor(FhirContext theContext, ResourceSearchParams theActiveParams, ISearchParamExtractor theSearchParamExtractor) {
|
||||
public ExtendedLuceneIndexExtractor(DaoConfig theDaoConfig, FhirContext theContext, ResourceSearchParams theActiveParams, ISearchParamExtractor theSearchParamExtractor) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
myContext = theContext;
|
||||
myParams = theActiveParams;
|
||||
mySearchParamExtractor = theSearchParamExtractor;
|
||||
|
@ -61,6 +64,10 @@ public class ExtendedLuceneIndexExtractor {
|
|||
public ExtendedLuceneIndexData extract(IBaseResource theResource, ResourceIndexedSearchParams theNewParams) {
|
||||
ExtendedLuceneIndexData retVal = new ExtendedLuceneIndexData(myContext);
|
||||
|
||||
if(myDaoConfig.isStoreResourceInLuceneIndex()) {
|
||||
retVal.setRawResourceData(myContext.newJsonParser().encodeResourceToString(theResource));
|
||||
}
|
||||
|
||||
retVal.setForcedId(theResource.getIdElement().getIdPart());
|
||||
|
||||
extractAutocompleteTokens(theResource, retVal);
|
||||
|
@ -115,7 +122,6 @@ public class ExtendedLuceneIndexExtractor {
|
|||
.filter(p->p.getParamType() == RestSearchParameterTypeEnum.TOKEN)
|
||||
// TODO it would be nice to reuse TokenExtractor
|
||||
.forEach(p-> mySearchParamExtractor.extractValues(p.getPath(), theResource)
|
||||
.stream()
|
||||
.forEach(nextValue->indexTokenValue(theRetVal, p, nextValue)
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,28 @@
|
|||
package ca.uhn.fhir.jpa.dao.search;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
/**
|
||||
|
@ -13,6 +34,7 @@ public class ExtendedLuceneResourceProjection {
|
|||
final String myResourceString;
|
||||
|
||||
public ExtendedLuceneResourceProjection(long thePid, String theForcedId, String theResourceString) {
|
||||
Validate.notEmpty(theResourceString, "Resource not stored in search index: " + thePid);
|
||||
myPid = thePid;
|
||||
myForcedId = theForcedId;
|
||||
myResourceString = theResourceString;
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.jpa.search.builder;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Iterator;
|
||||
|
||||
|
|
|
@ -342,12 +342,15 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
List<Long> rawPids = ResourcePersistentId.toLongList(fulltextMatchIds);
|
||||
|
||||
// wipmb extract
|
||||
// can we skip the database entirely and return the pid list from here?
|
||||
boolean canSkipDatabase =
|
||||
// if we processed an AND clause, and it returned nothing, then nothing can match.
|
||||
rawPids.isEmpty() ||
|
||||
// Our hibernate search query doesn't respect partitions yet
|
||||
(!myPartitionSettings.isPartitioningEnabled() &&
|
||||
// we don't support _count=0 yet.
|
||||
!theCount &&
|
||||
// were there AND terms left? Then we still need the db.
|
||||
theParams.isEmpty() &&
|
||||
// not every param is a param. :-(
|
||||
|
@ -357,8 +360,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
theParams.getOffset() == null &&
|
||||
// or sorting?
|
||||
theParams.getSort() == null
|
||||
// todo MB Ugh - review with someone else
|
||||
//theParams.toNormalizedQueryString(myContext).length() <= 1 &&
|
||||
);
|
||||
|
||||
if (canSkipDatabase) {
|
||||
|
@ -901,7 +902,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
// Can we fast track this loading by checking elastic search?
|
||||
if (isLoadingFromElasticSearchSupported(theIncludedPids)) {
|
||||
if (isLoadingFromElasticSearchSupported(thePids)) {
|
||||
theResourceListToPopulate.addAll(loadResourcesFromElasticSearch(thePids));
|
||||
} else {
|
||||
// We only chunk because some jdbc drivers can't handle long param lists.
|
||||
|
@ -914,19 +915,15 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
* We assume this is faster.
|
||||
*
|
||||
* Hibernate Search only stores the current version, and only if enabled.
|
||||
* @param theIncludedPids the _include target to check for versioned ids
|
||||
* @param thePids the pids to check for versioned references
|
||||
* @return can we fetch from Hibernate Search?
|
||||
*/
|
||||
private boolean isLoadingFromElasticSearchSupported(Collection<ResourcePersistentId> theIncludedPids) {
|
||||
// todo mb we can be smarter here.
|
||||
// todo check if theIncludedPids has any with version not null.
|
||||
private boolean isLoadingFromElasticSearchSupported(Collection<ResourcePersistentId> thePids) {
|
||||
|
||||
// is storage enabled?
|
||||
return myDaoConfig.isStoreResourceInLuceneIndex() &&
|
||||
// only support lastN for now.
|
||||
myParams.isLastN() &&
|
||||
// do we need to worry about versions?
|
||||
theIncludedPids.isEmpty() &&
|
||||
// we don't support history
|
||||
thePids.stream().noneMatch(p->p.getVersion()!=null) &&
|
||||
// skip the complexity for metadata in dstu2
|
||||
myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3);
|
||||
}
|
||||
|
@ -937,16 +934,13 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (myDaoConfig.isAdvancedLuceneIndexing() && myDaoConfig.isStoreResourceInLuceneIndex()) {
|
||||
List<Long> pidList = thePids.stream().map(ResourcePersistentId::getIdAsLong).collect(Collectors.toList());
|
||||
|
||||
// todo need to inject metadata - use profile to build resource, tags, and security labels
|
||||
//Map<Long, Collection<ResourceTag>> pidToTagMap = getPidToTagMap(pidList);
|
||||
// wipmb standardize on ResourcePersistentId
|
||||
List<IBaseResource> resources = myFulltextSearchSvc.getResources(pidList);
|
||||
return resources;
|
||||
} else if (!Objects.isNull(myParams) && myParams.isLastN()) {
|
||||
// legacy LastN implementation
|
||||
return myIElasticsearchSvc.getObservationResources(thePids);
|
||||
} else {
|
||||
// TODO I wonder if we should drop this path, and only support the new Hibernate Search path.
|
||||
Validate.isTrue(false, "Unexpected");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
@ -1391,7 +1385,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adapt simple Iterator to our internal query interface.
|
||||
* Adapt bare Iterator to our internal query interface.
|
||||
*/
|
||||
static class ResolvedSearchQueryExecutor implements ISearchQueryExecutor {
|
||||
private final Iterator<Long> myIterator;
|
||||
|
|
|
@ -43,7 +43,6 @@ import ca.uhn.fhir.validation.FhirValidator;
|
|||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
|
@ -790,14 +789,15 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Some queries can be satisfied directly from Hibernate Search.
|
||||
* We still need at least one query to fetch the resources.
|
||||
* We have a fast path that skips the database entirely
|
||||
* when we can satisfy the queries completely from Hibernate Search.
|
||||
*/
|
||||
@Nested
|
||||
public class FastPath {
|
||||
|
||||
@BeforeEach
|
||||
public void enableResourceStorage() {
|
||||
myDaoConfig.setStoreResourceInLuceneIndex(false);
|
||||
myDaoConfig.setStoreResourceInLuceneIndex(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
@ -817,7 +817,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
|
|||
|
||||
assertThat(ids, hasSize(1));
|
||||
assertThat(ids, contains(id.getIdPart()));
|
||||
assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "sql just to fetch resources");
|
||||
assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "we build the bundle with no sql");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -832,7 +832,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
|
|||
assertThat(ids, hasSize(1));
|
||||
assertThat(ids, contains(id.getIdPart()));
|
||||
|
||||
assertEquals(2, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "the pids come from elastic, but we use sql to sort, and fetch resources");
|
||||
assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "the pids come from elastic, but we use sql to sort");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -864,7 +864,7 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
|
|||
assertThat(ids, hasSize(1));
|
||||
assertThat(ids, contains(id.getIdPart()));
|
||||
|
||||
assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "just 1 to fetch the resources");
|
||||
assertEquals(0, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "no sql required");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -888,19 +888,22 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest {
|
|||
assertThat(tags.get(0).getSystem(), equalTo("http://example.com"));
|
||||
assertThat(tags.get(0).getCode(), equalTo("aTag"));
|
||||
|
||||
Meta meta = new Meta();
|
||||
meta.addTag().setSystem("tag_scheme1").setCode("tag_code1");
|
||||
meta.addProfile("http://profile/1");
|
||||
meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1");
|
||||
myObservationDao.metaAddOperation(id, meta, mySrd);
|
||||
|
||||
observations = myTestDaoSearch.searchForResources("Observation?code=theCode");
|
||||
|
||||
assertThat(observations, hasSize(1));
|
||||
IBaseMetaType newMeta = observations.get(0).getMeta();
|
||||
assertThat(newMeta.getProfile(), hasSize(1));
|
||||
assertThat(newMeta.getSecurity(), hasSize(1));
|
||||
assertThat(newMeta.getTag(), hasSize(2));
|
||||
// TODO
|
||||
// we assume tags, etc. are inline,
|
||||
// but the meta operations don't update the Hibernate Search index yet, so this fails
|
||||
// Meta meta = new Meta();
|
||||
// meta.addTag().setSystem("tag_scheme1").setCode("tag_code1");
|
||||
// meta.addProfile("http://profile/1");
|
||||
// meta.addSecurity().setSystem("seclabel_sys1").setCode("seclabel_code1");
|
||||
// myObservationDao.metaAddOperation(id, meta, mySrd);
|
||||
//
|
||||
// observations = myTestDaoSearch.searchForResources("Observation?code=theCode");
|
||||
//
|
||||
// assertThat(observations, hasSize(1));
|
||||
// IBaseMetaType newMeta = observations.get(0).getMeta();
|
||||
// assertThat(newMeta.getProfile(), hasSize(1));
|
||||
// assertThat(newMeta.getSecurity(), hasSize(1));
|
||||
// assertThat(newMeta.getTag(), hasSize(2));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -140,13 +140,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
|||
@PropertyBinding(binder = @PropertyBinderRef(type = SearchParamTextPropertyBinder.class))
|
||||
private ExtendedLuceneIndexData myLuceneIndexData;
|
||||
|
||||
// todo mb move this to ExtendedLuceneIndexData
|
||||
@Transient
|
||||
@GenericField(name="myRawResource", projectable = Projectable.YES, searchable = Searchable.NO)
|
||||
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
|
||||
@OptimisticLock(excluded = true)
|
||||
private String myRawResourceData;
|
||||
|
||||
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
|
||||
@OptimisticLock(excluded = true)
|
||||
private Collection<ResourceIndexedSearchParamCoords> myParamsCoords;
|
||||
|
@ -782,8 +775,4 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
|||
public void setLuceneIndexData(ExtendedLuceneIndexData theLuceneIndexData) {
|
||||
myLuceneIndexData = theLuceneIndexData;
|
||||
}
|
||||
|
||||
public void setRawResourceData(String theResourceData) {
|
||||
myRawResourceData = theResourceData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ public class ExtendedLuceneIndexData {
|
|||
final SetMultimap<String, IBaseCoding> mySearchParamTokens = HashMultimap.create();
|
||||
final SetMultimap<String, String> mySearchParamLinks = HashMultimap.create();
|
||||
final SetMultimap<String, DateSearchIndexData> mySearchParamDates = HashMultimap.create();
|
||||
String myForcedId;
|
||||
private String myForcedId;
|
||||
private String myResourceJSON;
|
||||
|
||||
public ExtendedLuceneIndexData(FhirContext theFhirContext) {
|
||||
this.myFhirContext = theFhirContext;
|
||||
|
@ -63,16 +64,23 @@ public class ExtendedLuceneIndexData {
|
|||
/**
|
||||
* Write the index document.
|
||||
*
|
||||
* Called by Hibernate Search after the ResourceTable entity has been flushed/committed.
|
||||
* Keep this in sync with the schema defined in {@link SearchParamTextPropertyBinder}
|
||||
* @param theDocument
|
||||
*
|
||||
* @param theDocument the Hibernate Search document for ResourceTable
|
||||
*/
|
||||
public void writeIndexElements(DocumentElement theDocument) {
|
||||
HibernateSearchIndexWriter indexWriter = HibernateSearchIndexWriter.forRoot(myFhirContext, theDocument);
|
||||
|
||||
ourLog.debug("Writing JPA index to Hibernate Search");
|
||||
|
||||
// todo can this be moved back to ResourceTable as a computed field to merge with myId?
|
||||
theDocument.addValue("myForcedId", myForcedId);
|
||||
|
||||
if (myResourceJSON != null) {
|
||||
theDocument.addValue("myRawResource", myResourceJSON);
|
||||
}
|
||||
|
||||
mySearchParamStrings.forEach(ifNotContained(indexWriter::writeStringIndex));
|
||||
mySearchParamTokens.forEach(ifNotContained(indexWriter::writeTokenIndex));
|
||||
mySearchParamLinks.forEach(ifNotContained(indexWriter::writeReferenceIndex));
|
||||
|
@ -114,4 +122,8 @@ public class ExtendedLuceneIndexData {
|
|||
public String getForcedId() {
|
||||
return myForcedId;
|
||||
}
|
||||
|
||||
public void setRawResourceData(String theResourceJSON) {
|
||||
myResourceJSON = theResourceJSON;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaObjectF
|
|||
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.Searchable;
|
||||
import org.hibernate.search.engine.backend.types.Sortable;
|
||||
import org.hibernate.search.engine.backend.types.dsl.IndexFieldTypeFactory;
|
||||
import org.hibernate.search.engine.backend.types.dsl.StandardIndexFieldTypeOptionsStep;
|
||||
|
@ -104,6 +105,12 @@ public class SearchParamTextPropertyBinder implements PropertyBinder, PropertyBr
|
|||
.projectable(Projectable.YES)
|
||||
.aggregable(Aggregable.NO);
|
||||
|
||||
// type to store payload fields that do not participate in search, only results
|
||||
StringIndexFieldTypeOptionsStep<?> stringStorageType = indexFieldTypeFactory.asString()
|
||||
.searchable(Searchable.NO)
|
||||
.projectable(Projectable.YES)
|
||||
.aggregable(Aggregable.NO);
|
||||
|
||||
// the old style for _text and _contains
|
||||
indexSchemaElement
|
||||
.fieldTemplate("SearchParamText", standardAnalyzer)
|
||||
|
@ -112,6 +119,8 @@ public class SearchParamTextPropertyBinder implements PropertyBinder, PropertyBr
|
|||
|
||||
indexSchemaElement.field("myForcedId", forcedIdType).toReference();
|
||||
|
||||
indexSchemaElement.field("myRawResource", stringStorageType).toReference();
|
||||
|
||||
// The following section is a bit ugly. We need to enforce order and dependency or the object matches will be too big.
|
||||
{
|
||||
IndexSchemaObjectField spfield = indexSchemaElement.objectField(HibernateSearchIndexWriter.SEARCH_PARAM_ROOT, ObjectStructure.FLATTENED);
|
||||
|
|
Loading…
Reference in New Issue