Skip to content

Commit cea85ed

Browse files
authored
Clarify what query components cross_fields supports (#69209)
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.
1 parent 97389a5 commit cea85ed

File tree

2 files changed

+37
-31
lines changed

2 files changed

+37
-31
lines changed

server/src/main/java/org/elasticsearch/index/search/MatchQueryParser.java

+7-14
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ public Query parse(Type type, String fieldName, Object value) throws IOException
254254
assert analyzer != null;
255255

256256
MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
257+
String resolvedFieldName = fieldType.name();
258+
String stringValue = value.toString();
257259

258260
/*
259261
* 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
262264
* a prefix query instead
263265
*/
264266
if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) {
265-
final Term term = new Term(fieldType.name(), value.toString());
267+
final Term term = new Term(resolvedFieldName, stringValue);
266268
if (type == Type.BOOLEAN_PREFIX
267269
&& (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) {
268270
return builder.newPrefixQuery(term);
@@ -271,11 +273,7 @@ public Query parse(Type type, String fieldName, Object value) throws IOException
271273
}
272274
}
273275

274-
return parseInternal(type, fieldType.name(), builder, value);
275-
}
276-
277-
protected final Query parseInternal(Type type, String fieldName, MatchQueryBuilder builder, Object value) throws IOException {
278-
final Query query;
276+
Query query;
279277
switch (type) {
280278
case BOOLEAN:
281279
if (commonTermsCutoff == null) {
@@ -284,23 +282,18 @@ protected final Query parseInternal(Type type, String fieldName, MatchQueryBuild
284282
query = createCommonTermsQuery(builder, fieldName, value.toString(), occur, occur, commonTermsCutoff);
285283
}
286284
break;
287-
288285
case BOOLEAN_PREFIX:
289-
query = builder.createBooleanPrefixQuery(fieldName, value.toString(), occur);
286+
query = builder.createBooleanPrefixQuery(resolvedFieldName, stringValue, occur);
290287
break;
291-
292288
case PHRASE:
293-
query = builder.createPhraseQuery(fieldName, value.toString(), phraseSlop);
289+
query = builder.createPhraseQuery(resolvedFieldName, stringValue, phraseSlop);
294290
break;
295-
296291
case PHRASE_PREFIX:
297-
query = builder.createPhrasePrefixQuery(fieldName, value.toString(), phraseSlop);
292+
query = builder.createPhrasePrefixQuery(resolvedFieldName, stringValue, phraseSlop);
298293
break;
299-
300294
default:
301295
throw new IllegalStateException("No type found for [" + type + "]");
302296
}
303-
304297
return query == null ? zeroTermsQuery() : query;
305298
}
306299

server/src/main/java/org/elasticsearch/index/search/MultiMatchQueryParser.java

+30-17
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.apache.lucene.analysis.TokenStream;
1313
import org.apache.lucene.index.Term;
1414
import org.apache.lucene.queries.BlendedTermQuery;
15+
import org.apache.lucene.search.BooleanClause;
1516
import org.apache.lucene.search.BoostQuery;
1617
import org.apache.lucene.search.DisjunctionMaxQuery;
1718
import org.apache.lucene.search.MatchNoDocsQuery;
@@ -66,7 +67,7 @@ public Query parse(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNam
6667
break;
6768

6869
case CROSS_FIELDS:
69-
queries = buildCrossFieldQuery(type, fieldNames, value, minimumShouldMatch, tieBreaker);
70+
queries = buildCrossFieldQuery(fieldNames, value, minimumShouldMatch, tieBreaker);
7071
break;
7172

7273
default:
@@ -108,15 +109,16 @@ private List<Query> buildFieldQueries(MultiMatchQueryBuilder.Type type, Map<Stri
108109
return queries;
109110
}
110111

111-
private List<Query> buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNames,
112-
Object value, String minimumShouldMatch, float tieBreaker) throws IOException {
112+
private List<Query> buildCrossFieldQuery(Map<String, Float> fieldNames,
113+
Object value, String minimumShouldMatch, float tieBreaker) {
114+
113115
Map<Analyzer, List<FieldAndBoost>> groups = new HashMap<>();
114116
List<Query> queries = new ArrayList<>();
115117
for (Map.Entry<String, Float> entry : fieldNames.entrySet()) {
116118
String name = entry.getKey();
117119
MappedFieldType fieldType = context.getFieldType(name);
118120
if (fieldType != null) {
119-
Analyzer actualAnalyzer = getAnalyzer(fieldType, type == MultiMatchQueryBuilder.Type.PHRASE);
121+
Analyzer actualAnalyzer = getAnalyzer(fieldType, false);
120122
if (groups.containsKey(actualAnalyzer) == false) {
121123
groups.put(actualAnalyzer, new ArrayList<>());
122124
}
@@ -130,7 +132,7 @@ private List<Query> buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map<S
130132
builder = new MatchQueryBuilder(group.getKey(), group.getValue().get(0).fieldType,
131133
enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
132134
} else {
133-
builder = new BlendedQueryBuilder(group.getKey(), group.getValue(), tieBreaker,
135+
builder = new CrossFieldsQueryBuilder(group.getKey(), group.getValue(), tieBreaker,
134136
enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
135137
}
136138

@@ -140,7 +142,11 @@ private List<Query> buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map<S
140142
* fields are already grouped by their analyzers/types.
141143
*/
142144
String representativeField = group.getValue().get(0).fieldType.name();
143-
Query query = parseInternal(type.matchQueryType(), representativeField, builder, value);
145+
Query query = builder.createBooleanQuery(representativeField, value.toString(), occur);
146+
if (query == null) {
147+
query = zeroTermsQuery();
148+
}
149+
144150
query = Queries.maybeApplyMinimumShouldMatch(query, minimumShouldMatch);
145151
if (query != null) {
146152
if (group.getValue().size() == 1) {
@@ -157,17 +163,32 @@ private List<Query> buildCrossFieldQuery(MultiMatchQueryBuilder.Type type, Map<S
157163
return queries;
158164
}
159165

160-
private class BlendedQueryBuilder extends MatchQueryBuilder {
166+
private class CrossFieldsQueryBuilder extends MatchQueryBuilder {
161167
private final List<FieldAndBoost> blendedFields;
162168
private final float tieBreaker;
163169

164-
BlendedQueryBuilder(Analyzer analyzer, List<FieldAndBoost> blendedFields, float tieBreaker,
170+
CrossFieldsQueryBuilder(Analyzer analyzer, List<FieldAndBoost> blendedFields, float tieBreaker,
165171
boolean enablePositionIncrements, boolean autoGenerateSynonymsPhraseQuery) {
166172
super(analyzer, blendedFields.get(0).fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
167173
this.blendedFields = blendedFields;
168174
this.tieBreaker = tieBreaker;
169175
}
170176

177+
@Override
178+
public Query createPhraseQuery(String field, String queryText, int phraseSlop) {
179+
throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support phrases");
180+
}
181+
182+
@Override
183+
protected Query createPhrasePrefixQuery(String field, String queryText, int slop) {
184+
throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support phrase prefix");
185+
}
186+
187+
@Override
188+
protected Query createBooleanPrefixQuery(String field, String queryText, BooleanClause.Occur occur) {
189+
throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support boolean prefix");
190+
}
191+
171192
@Override
172193
protected Query newSynonymQuery(TermAndBoost[] terms) {
173194
BytesRef[] values = new BytesRef[terms.length];
@@ -184,15 +205,7 @@ protected Query newTermQuery(Term term, float boost) {
184205

185206
@Override
186207
protected Query newPrefixQuery(Term term) {
187-
List<Query> disjunctions = new ArrayList<>();
188-
for (FieldAndBoost fieldType : blendedFields) {
189-
Query query = fieldType.fieldType.prefixQuery(term.text(), null, context);
190-
if (fieldType.boost != 1f) {
191-
query = new BoostQuery(query, fieldType.boost);
192-
}
193-
disjunctions.add(query);
194-
}
195-
return new DisjunctionMaxQuery(disjunctions, tieBreaker);
208+
throw new IllegalArgumentException("[multi_match] queries in [cross_fields] mode don't support prefix");
196209
}
197210

198211
@Override

0 commit comments

Comments
 (0)