Skip to content

Commit a3c68f6

Browse files
committed
Refactors MultiMatchQueryBuilder and Parser
Relates to #10217 This PR is against the query-refactoring branch. Closes #13405
1 parent c5a7fed commit a3c68f6

File tree

8 files changed

+632
-168
lines changed

8 files changed

+632
-168
lines changed

core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java

Lines changed: 356 additions & 80 deletions
Large diffs are not rendered by default.

core/src/main/java/org/elasticsearch/index/query/MultiMatchQueryParser.java

Lines changed: 55 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,10 @@
1919

2020
package org.elasticsearch.index.query;
2121

22-
import org.apache.lucene.search.Query;
2322
import org.elasticsearch.common.inject.Inject;
24-
import org.elasticsearch.common.regex.Regex;
2523
import org.elasticsearch.common.unit.Fuzziness;
2624
import org.elasticsearch.common.xcontent.XContentParser;
27-
import org.elasticsearch.index.query.support.QueryParsers;
2825
import org.elasticsearch.index.search.MatchQuery;
29-
import org.elasticsearch.index.search.MultiMatchQuery;
3026

3127
import java.io.IOException;
3228
import java.util.HashMap;
@@ -35,7 +31,7 @@
3531
/**
3632
* Same as {@link MatchQueryParser} but has support for multiple fields.
3733
*/
38-
public class MultiMatchQueryParser extends BaseQueryParserTemp {
34+
public class MultiMatchQueryParser extends BaseQueryParser<MultiMatchQueryBuilder> {
3935

4036
@Override
4137
public String[] names() {
@@ -45,31 +41,41 @@ public String[] names() {
4541
}
4642

4743
@Override
48-
public Query parse(QueryShardContext context) throws IOException, QueryParsingException {
49-
QueryParseContext parseContext = context.parseContext();
44+
public MultiMatchQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
5045
XContentParser parser = parseContext.parser();
5146

5247
Object value = null;
53-
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
54-
Float tieBreaker = null;
55-
MultiMatchQueryBuilder.Type type = null;
56-
MultiMatchQuery multiMatchQuery = new MultiMatchQuery(context);
48+
Map<String, Float> fieldsBoosts = new HashMap<>();
49+
MultiMatchQueryBuilder.Type type = MultiMatchQueryBuilder.DEFAULT_TYPE;
50+
String analyzer = null;
51+
int slop = MultiMatchQueryBuilder.DEFAULT_PHRASE_SLOP;
52+
Fuzziness fuzziness = null;
53+
int prefixLength = MultiMatchQueryBuilder.DEFAULT_PREFIX_LENGTH;
54+
int maxExpansions = MultiMatchQueryBuilder.DEFAULT_MAX_EXPANSIONS;
55+
Operator operator = MultiMatchQueryBuilder.DEFAULT_OPERATOR;
5756
String minimumShouldMatch = null;
58-
Map<String, Float> fieldNameWithBoosts = new HashMap<>();
57+
String fuzzyRewrite = null;
58+
Boolean useDisMax = null;
59+
Float tieBreaker = null;
60+
Float cutoffFrequency = null;
61+
boolean lenient = MultiMatchQueryBuilder.DEFAULT_LENIENCY;
62+
MatchQuery.ZeroTermsQuery zeroTermsQuery = MultiMatchQueryBuilder.DEFAULT_ZERO_TERMS_QUERY;
63+
64+
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
5965
String queryName = null;
66+
6067
XContentParser.Token token;
6168
String currentFieldName = null;
62-
Boolean useDisMax = null;
6369
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
6470
if (token == XContentParser.Token.FIELD_NAME) {
6571
currentFieldName = parser.currentName();
6672
} else if ("fields".equals(currentFieldName)) {
6773
if (token == XContentParser.Token.START_ARRAY) {
6874
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
69-
extractFieldAndBoost(context, parser, fieldNameWithBoosts);
75+
parseFieldAndBoost(parser, fieldsBoosts);
7076
}
7177
} else if (token.isValue()) {
72-
extractFieldAndBoost(context, parser, fieldNameWithBoosts);
78+
parseFieldAndBoost(parser, fieldsBoosts);
7379
} else {
7480
throw new QueryParsingException(parseContext, "[" + MultiMatchQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]");
7581
}
@@ -79,41 +85,37 @@ public Query parse(QueryShardContext context) throws IOException, QueryParsingEx
7985
} else if ("type".equals(currentFieldName)) {
8086
type = MultiMatchQueryBuilder.Type.parse(parser.text(), parseContext.parseFieldMatcher());
8187
} else if ("analyzer".equals(currentFieldName)) {
82-
String analyzer = parser.text();
83-
if (context.analysisService().analyzer(analyzer) == null) {
84-
throw new QueryParsingException(parseContext, "[" + MultiMatchQueryBuilder.NAME + "] analyzer [" + parser.text() + "] not found");
85-
}
86-
multiMatchQuery.setAnalyzer(analyzer);
88+
analyzer = parser.text();
8789
} else if ("boost".equals(currentFieldName)) {
8890
boost = parser.floatValue();
8991
} else if ("slop".equals(currentFieldName) || "phrase_slop".equals(currentFieldName) || "phraseSlop".equals(currentFieldName)) {
90-
multiMatchQuery.setPhraseSlop(parser.intValue());
92+
slop = parser.intValue();
9193
} else if (parseContext.parseFieldMatcher().match(currentFieldName, Fuzziness.FIELD)) {
92-
multiMatchQuery.setFuzziness(Fuzziness.parse(parser));
94+
fuzziness = Fuzziness.parse(parser);
9395
} else if ("prefix_length".equals(currentFieldName) || "prefixLength".equals(currentFieldName)) {
94-
multiMatchQuery.setFuzzyPrefixLength(parser.intValue());
96+
prefixLength = parser.intValue();
9597
} else if ("max_expansions".equals(currentFieldName) || "maxExpansions".equals(currentFieldName)) {
96-
multiMatchQuery.setMaxExpansions(parser.intValue());
98+
maxExpansions = parser.intValue();
9799
} else if ("operator".equals(currentFieldName)) {
98-
multiMatchQuery.setOccur(Operator.fromString(parser.text()).toBooleanClauseOccur());
100+
operator = Operator.fromString(parser.text());
99101
} else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
100102
minimumShouldMatch = parser.textOrNull();
101103
} else if ("fuzzy_rewrite".equals(currentFieldName) || "fuzzyRewrite".equals(currentFieldName)) {
102-
multiMatchQuery.setFuzzyRewriteMethod(QueryParsers.parseRewriteMethod(parseContext.parseFieldMatcher(), parser.textOrNull(), null));
104+
fuzzyRewrite = parser.textOrNull();
103105
} else if ("use_dis_max".equals(currentFieldName) || "useDisMax".equals(currentFieldName)) {
104106
useDisMax = parser.booleanValue();
105107
} else if ("tie_breaker".equals(currentFieldName) || "tieBreaker".equals(currentFieldName)) {
106-
multiMatchQuery.setTieBreaker(tieBreaker = parser.floatValue());
108+
tieBreaker = parser.floatValue();
107109
} else if ("cutoff_frequency".equals(currentFieldName)) {
108-
multiMatchQuery.setCommonTermsCutoff(parser.floatValue());
110+
cutoffFrequency = parser.floatValue();
109111
} else if ("lenient".equals(currentFieldName)) {
110-
multiMatchQuery.setLenient(parser.booleanValue());
112+
lenient = parser.booleanValue();
111113
} else if ("zero_terms_query".equals(currentFieldName)) {
112114
String zeroTermsDocs = parser.text();
113115
if ("none".equalsIgnoreCase(zeroTermsDocs)) {
114-
multiMatchQuery.setZeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE);
116+
zeroTermsQuery = MatchQuery.ZeroTermsQuery.NONE;
115117
} else if ("all".equalsIgnoreCase(zeroTermsDocs)) {
116-
multiMatchQuery.setZeroTermsQuery(MatchQuery.ZeroTermsQuery.ALL);
118+
zeroTermsQuery = MatchQuery.ZeroTermsQuery.ALL;
117119
} else {
118120
throw new QueryParsingException(parseContext, "Unsupported zero_terms_docs value [" + zeroTermsDocs + "]");
119121
}
@@ -129,37 +131,33 @@ public Query parse(QueryShardContext context) throws IOException, QueryParsingEx
129131
throw new QueryParsingException(parseContext, "No text specified for multi_match query");
130132
}
131133

132-
if (fieldNameWithBoosts.isEmpty()) {
134+
if (fieldsBoosts.isEmpty()) {
133135
throw new QueryParsingException(parseContext, "No fields specified for multi_match query");
134136
}
135-
if (type == null) {
136-
type = MultiMatchQueryBuilder.Type.BEST_FIELDS;
137-
}
138-
if (useDisMax != null) { // backwards foobar
139-
boolean typeUsesDismax = type.tieBreaker() != 1.0f;
140-
if (typeUsesDismax != useDisMax) {
141-
if (useDisMax && tieBreaker == null) {
142-
multiMatchQuery.setTieBreaker(0.0f);
143-
} else {
144-
multiMatchQuery.setTieBreaker(1.0f);
145-
}
146-
}
147-
}
148-
Query query = multiMatchQuery.parse(type, fieldNameWithBoosts, value, minimumShouldMatch);
149-
if (query == null) {
150-
return null;
151-
}
152137

153-
query.setBoost(boost);
154-
if (queryName != null) {
155-
context.addNamedQuery(queryName, query);
156-
}
157-
return query;
138+
return new MultiMatchQueryBuilder(value)
139+
.fields(fieldsBoosts)
140+
.type(type)
141+
.analyzer(analyzer)
142+
.cutoffFrequency(cutoffFrequency)
143+
.fuzziness(fuzziness)
144+
.fuzzyRewrite(fuzzyRewrite)
145+
.useDisMax(useDisMax)
146+
.lenient(lenient)
147+
.maxExpansions(maxExpansions)
148+
.minimumShouldMatch(minimumShouldMatch)
149+
.operator(operator)
150+
.prefixLength(prefixLength)
151+
.slop(slop)
152+
.tieBreaker(tieBreaker)
153+
.zeroTermsQuery(zeroTermsQuery)
154+
.boost(boost)
155+
.queryName(queryName);
158156
}
159157

160-
private void extractFieldAndBoost(QueryShardContext context, XContentParser parser, Map<String, Float> fieldNameWithBoosts) throws IOException {
158+
private void parseFieldAndBoost(XContentParser parser, Map<String, Float> fieldsBoosts) throws IOException {
161159
String fField = null;
162-
Float fBoost = null;
160+
Float fBoost = AbstractQueryBuilder.DEFAULT_BOOST;
163161
char[] fieldText = parser.textCharacters();
164162
int end = parser.textOffset() + parser.textLength();
165163
for (int i = parser.textOffset(); i < end; i++) {
@@ -173,14 +171,7 @@ private void extractFieldAndBoost(QueryShardContext context, XContentParser pars
173171
if (fField == null) {
174172
fField = parser.text();
175173
}
176-
177-
if (Regex.isSimpleMatchPattern(fField)) {
178-
for (String field : context.mapperService().simpleMatchToIndexNames(fField)) {
179-
fieldNameWithBoosts.put(field, fBoost);
180-
}
181-
} else {
182-
fieldNameWithBoosts.put(fField, fBoost);
183-
}
174+
fieldsBoosts.put(fField, fBoost);
184175
}
185176

186177
@Override

core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
package org.elasticsearch.index.query;
2121

2222
import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
23-
2423
import org.apache.lucene.search.Query;
2524
import org.elasticsearch.Version;
26-
import org.elasticsearch.action.ActionFuture;
2725
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
2826
import org.elasticsearch.action.get.GetRequest;
2927
import org.elasticsearch.action.get.GetResponse;
@@ -79,11 +77,7 @@
7977
import org.elasticsearch.threadpool.ThreadPoolModule;
8078
import org.joda.time.DateTime;
8179
import org.joda.time.DateTimeZone;
82-
import org.junit.After;
83-
import org.junit.AfterClass;
84-
import org.junit.Before;
85-
import org.junit.BeforeClass;
86-
import org.junit.Test;
80+
import org.junit.*;
8781

8882
import java.io.IOException;
8983
import java.lang.reflect.InvocationHandler;
@@ -95,10 +89,7 @@
9589
import java.util.Map;
9690
import java.util.concurrent.ExecutionException;
9791

98-
import static org.hamcrest.Matchers.equalTo;
99-
import static org.hamcrest.Matchers.not;
100-
import static org.hamcrest.Matchers.notNullValue;
101-
import static org.hamcrest.Matchers.nullValue;
92+
import static org.hamcrest.Matchers.*;
10293

10394
public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ESTestCase {
10495

@@ -520,6 +511,15 @@ protected static Object getRandomValueForFieldName(String fieldName) {
520511
return value;
521512
}
522513

514+
protected static String getRandomQueryText() {
515+
int terms = randomIntBetween(0, 3);
516+
StringBuilder builder = new StringBuilder();
517+
for (int i = 0; i < terms; i++) {
518+
builder.append(randomAsciiOfLengthBetween(1, 10) + " ");
519+
}
520+
return builder.toString().trim();
521+
}
522+
523523
/**
524524
* Helper method to return a mapped or a random field
525525
*/

0 commit comments

Comments
 (0)