From 1f9b12136324e32196f3ae15eff9366e2764904c Mon Sep 17 00:00:00 2001 From: Julie Tibshirani Date: Thu, 18 Feb 2021 10:20:42 -0800 Subject: [PATCH] Clarify what query components cross_fields supports (#68795) The parsing logic for the `cross_fields` mode was very general, making it hard to tell what query components it actually supports. This PR clarifies that only 'bag of words' queries are supported, it does not accept phrase or prefix queries. It also renames `BlendedQueryBuilder` -> `CrossFieldsQueryBuilder` for clarity. --- .../index/search/MatchQueryParser.java | 21 +++------ .../index/search/MultiMatchQueryParser.java | 47 ++++++++++++------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java b/server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java index 9c8f5ad7590a9..0183d9792b7b1 100644 --- a/server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java +++ b/server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java @@ -254,6 +254,8 @@ public Query parse(Type type, String fieldName, Object value) throws IOException assert analyzer != null; MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery); + String resolvedFieldName = fieldType.name(); + String stringValue = value.toString(); /* * If a keyword analyzer is used, we know that further analysis isn't @@ -262,7 +264,7 @@ public Query parse(Type type, String fieldName, Object value) throws IOException * a prefix query instead */ if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) { - final Term term = new Term(fieldType.name(), value.toString()); + final Term term = new Term(resolvedFieldName, stringValue); if (type == Type.BOOLEAN_PREFIX && (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) { return builder.newPrefixQuery(term); @@ -271,11 +273,7 @@ public Query parse(Type type, String fieldName, Object value) throws IOException } } - return parseInternal(type, fieldType.name(), builder, value); - } - - protected final Query parseInternal(Type type, String fieldName, MatchQueryBuilder builder, Object value) throws IOException { - final Query query; + Query query; switch (type) { case BOOLEAN: if (commonTermsCutoff == null) { @@ -284,23 +282,18 @@ protected final Query parseInternal(Type type, String fieldName, MatchQueryBuild query = createCommonTermsQuery(builder, fieldName, value.toString(), occur, occur, commonTermsCutoff); } break; - case BOOLEAN_PREFIX: - query = builder.createBooleanPrefixQuery(fieldName, value.toString(), occur); + query = builder.createBooleanPrefixQuery(resolvedFieldName, stringValue, occur); break; - case PHRASE: - query = builder.createPhraseQuery(fieldName, value.toString(), phraseSlop); + query = builder.createPhraseQuery(resolvedFieldName, stringValue, phraseSlop); break; - case PHRASE_PREFIX: - query = builder.createPhrasePrefixQuery(fieldName, value.toString(), phraseSlop); + query = builder.createPhrasePrefixQuery(resolvedFieldName, stringValue, phraseSlop); break; - default: throw new IllegalStateException("No type found for [" + type + "]"); } - return query == null ? zeroTermsQuery() : query; } diff --git a/server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java b/server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java index bd908e86cfb1b..b5bf1f1ca02e2 100644 --- a/server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java +++ b/server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java @@ -12,6 +12,7 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.index.Term; import org.apache.lucene.queries.BlendedTermQuery; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.MatchNoDocsQuery; @@ -66,7 +67,7 @@ public Query parse(MultiMatchQueryBuilder.Type type, Map fieldNam break; case CROSS_FIELDS: - queries = buildCrossFieldQuery(type, fieldNames, value, minimumShouldMatch, tieBreaker); + queries = buildCrossFieldQuery(fieldNames, value, minimumShouldMatch, tieBreaker); break; default: @@ -108,15 +109,16 @@ private List buildFieldQueries(MultiMatchQueryBuilder.Type type, Map buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map fieldNames, - Object value, String minimumShouldMatch, float tieBreaker) throws IOException { + private List buildCrossFieldQuery(Map fieldNames, + Object value, String minimumShouldMatch, float tieBreaker) { + Map> groups = new HashMap<>(); List queries = new ArrayList<>(); for (Map.Entry entry : fieldNames.entrySet()) { String name = entry.getKey(); MappedFieldType fieldType = context.getFieldType(name); if (fieldType != null) { - Analyzer actualAnalyzer = getAnalyzer(fieldType, type == MultiMatchQueryBuilder.Type.PHRASE); + Analyzer actualAnalyzer = getAnalyzer(fieldType, false); if (groups.containsKey(actualAnalyzer) == false) { groups.put(actualAnalyzer, new ArrayList<>()); } @@ -130,7 +132,7 @@ private List buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map blendedFields; private final float tieBreaker; - BlendedQueryBuilder(Analyzer analyzer, List blendedFields, float tieBreaker, + CrossFieldsQueryBuilder(Analyzer analyzer, List blendedFields, float tieBreaker, boolean enablePositionIncrements, boolean autoGenerateSynonymsPhraseQuery) { super(analyzer, blendedFields.get(0).fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery); this.blendedFields = blendedFields; this.tieBreaker = tieBreaker; } + @Override + public Query createPhraseQuery(String field, String queryText, int phraseSlop) { + throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support phrases"); + } + + @Override + protected Query createPhrasePrefixQuery(String field, String queryText, int slop) { + throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support phrase prefix"); + } + + @Override + protected Query createBooleanPrefixQuery(String field, String queryText, BooleanClause.Occur occur) { + throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support boolean prefix"); + } + @Override protected Query newSynonymQuery(TermAndBoost[] terms) { BytesRef[] values = new BytesRef[terms.length]; @@ -184,15 +205,7 @@ protected Query newTermQuery(Term term, float boost) { @Override protected Query newPrefixQuery(Term term) { - List disjunctions = new ArrayList<>(); - for (FieldAndBoost fieldType : blendedFields) { - Query query = fieldType.fieldType.prefixQuery(term.text(), null, context); - if (fieldType.boost != 1f) { - query = new BoostQuery(query, fieldType.boost); - } - disjunctions.add(query); - } - return new DisjunctionMaxQuery(disjunctions, tieBreaker); + throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support prefix"); } @Override