Skip to content

Commit 0edb1d3

Browse files
authored
BoolQueryBuilder uses ObjectParser (#52880)
This commit removes the hand-rolled x-content parsing logic from BoolQueryBuilder and instead uses an ObjectParser to handle parsing. It also removes the long-deprecated (since version 6) disable_coord parameter.
1 parent 2930b32 commit 0edb1d3

File tree

2 files changed

+41
-99
lines changed

2 files changed

+41
-99
lines changed

server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java

Lines changed: 28 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.common.io.stream.StreamInput;
3030
import org.elasticsearch.common.io.stream.StreamOutput;
3131
import org.elasticsearch.common.lucene.search.Queries;
32+
import org.elasticsearch.common.xcontent.ObjectParser;
3233
import org.elasticsearch.common.xcontent.XContentBuilder;
3334
import org.elasticsearch.common.xcontent.XContentParser;
3435

@@ -51,13 +52,11 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
5152

5253
public static final boolean ADJUST_PURE_NEGATIVE_DEFAULT = true;
5354

54-
private static final String MUSTNOT = "mustNot";
55-
private static final String MUST_NOT = "must_not";
56-
private static final String FILTER = "filter";
57-
private static final String SHOULD = "should";
58-
private static final String MUST = "must";
59-
private static final ParseField DISABLE_COORD_FIELD = new ParseField("disable_coord")
60-
.withAllDeprecated("disable_coord has been removed");
55+
private static final ParseField MUSTNOT = new ParseField("mustNot"); // TODO deprecate?
56+
private static final ParseField MUST_NOT = new ParseField("must_not");
57+
private static final ParseField FILTER = new ParseField("filter");
58+
private static final ParseField SHOULD = new ParseField("should");
59+
private static final ParseField MUST = new ParseField("must");
6160
private static final ParseField MINIMUM_SHOULD_MATCH = new ParseField("minimum_should_match");
6261
private static final ParseField ADJUST_PURE_NEGATIVE = new ParseField("adjust_pure_negative");
6362

@@ -260,106 +259,39 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep
260259
builder.endObject();
261260
}
262261

263-
private static void doXArrayContent(String field, List<QueryBuilder> clauses, XContentBuilder builder, Params params)
262+
private static void doXArrayContent(ParseField field, List<QueryBuilder> clauses, XContentBuilder builder, Params params)
264263
throws IOException {
265264
if (clauses.isEmpty()) {
266265
return;
267266
}
268-
builder.startArray(field);
267+
builder.startArray(field.getPreferredName());
269268
for (QueryBuilder clause : clauses) {
270269
clause.toXContent(builder, params);
271270
}
272271
builder.endArray();
273272
}
274273

274+
private static final ObjectParser<BoolQueryBuilder, Void> PARSER = new ObjectParser<>("bool", BoolQueryBuilder::new);
275+
static {
276+
PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::must), (p, c) -> parseInnerQueryBuilder(p),
277+
MUST);
278+
PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::should), (p, c) -> parseInnerQueryBuilder(p),
279+
SHOULD);
280+
PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::mustNot), (p, c) -> parseInnerQueryBuilder(p),
281+
MUST_NOT);
282+
PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::mustNot), (p, c) -> parseInnerQueryBuilder(p),
283+
MUSTNOT); // TODO should we deprecate this version?
284+
PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::filter), (p, c) -> parseInnerQueryBuilder(p),
285+
FILTER);
286+
PARSER.declareBoolean(BoolQueryBuilder::adjustPureNegative, ADJUST_PURE_NEGATIVE);
287+
PARSER.declareField(BoolQueryBuilder::minimumShouldMatch, (p, c) -> p.text(),
288+
MINIMUM_SHOULD_MATCH, ObjectParser.ValueType.VALUE);
289+
PARSER.declareString(BoolQueryBuilder::queryName, NAME_FIELD);
290+
PARSER.declareFloat(BoolQueryBuilder::boost, BOOST_FIELD);
291+
}
292+
275293
public static BoolQueryBuilder fromXContent(XContentParser parser) throws IOException, ParsingException {
276-
boolean adjustPureNegative = BoolQueryBuilder.ADJUST_PURE_NEGATIVE_DEFAULT;
277-
float boost = DEFAULT_BOOST;
278-
String minimumShouldMatch = null;
279-
280-
final List<QueryBuilder> mustClauses = new ArrayList<>();
281-
final List<QueryBuilder> mustNotClauses = new ArrayList<>();
282-
final List<QueryBuilder> shouldClauses = new ArrayList<>();
283-
final List<QueryBuilder> filterClauses = new ArrayList<>();
284-
String queryName = null;
285-
286-
String currentFieldName = null;
287-
XContentParser.Token token;
288-
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
289-
if (token == XContentParser.Token.FIELD_NAME) {
290-
currentFieldName = parser.currentName();
291-
} else if (token == XContentParser.Token.START_OBJECT) {
292-
switch (currentFieldName) {
293-
case MUST:
294-
mustClauses.add(parseInnerQueryBuilder(parser));
295-
break;
296-
case SHOULD:
297-
shouldClauses.add(parseInnerQueryBuilder(parser));
298-
break;
299-
case FILTER:
300-
filterClauses.add(parseInnerQueryBuilder(parser));
301-
break;
302-
case MUST_NOT:
303-
case MUSTNOT:
304-
mustNotClauses.add(parseInnerQueryBuilder(parser));
305-
break;
306-
default:
307-
throw new ParsingException(parser.getTokenLocation(), "[bool] query does not support [" + currentFieldName + "]");
308-
}
309-
} else if (token == XContentParser.Token.START_ARRAY) {
310-
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
311-
switch (currentFieldName) {
312-
case MUST:
313-
mustClauses.add(parseInnerQueryBuilder(parser));
314-
break;
315-
case SHOULD:
316-
shouldClauses.add(parseInnerQueryBuilder(parser));
317-
break;
318-
case FILTER:
319-
filterClauses.add(parseInnerQueryBuilder(parser));
320-
break;
321-
case MUST_NOT:
322-
case MUSTNOT:
323-
mustNotClauses.add(parseInnerQueryBuilder(parser));
324-
break;
325-
default:
326-
throw new ParsingException(parser.getTokenLocation(), "bool query does not support [" + currentFieldName + "]");
327-
}
328-
}
329-
} else if (token.isValue()) {
330-
if (DISABLE_COORD_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
331-
// ignore
332-
} else if (MINIMUM_SHOULD_MATCH.match(currentFieldName, parser.getDeprecationHandler())) {
333-
minimumShouldMatch = parser.textOrNull();
334-
} else if (BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
335-
boost = parser.floatValue();
336-
} else if (ADJUST_PURE_NEGATIVE.match(currentFieldName, parser.getDeprecationHandler())) {
337-
adjustPureNegative = parser.booleanValue();
338-
} else if (NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
339-
queryName = parser.text();
340-
} else {
341-
throw new ParsingException(parser.getTokenLocation(), "[bool] query does not support [" + currentFieldName + "]");
342-
}
343-
}
344-
}
345-
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
346-
for (QueryBuilder queryBuilder : mustClauses) {
347-
boolQuery.must(queryBuilder);
348-
}
349-
for (QueryBuilder queryBuilder : mustNotClauses) {
350-
boolQuery.mustNot(queryBuilder);
351-
}
352-
for (QueryBuilder queryBuilder : shouldClauses) {
353-
boolQuery.should(queryBuilder);
354-
}
355-
for (QueryBuilder queryBuilder : filterClauses) {
356-
boolQuery.filter(queryBuilder);
357-
}
358-
boolQuery.boost(boost);
359-
boolQuery.adjustPureNegative(adjustPureNegative);
360-
boolQuery.minimumShouldMatch(minimumShouldMatch);
361-
boolQuery.queryName(queryName);
362-
return boolQuery;
294+
return PARSER.parse(parser, null);
363295
}
364296

365297
@Override

server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import org.apache.lucene.search.MatchAllDocsQuery;
2525
import org.apache.lucene.search.MatchNoDocsQuery;
2626
import org.apache.lucene.search.Query;
27-
import org.elasticsearch.common.ParsingException;
2827
import org.elasticsearch.common.xcontent.XContentBuilder;
2928
import org.elasticsearch.common.xcontent.XContentFactory;
29+
import org.elasticsearch.common.xcontent.XContentParseException;
3030
import org.elasticsearch.common.xcontent.XContentParser;
3131
import org.elasticsearch.common.xcontent.XContentType;
3232
import org.elasticsearch.test.AbstractQueryTestCase;
@@ -41,6 +41,7 @@
4141

4242
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
4343
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
44+
import static org.hamcrest.CoreMatchers.containsString;
4445
import static org.hamcrest.CoreMatchers.equalTo;
4546
import static org.hamcrest.CoreMatchers.instanceOf;
4647

@@ -277,14 +278,23 @@ public void testFromJson() throws IOException {
277278
assertEquals(query, "kimchy", ((TermQueryBuilder)queryBuilder.must().get(0)).value());
278279
}
279280

281+
public void testMinimumShouldMatchNumber() throws IOException {
282+
String query = "{\"bool\" : {\"must\" : { \"term\" : { \"field\" : \"value\" } }, \"minimum_should_match\" : 1 } }";
283+
BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query);
284+
assertEquals("1", builder.minimumShouldMatch());
285+
}
286+
280287
/**
281288
* test that unknown query names in the clauses throw an error
282289
*/
283290
public void testUnknownQueryName() throws IOException {
284291
String query = "{\"bool\" : {\"must\" : { \"unknown_query\" : { } } } }";
285292

286-
ParsingException ex = expectThrows(ParsingException.class, () -> parseQuery(query));
287-
assertEquals("unknown query [unknown_query]", ex.getMessage());
293+
XContentParseException ex = expectThrows(XContentParseException.class, () -> parseQuery(query));
294+
assertEquals("[1:41] [bool] failed to parse field [must]", ex.getMessage());
295+
Throwable e = ex.getCause();
296+
assertThat(e.getMessage(), containsString("unknown query [unknown_query]"));
297+
288298
}
289299

290300
public void testRewrite() throws IOException {

0 commit comments

Comments
 (0)