Skip to content

Commit

Permalink
HSEARCH-3312 Avoid skip analysis on normalized field for ES
Browse files Browse the repository at this point in the history
  • Loading branch information
fax4ever authored and yrodiere committed Apr 3, 2019
1 parent 3cee291 commit b79a1d4
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -486,4 +486,7 @@ SearchException conflictingIdentifierTypesForPredicate(ToDocumentIdentifierValue
+ " This version would require dialect '%2$s', but Hibernate Search was configured to use dialect '%3$s'.")
SearchException unexpectedElasticsearchVersion(ElasticsearchVersion actualVersion,
ElasticsearchDialectName appropriateDialectName, ElasticsearchDialectName configuredDialectName);

@Message(id = ID_OFFSET_3 + 60, value = "Elasticsearch backend does not support skip analysis on not analyzed field: '%1$s'.")
SearchException skipAnalysisOnKeywordField(String absoluteFieldPath, @Param EventContext context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public IndexFieldType<String> toIndexFieldType() {

return new ElasticsearchIndexFieldType<>(
codec,
new ElasticsearchTextFieldPredicateBuilderFactory( dslToIndexConverter, createToDocumentRawConverter(), codec ),
new ElasticsearchTextFieldPredicateBuilderFactory( dslToIndexConverter, createToDocumentRawConverter(), codec, mapping ),
new ElasticsearchStandardFieldSortBuilderFactory<>( resolvedSortable, dslToIndexConverter, createToDocumentRawConverter(), codec ),
new ElasticsearchStandardFieldProjectionBuilderFactory<>( resolvedProjectable, indexToProjectionConverter, createFromDocumentRawConverter(), codec ),
mapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class ElasticsearchStandardMatchPredicateBuilder<F> extends AbstractElasticsearc

private static final JsonObjectAccessor MATCH_ACCESSOR = JsonAccessor.root().property( "match" ).asObject();

private final ElasticsearchSearchContext searchContext;
protected final String absoluteFieldPath;

private final String absoluteFieldPath;
private final ElasticsearchSearchContext searchContext;

private final ToDocumentFieldValueConverter<?, ? extends F> converter;
private final ToDocumentFieldValueConverter<F, ? extends F> rawConverter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
package org.hibernate.search.backend.elasticsearch.types.predicate.impl;

import org.hibernate.search.backend.elasticsearch.document.model.impl.esnative.DataType;
import org.hibernate.search.backend.elasticsearch.document.model.impl.esnative.PropertyMapping;
import org.hibernate.search.backend.elasticsearch.search.impl.ElasticsearchConverterCompatibilityChecker;
import org.hibernate.search.backend.elasticsearch.search.impl.ElasticsearchSearchContext;
import org.hibernate.search.backend.elasticsearch.search.predicate.impl.ElasticsearchSearchPredicateBuilder;
Expand All @@ -18,17 +20,20 @@
public class ElasticsearchTextFieldPredicateBuilderFactory
extends ElasticsearchStandardFieldPredicateBuilderFactory<String> {

private final DataType type;

public ElasticsearchTextFieldPredicateBuilderFactory(
ToDocumentFieldValueConverter<?, ? extends String> converter,
ToDocumentFieldValueConverter<String, ? extends String> rawConverter,
ElasticsearchFieldCodec<String> codec) {
ElasticsearchFieldCodec<String> codec, PropertyMapping mapping) {
super( converter, rawConverter, codec );
this.type = mapping.getType();
}

@Override
public MatchPredicateBuilder<ElasticsearchSearchPredicateBuilder> createMatchPredicateBuilder(
ElasticsearchSearchContext searchContext, String absoluteFieldPath, ElasticsearchConverterCompatibilityChecker converterChecker) {
return new ElasticsearchTextMatchPredicateBuilder( searchContext, absoluteFieldPath, converter, rawConverter, converterChecker, codec );
return new ElasticsearchTextMatchPredicateBuilder( searchContext, absoluteFieldPath, converter, rawConverter, converterChecker, codec, type );
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,32 @@
*/
package org.hibernate.search.backend.elasticsearch.types.predicate.impl;

import java.lang.invoke.MethodHandles;

import org.hibernate.search.backend.elasticsearch.document.model.impl.esnative.DataType;
import org.hibernate.search.backend.elasticsearch.gson.impl.JsonAccessor;
import org.hibernate.search.backend.elasticsearch.logging.impl.Log;
import org.hibernate.search.backend.elasticsearch.search.impl.ElasticsearchConverterCompatibilityChecker;
import org.hibernate.search.backend.elasticsearch.search.impl.ElasticsearchSearchContext;
import org.hibernate.search.backend.elasticsearch.search.predicate.impl.ElasticsearchSearchPredicateContext;
import org.hibernate.search.backend.elasticsearch.types.codec.impl.ElasticsearchFieldCodec;
import org.hibernate.search.backend.elasticsearch.util.impl.AnalyzerUtils;
import org.hibernate.search.engine.backend.types.converter.ToDocumentFieldValueConverter;
import org.hibernate.search.engine.reporting.spi.EventContexts;
import org.hibernate.search.util.common.logging.impl.LoggerFactory;

import com.google.gson.JsonObject;

class ElasticsearchTextMatchPredicateBuilder extends ElasticsearchStandardMatchPredicateBuilder<String> {

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );

private static final JsonAccessor<Integer> FUZZINESS_ACCESSOR = JsonAccessor.root().property( "fuzziness" ).asInteger();
private static final JsonAccessor<Integer> PREFIX_LENGTH_ACCESSOR = JsonAccessor.root().property( "prefix_length" ).asInteger();
private static final JsonAccessor<String> ANALYZER_ACCESSOR = JsonAccessor.root().property( "analyzer" ).asString();

private final DataType type;

private Integer fuzziness;
private Integer prefixLength;
private String analyzer;
Expand All @@ -30,8 +40,10 @@ class ElasticsearchTextMatchPredicateBuilder extends ElasticsearchStandardMatchP
ElasticsearchSearchContext searchContext,
String absoluteFieldPath,
ToDocumentFieldValueConverter<?, ? extends String> converter, ToDocumentFieldValueConverter<String, ? extends String> rawConverter,
ElasticsearchConverterCompatibilityChecker converterChecker, ElasticsearchFieldCodec<String> codec) {
ElasticsearchConverterCompatibilityChecker converterChecker, ElasticsearchFieldCodec<String> codec,
DataType type) {
super( searchContext, absoluteFieldPath, converter, rawConverter, converterChecker, codec );
this.type = type;
}

@Override
Expand All @@ -47,6 +59,10 @@ public void analyzer(String analyzerName) {

@Override
public void skipAnalysis() {
if ( DataType.KEYWORD.equals( type ) ) {
throw log.skipAnalysisOnKeywordField( absoluteFieldPath, EventContexts.fromIndexFieldAbsolutePath( absoluteFieldPath ) );
}

analyzer( AnalyzerUtils.KEYWORD_ANALYZER );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.integrationtest.backend.elasticsearch.tmp;

import static org.hibernate.search.util.impl.integrationtest.common.stub.mapper.StubMapperUtils.referenceProvider;

import org.hibernate.search.engine.backend.document.DocumentElement;
import org.hibernate.search.engine.backend.document.IndexFieldReference;
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement;
import org.hibernate.search.engine.backend.index.spi.IndexWorkPlan;
import org.hibernate.search.integrationtest.backend.tck.testsupport.configuration.DefaultAnalysisDefinitions;
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.rule.SearchSetupHelper;
import org.hibernate.search.util.common.SearchException;
import org.hibernate.search.util.impl.integrationtest.common.stub.mapper.StubMappingIndexManager;
import org.hibernate.search.util.impl.test.SubTest;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class ElasticsearchMatchSearchPredicateIT {

private static final String INDEX_NAME = "IndexName";
private static final String TEST_TERM = "ThisWillBeLowercasedByTheNormalizer";

@Rule
public SearchSetupHelper setupHelper = new SearchSetupHelper();

@Rule
public ExpectedException thrown = ExpectedException.none();

private IndexMapping indexMapping;
private StubMappingIndexManager indexManager;

@Before
public void setup() {
setupHelper.withDefaultConfiguration()
.withIndex(
INDEX_NAME,
ctx -> this.indexMapping = new IndexMapping( ctx.getSchemaElement() ),
indexManager -> this.indexManager = indexManager
)
.setup();

initData();
}

@Test
public void match_skipAnalysis_normalizedStringField() {
SubTest.expectException( () -> indexManager.createSearchScope().query()
.asReference()
.predicate( f -> f.match().onField( "normalizedStringField" ).matching( TEST_TERM ).skipAnalysis() )
.toQuery()
)
.assertThrown()
.isInstanceOf( SearchException.class )
.hasMessageContaining( "HSEARCH400560" )
.hasMessageContaining( "Elasticsearch backend does not support skip analysis on not analyzed field" )
.hasMessageContaining( "normalizedStringField" );
}

private void initData() {
IndexWorkPlan<? extends DocumentElement> workPlan = indexManager.createWorkPlan();
workPlan.add( referenceProvider( "1" ), document -> document.addValue( indexMapping.normalizedStringField, TEST_TERM ) );
workPlan.execute().join();
}

private static class IndexMapping {
final IndexFieldReference<String> normalizedStringField;

IndexMapping(IndexSchemaElement root) {
normalizedStringField = root.field(
"normalizedStringField",
c -> c.asString().normalizer( DefaultAnalysisDefinitions.NORMALIZER_LOWERCASE.name )
)
.toReference();
}
}
}

0 comments on commit b79a1d4

Please sign in to comment.