Skip to content

Commit 164d991

Browse files
authored
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.
1 parent 0af38bb commit 164d991

File tree

2 files changed

+38
-32
lines changed

2 files changed

+38
-32
lines changed

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

+8-15
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ public Query parse(Type type, String fieldName, Object value) throws IOException
241241
assert analyzer != null;
242242

243243
MatchQueryBuilder builder = new MatchQueryBuilder(analyzer, fieldType, enablePositionIncrements, autoGenerateSynonymsPhraseQuery);
244+
String resolvedFieldName = fieldType.name();
245+
String stringValue = value.toString();
244246

245247
/*
246248
* If a keyword analyzer is used, we know that further analysis isn't
@@ -249,7 +251,7 @@ public Query parse(Type type, String fieldName, Object value) throws IOException
249251
* a prefix query instead
250252
*/
251253
if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) {
252-
final Term term = new Term(fieldType.name(), value.toString());
254+
final Term term = new Term(resolvedFieldName, stringValue);
253255
if (type == Type.BOOLEAN_PREFIX
254256
&& (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) {
255257
return builder.newPrefixQuery(term);
@@ -258,32 +260,23 @@ public Query parse(Type type, String fieldName, Object value) throws IOException
258260
}
259261
}
260262

261-
return parseInternal(type, fieldType.name(), builder, value);
262-
}
263-
264-
protected final Query parseInternal(Type type, String fieldName, MatchQueryBuilder builder, Object value) throws IOException {
265-
final Query query;
263+
Query query;
266264
switch (type) {
267265
case BOOLEAN:
268-
query = builder.createBooleanQuery(fieldName, value.toString(), occur);
266+
query = builder.createBooleanQuery(resolvedFieldName, stringValue, occur);
269267
break;
270-
271268
case BOOLEAN_PREFIX:
272-
query = builder.createBooleanPrefixQuery(fieldName, value.toString(), occur);
269+
query = builder.createBooleanPrefixQuery(resolvedFieldName, stringValue, occur);
273270
break;
274-
275271
case PHRASE:
276-
query = builder.createPhraseQuery(fieldName, value.toString(), phraseSlop);
272+
query = builder.createPhraseQuery(resolvedFieldName, stringValue, phraseSlop);
277273
break;
278-
279274
case PHRASE_PREFIX:
280-
query = builder.createPhrasePrefixQuery(fieldName, value.toString(), phraseSlop);
275+
query = builder.createPhrasePrefixQuery(resolvedFieldName, stringValue, phraseSlop);
281276
break;
282-
283277
default:
284278
throw new IllegalStateException("No type found for [" + type + "]");
285279
}
286-
287280
return query == null ? zeroTermsQuery() : query;
288281
}
289282

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)