diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java index b53885f2262d9..e76116ef7376e 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/AbstractObjectParser.java @@ -240,6 +240,24 @@ public void declareObjectArray(BiConsumer> consumer, ContextP declareFieldArray(consumer, (p, c) -> objectParser.parse(p, c), field, ValueType.OBJECT_ARRAY); } + /** + * like {@link #declareObjectArray(BiConsumer, ContextParser, ParseField)}, but can also handle single null values, + * in which case the consumer isn't called + */ + public < + T> void declareObjectArrayOrNull( + BiConsumer> consumer, + ContextParser objectParser, + ParseField field + ) { + declareField( + (value, list) -> { if (list != null) consumer.accept(value, list); }, + (p, c) -> p.currentToken() == XContentParser.Token.VALUE_NULL ? null : parseArray(p, () -> objectParser.parse(p, c)), + field, + ValueType.OBJECT_ARRAY_OR_NULL + ); + } + public void declareStringArray(BiConsumer> consumer, ParseField field) { declareFieldArray(consumer, (p, c) -> p.text(), field, ValueType.STRING_ARRAY); } diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 1ed6627e51575..26b77551909c0 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -641,6 +641,7 @@ public enum ValueType { OBJECT(START_OBJECT), OBJECT_OR_NULL(START_OBJECT, VALUE_NULL), OBJECT_ARRAY(START_OBJECT, START_ARRAY), + OBJECT_ARRAY_OR_NULL(START_OBJECT, START_ARRAY, VALUE_NULL), OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN), OBJECT_OR_STRING(START_OBJECT, VALUE_STRING), OBJECT_OR_LONG(START_OBJECT, VALUE_NUMBER), diff --git a/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java index d932df81f3398..2563328326e3c 100644 --- a/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -273,16 +273,16 @@ private static void doXArrayContent(ParseField field, List clauses private static final ObjectParser PARSER = new ObjectParser<>("bool", BoolQueryBuilder::new); static { - PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::must), (p, c) -> parseInnerQueryBuilder(p), + PARSER.declareObjectArrayOrNull((builder, clauses) -> clauses.forEach(builder::must), (p, c) -> parseInnerQueryBuilder(p), MUST); - PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::should), (p, c) -> parseInnerQueryBuilder(p), + PARSER.declareObjectArrayOrNull((builder, clauses) -> clauses.forEach(builder::should), (p, c) -> parseInnerQueryBuilder(p), SHOULD); - PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::mustNot), (p, c) -> parseInnerQueryBuilder(p), + PARSER.declareObjectArrayOrNull((builder, clauses) -> clauses.forEach(builder::mustNot), (p, c) -> parseInnerQueryBuilder(p), MUST_NOT); - PARSER.declareObjectArray((builder, clauses) -> clauses.forEach(builder::filter), (p, c) -> parseInnerQueryBuilder(p), + PARSER.declareObjectArrayOrNull((builder, clauses) -> clauses.forEach(builder::filter), (p, c) -> parseInnerQueryBuilder(p), FILTER); PARSER.declareBoolean(BoolQueryBuilder::adjustPureNegative, ADJUST_PURE_NEGATIVE); - PARSER.declareField(BoolQueryBuilder::minimumShouldMatch, (p, c) -> p.text(), + PARSER.declareField(BoolQueryBuilder::minimumShouldMatch, (p, c) -> p.textOrNull(), MINIMUM_SHOULD_MATCH, ObjectParser.ValueType.VALUE); PARSER.declareString(BoolQueryBuilder::queryName, NAME_FIELD); PARSER.declareFloat(BoolQueryBuilder::boost, BOOST_FIELD); diff --git a/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java index 6ee301510b58f..efe252bb36442 100644 --- a/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java @@ -284,6 +284,36 @@ public void testMinimumShouldMatchNumber() throws IOException { assertEquals("1", builder.minimumShouldMatch()); } + public void testMinimumShouldMatchNull() throws IOException { + String query = "{\"bool\" : {\"must\" : { \"term\" : { \"field\" : \"value\" } }, \"minimum_should_match\" : null } }"; + BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query); + assertEquals(null, builder.minimumShouldMatch()); + } + + public void testMustNull() throws IOException { + String query = "{\"bool\" : {\"must\" : null } }"; + BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query); + assertTrue(builder.must().isEmpty()); + } + + public void testMustNotNull() throws IOException { + String query = "{\"bool\" : {\"must_not\" : null } }"; + BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query); + assertTrue(builder.mustNot().isEmpty()); + } + + public void testShouldNull() throws IOException { + String query = "{\"bool\" : {\"should\" : null } }"; + BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query); + assertTrue(builder.should().isEmpty()); + } + + public void testFilterNull() throws IOException { + String query = "{\"bool\" : {\"filter\" : null } }"; + BoolQueryBuilder builder = (BoolQueryBuilder) parseQuery(query); + assertTrue(builder.filter().isEmpty()); + } + /** * test that unknown query names in the clauses throw an error */