Browse Source

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.
Julie Tibshirani 4 years ago
parent
commit
164d991189

+ 8 - 15
server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java

@@ -241,6 +241,8 @@ public class MatchQueryParser {
         assert analyzer != null;
         assert analyzer != null;
 
 
         MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
         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
          * If a keyword analyzer is used, we know that further analysis isn't
@@ -249,7 +251,7 @@ public class MatchQueryParser {
          * a prefix query instead
          * a prefix query instead
          */
          */
         if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) {
         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
             if (type == Type.BOOLEAN_PREFIX
                     && (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) {
                     && (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) {
                 return builder.newPrefixQuery(term);
                 return builder.newPrefixQuery(term);
@@ -258,32 +260,23 @@ public class MatchQueryParser {
             }
             }
         }
         }
 
 
-        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) {
         switch (type) {
             case BOOLEAN:
             case BOOLEAN:
-                query = builder.createBooleanQuery(fieldName, value.toString(), occur);
+                query = builder.createBooleanQuery(resolvedFieldName, stringValue, occur);
                 break;
                 break;
-
             case BOOLEAN_PREFIX:
             case BOOLEAN_PREFIX:
-                query = builder.createBooleanPrefixQuery(fieldName, value.toString(), occur);
+                query = builder.createBooleanPrefixQuery(resolvedFieldName, stringValue, occur);
                 break;
                 break;
-
             case PHRASE:
             case PHRASE:
-                query = builder.createPhraseQuery(fieldName, value.toString(), phraseSlop);
+                query = builder.createPhraseQuery(resolvedFieldName, stringValue, phraseSlop);
                 break;
                 break;
-
             case PHRASE_PREFIX:
             case PHRASE_PREFIX:
-                query = builder.createPhrasePrefixQuery(fieldName, value.toString(), phraseSlop);
+                query = builder.createPhrasePrefixQuery(resolvedFieldName, stringValue, phraseSlop);
                 break;
                 break;
-
             default:
             default:
                 throw new IllegalStateException("No type found for [" + type + "]");
                 throw new IllegalStateException("No type found for [" + type + "]");
         }
         }
-
         return query == null ? zeroTermsQuery() : query;
         return query == null ? zeroTermsQuery() : query;
     }
     }
 
 

+ 30 - 17
server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java

@@ -12,6 +12,7 @@ import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queries.BlendedTermQuery;
 import org.apache.lucene.queries.BlendedTermQuery;
+import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.BoostQuery;
 import org.apache.lucene.search.DisjunctionMaxQuery;
 import org.apache.lucene.search.DisjunctionMaxQuery;
 import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.MatchNoDocsQuery;
@@ -66,7 +67,7 @@ public class MultiMatchQueryParser extends MatchQueryParser {
                 break;
                 break;
 
 
             case CROSS_FIELDS:
             case CROSS_FIELDS:
-                queries = buildCrossFieldQuery(type, fieldNames, value, minimumShouldMatch, tieBreaker);
+                queries = buildCrossFieldQuery(fieldNames, value, minimumShouldMatch, tieBreaker);
                 break;
                 break;
 
 
             default:
             default:
@@ -108,15 +109,16 @@ public class MultiMatchQueryParser extends MatchQueryParser {
         return queries;
         return queries;
     }
     }
 
 
-    private List<Query> buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNames,
-                                            Object value, String minimumShouldMatch, float tieBreaker) throws IOException {
+    private List<Query> buildCrossFieldQuery(Map<String, Float> fieldNames,
+                                             Object value, String minimumShouldMatch, float tieBreaker) {
+
         Map<Analyzer, List<FieldAndBoost>> groups = new HashMap<>();
         Map<Analyzer, List<FieldAndBoost>> groups = new HashMap<>();
         List<Query> queries = new ArrayList<>();
         List<Query> queries = new ArrayList<>();
         for (Map.Entry<String, Float> entry : fieldNames.entrySet()) {
         for (Map.Entry<String, Float> entry : fieldNames.entrySet()) {
             String name = entry.getKey();
             String name = entry.getKey();
             MappedFieldType fieldType = context.getFieldType(name);
             MappedFieldType fieldType = context.getFieldType(name);
             if (fieldType != null) {
             if (fieldType != null) {
-                Analyzer actualAnalyzer = getAnalyzer(fieldType, type == MultiMatchQueryBuilder.Type.PHRASE);
+                Analyzer actualAnalyzer = getAnalyzer(fieldType, false);
                 if (groups.containsKey(actualAnalyzer) == false) {
                 if (groups.containsKey(actualAnalyzer) == false) {
                     groups.put(actualAnalyzer, new ArrayList<>());
                     groups.put(actualAnalyzer, new ArrayList<>());
                 }
                 }
@@ -130,7 +132,7 @@ public class MultiMatchQueryParser extends MatchQueryParser {
                 builder = new MatchQueryBuilder(group.getKey(), group.getValue().get(0).fieldType,
                 builder = new MatchQueryBuilder(group.getKey(), group.getValue().get(0).fieldType,
                     enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
                     enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
             } else {
             } else {
-                builder = new BlendedQueryBuilder(group.getKey(), group.getValue(), tieBreaker,
+                builder = new CrossFieldsQueryBuilder(group.getKey(), group.getValue(), tieBreaker,
                     enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
                     enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
             }
             }
 
 
@@ -140,7 +142,11 @@ public class MultiMatchQueryParser extends MatchQueryParser {
              * fields are already grouped by their analyzers/types.
              * fields are already grouped by their analyzers/types.
              */
              */
             String representativeField = group.getValue().get(0).fieldType.name();
             String representativeField = group.getValue().get(0).fieldType.name();
-            Query query = parseInternal(type.matchQueryType(), representativeField, builder, value);
+            Query query = builder.createBooleanQuery(representativeField, value.toString(), occur);
+            if (query == null) {
+                query = zeroTermsQuery();
+            }
+
             query = Queries.maybeApplyMinimumShouldMatch(query, minimumShouldMatch);
             query = Queries.maybeApplyMinimumShouldMatch(query, minimumShouldMatch);
             if (query != null) {
             if (query != null) {
                 if (group.getValue().size() == 1) {
                 if (group.getValue().size() == 1) {
@@ -157,17 +163,32 @@ public class MultiMatchQueryParser extends MatchQueryParser {
         return queries;
         return queries;
     }
     }
 
 
-    private class BlendedQueryBuilder extends MatchQueryBuilder {
+    private class CrossFieldsQueryBuilder extends MatchQueryBuilder {
         private final List<FieldAndBoost> blendedFields;
         private final List<FieldAndBoost> blendedFields;
         private final float tieBreaker;
         private final float tieBreaker;
 
 
-        BlendedQueryBuilder(Analyzer analyzer, List<FieldAndBoost> blendedFields, float tieBreaker,
+        CrossFieldsQueryBuilder(Analyzer analyzer, List<FieldAndBoost> blendedFields, float tieBreaker,
                                 boolean enablePositionIncrements, boolean autoGenerateSynonymsPhraseQuery) {
                                 boolean enablePositionIncrements, boolean autoGenerateSynonymsPhraseQuery) {
             super(analyzer, blendedFields.get(0).fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
             super(analyzer, blendedFields.get(0).fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
             this.blendedFields = blendedFields;
             this.blendedFields = blendedFields;
             this.tieBreaker = tieBreaker;
             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
         @Override
         protected Query newSynonymQuery(TermAndBoost[] terms) {
         protected Query newSynonymQuery(TermAndBoost[] terms) {
             BytesRef[] values = new BytesRef[terms.length];
             BytesRef[] values = new BytesRef[terms.length];
@@ -184,15 +205,7 @@ public class MultiMatchQueryParser extends MatchQueryParser {
 
 
         @Override
         @Override
         protected Query newPrefixQuery(Term term) {
         protected Query newPrefixQuery(Term term) {
-            List<Query> 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
         @Override