Query Refactoring: Make sure that parsing nested queries always returns an query builder

Currently parsing inner queries can return `null` which leads to unnecessary complicated
null checks further up the query hierarchy. By introducing a special EmptyQueryBuilder
that can be used to signal that this query clause should be ignored upstream where possible,
we can avoid additional null checks in parent query builders and still allow for this query
to be ignored when creating the lucene query later.

This new builder returns `null` when calling its `toQuery()` method, so at this point
we still need to handle it, but having an explicit object for the intermediate query
representation makes it easier to differentiate between cases where inner query clauses are
empty (legal) or are not set at all (illegal).

Also added check for empty inner builder list to BoolQueryBuilder and OrQueryBuilder.
Removed setters for positive/negatice query in favour of constructor with
mandatory non-null arguments in BoostingQueryBuilder.

Closes #11915
This commit is contained in:
Christoph Büscher 2015-06-26 17:18:40 +02:00
parent 63530631f9
commit c51cfbb6d0
34 changed files with 338 additions and 289 deletions

View File

@ -233,22 +233,6 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder> exte
return queries;
}
/**
* Utility method that converts inner query builders to xContent and
* checks for null values, rendering out empty object in this case.
*/
protected static void doXContentInnerBuilder(XContentBuilder xContentBuilder, String fieldName,
QueryBuilder queryBuilder, Params params) throws IOException {
xContentBuilder.field(fieldName);
if (queryBuilder != null) {
queryBuilder.toXContent(xContentBuilder, params);
} else {
// we output an empty object, QueryParseContext#parseInnerQueryBuilder will parse this back to `null` value
xContentBuilder.startObject();
xContentBuilder.endObject();
}
}
protected static QueryValidationException validateInnerQueries(List<QueryBuilder> queryBuilders, QueryValidationException initialValidationException) {
QueryValidationException validationException = initialValidationException;
for (QueryBuilder queryBuilder : queryBuilders) {

View File

@ -98,6 +98,10 @@ public class AndQueryBuilder extends AbstractQueryBuilder<AndQueryBuilder> {
query.add(innerQuery, Occur.MUST);
}
}
if (query.clauses().isEmpty()) {
// no inner lucene query exists, ignore upstream
return null;
}
return query;
}

View File

@ -54,9 +54,7 @@ public class AndQueryParser extends BaseQueryParser {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
queriesFound = true;
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
queries.add(filter);
}
} else {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -69,17 +67,13 @@ public class AndQueryParser extends BaseQueryParser {
queriesFound = true;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
queries.add(filter);
}
} else {
queriesFound = true;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
queries.add(filter);
}
}
} else if (token.isValue()) {

View File

@ -255,16 +255,16 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
if (!hasClauses()) {
return new MatchAllDocsQuery();
}
BooleanQuery booleanQuery = new BooleanQuery(disableCoord);
addBooleanClauses(parseContext, booleanQuery, mustClauses, BooleanClause.Occur.MUST);
addBooleanClauses(parseContext, booleanQuery, mustNotClauses, BooleanClause.Occur.MUST_NOT);
addBooleanClauses(parseContext, booleanQuery, shouldClauses, BooleanClause.Occur.SHOULD);
addBooleanClauses(parseContext, booleanQuery, filterClauses, BooleanClause.Occur.FILTER);
if (booleanQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery();
}
Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch);
return adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
}

View File

@ -71,14 +71,13 @@ public class BoolQueryParser extends BaseQueryParser {
switch (currentFieldName) {
case "must":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
mustClauses.add(query);
}
mustClauses.add(query);
break;
case "should":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
shouldClauses.add(query);
shouldClauses.add(query);
// EmptyQueryBuilder does not add lucene query later, skip setting minuminShouldMatch
if (query != EmptyQueryBuilder.PROTOTYPE) {
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
@ -86,16 +85,12 @@ public class BoolQueryParser extends BaseQueryParser {
break;
case "filter":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
filterClauses.add(query);
}
filterClauses.add(query);
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
mustNotClauses.add(query);
}
mustNotClauses.add(query);
break;
default:
throw new QueryParsingException(parseContext, "[bool] query does not support [" + currentFieldName + "]");
@ -105,14 +100,13 @@ public class BoolQueryParser extends BaseQueryParser {
switch (currentFieldName) {
case "must":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
mustClauses.add(query);
}
mustClauses.add(query);
break;
case "should":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
shouldClauses.add(query);
shouldClauses.add(query);
// EmptyQueryBuilder does not add lucene query later, skip setting minuminShouldMatch
if (query != EmptyQueryBuilder.PROTOTYPE) {
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
@ -120,16 +114,12 @@ public class BoolQueryParser extends BaseQueryParser {
break;
case "filter":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
filterClauses.add(query);
}
filterClauses.add(query);
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
mustNotClauses.add(query);
}
mustNotClauses.add(query);
break;
default:
throw new QueryParsingException(parseContext, "bool query does not support [" + currentFieldName + "]");

View File

@ -44,23 +44,31 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
public static final String NAME = "boosting";
private QueryBuilder positiveQuery;
private final QueryBuilder positiveQuery;
private QueryBuilder negativeQuery;
private final QueryBuilder negativeQuery;
private float negativeBoost = -1;
static final BoostingQueryBuilder PROTOTYPE = new BoostingQueryBuilder();
public BoostingQueryBuilder() {
/**
* this constructor only used for prototype
*/
private BoostingQueryBuilder() {
this.positiveQuery = null;
this.negativeQuery = null;
}
/**
* Add the positive query for this boosting query.
* Create a new {@link BoostingQueryBuilder}
*
* @param positiveQuery the positive query for this boosting query.
* @param negativeQuery the negative query for this boosting query.
*/
public BoostingQueryBuilder positive(QueryBuilder positiveQuery) {
this.positiveQuery = positiveQuery;
return this;
public BoostingQueryBuilder(QueryBuilder positiveQuery, QueryBuilder negativeQuery) {
this.positiveQuery = Objects.requireNonNull(positiveQuery);
this.negativeQuery = Objects.requireNonNull(negativeQuery);
}
/**
@ -70,14 +78,6 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
return this.positiveQuery;
}
/**
* Add the negative query for this boosting query.
*/
public BoostingQueryBuilder negative(QueryBuilder negativeQuery) {
this.negativeQuery = negativeQuery;
return this;
}
/**
* Get the negative query for this boosting query.
*/
@ -103,8 +103,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "positive", positiveQuery, params);
doXContentInnerBuilder(builder, "negative", negativeQuery, params);
builder.field("positive");
positiveQuery.toXContent(builder, params);
builder.field("negative");
negativeQuery.toXContent(builder, params);
builder.field("negative_boost", negativeBoost);
printBoostAndQueryName(builder);
builder.endObject();
@ -128,13 +130,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// make upstream queries ignore this query by returning `null`
// if either inner query builder is null or returns null-Query
if (positiveQuery == null || negativeQuery == null) {
return null;
}
Query positive = positiveQuery.toQuery(parseContext);
Query negative = negativeQuery.toQuery(parseContext);
// make upstream queries ignore this query by returning `null`
// if either inner query builder returns null
if (positive == null || negative == null) {
return null;
}
@ -158,9 +157,7 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
protected BoostingQueryBuilder doReadFrom(StreamInput in) throws IOException {
QueryBuilder positiveQuery = in.readNamedWriteable();
QueryBuilder negativeQuery = in.readNamedWriteable();
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
boostingQuery.positive(positiveQuery);
boostingQuery.negative(negativeQuery);
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positiveQuery, negativeQuery);
boostingQuery.negativeBoost = in.readFloat();
return boostingQuery;
}

View File

@ -88,9 +88,7 @@ public class BoostingQueryParser extends BaseQueryParser {
throw new QueryParsingException(parseContext, "[boosting] query requires 'negative_boost' to be set to be a positive value'");
}
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
boostingQuery.positive(positiveQuery);
boostingQuery.negative(negativeQuery);
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positiveQuery, negativeQuery);
boostingQuery.negativeBoost(negativeBoost);
boostingQuery.boost(boost);
boostingQuery.queryName(queryName);

View File

@ -38,7 +38,12 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
private final QueryBuilder filterBuilder;
static final ConstantScoreQueryBuilder PROTOTYPE = new ConstantScoreQueryBuilder(null);
static final ConstantScoreQueryBuilder PROTOTYPE = new ConstantScoreQueryBuilder();
// only used for prototype
private ConstantScoreQueryBuilder() {
this.filterBuilder = null;
}
/**
* A query that wraps another query and simply returns a constant score equal to the
@ -47,7 +52,7 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
* @param filterBuilder The query to wrap in a constant score query
*/
public ConstantScoreQueryBuilder(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
this.filterBuilder = Objects.requireNonNull(filterBuilder);
}
/**
@ -60,24 +65,19 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "filter", filterBuilder, params);
builder.field("filter");
filterBuilder.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// current DSL allows empty inner filter clauses, we ignore them
if (filterBuilder == null) {
return null;
}
Query innerFilter = filterBuilder.toQuery(parseContext);
if (innerFilter == null ) {
// return null so that parent queries (e.g. bool) also ignore this
return null;
}
return new ConstantScoreQuery(filterBuilder.toQuery(parseContext));
}

View File

@ -62,9 +62,7 @@ public class DisMaxQueryParser extends BaseQueryParser {
if ("queries".equals(currentFieldName)) {
queriesFound = true;
QueryBuilder query = parseContext.parseInnerQueryBuilder();
if (query != null) {
queries.add(query);
}
queries.add(query);
} else {
throw new QueryParsingException(parseContext, "[dis_max] query does not support [" + currentFieldName + "]");
}
@ -73,9 +71,7 @@ public class DisMaxQueryParser extends BaseQueryParser {
queriesFound = true;
while (token != XContentParser.Token.END_ARRAY) {
QueryBuilder query = parseContext.parseInnerQueryBuilder();
if (query != null) {
queries.add(query);
}
queries.add(query);
token = parser.nextToken();
}
} else {

View File

@ -0,0 +1,83 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
/**
* A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL.
* The current DSL allows parsing inner queries / filters like "{ }", in order to have a
* valid non-null representation of these clauses that actually do nothing we can use this class.
*
* This builder has no corresponding parser and it is not registered under the query name. It is
* intended to be used internally as a stand-in for nested queries that are left empty and should
* be ignored upstream.
*/
public class EmptyQueryBuilder extends AbstractQueryBuilder<EmptyQueryBuilder> {
public static final String NAME = "empty_query";
/** the one and only empty query builder */
public static final EmptyQueryBuilder PROTOTYPE = new EmptyQueryBuilder();
private EmptyQueryBuilder() {
// prevent other constructors
}
@Override
public String getName() {
return NAME;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
// empty
}
/**
* @returns <tt>null</tt> to signal to caller that this query should be ignored
* in the context of the DSL.
*/
@Override
public Query doToQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
return null;
}
@Override
public QueryValidationException validate() {
// nothing to validate
return null;
}
@Override
public EmptyQueryBuilder doReadFrom(StreamInput in) throws IOException {
return EmptyQueryBuilder.PROTOTYPE;
}
@Override
public void doWriteTo(StreamOutput out) throws IOException {
// nothing to serialize
}
}

View File

@ -39,7 +39,7 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
public static final String NAME = "fquery";
static final FQueryFilterBuilder PROTOTYPE = new FQueryFilterBuilder(null);
static final FQueryFilterBuilder PROTOTYPE = new FQueryFilterBuilder();
private final QueryBuilder queryBuilder;
@ -49,7 +49,11 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
* @param queryBuilder The query to wrap as a filter
*/
public FQueryFilterBuilder(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.queryBuilder = Objects.requireNonNull(queryBuilder);
}
private FQueryFilterBuilder() {
this.queryBuilder = null;
}
/**
@ -62,7 +66,8 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(FQueryFilterBuilder.NAME);
doXContentInnerBuilder(builder, "query", queryBuilder, params);
builder.field("query");
queryBuilder.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@ -70,9 +75,6 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// inner query builder can potentially be `null`, in that case we ignore it
if (this.queryBuilder == null) {
return null;
}
Query innerQuery = this.queryBuilder.toQuery(parseContext);
if (innerQuery == null) {
return null;

View File

@ -76,9 +76,6 @@ public class FQueryFilterParser extends BaseQueryParser {
if (!queryFound) {
throw new QueryParsingException(parseContext, "[fquery] requires 'query' element");
}
if (wrappedQuery == null) {
return null;
}
FQueryFilterBuilder queryBuilder = new FQueryFilterBuilder(wrappedQuery);
queryBuilder.queryName(queryName);
queryBuilder.boost(boost);

View File

@ -40,6 +40,7 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
static final FieldMaskingSpanQueryBuilder PROTOTYPE = new FieldMaskingSpanQueryBuilder();
// only used for prototype
private FieldMaskingSpanQueryBuilder() {
this.queryBuilder = null;
this.fieldName = null;
@ -53,7 +54,7 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
*/
public FieldMaskingSpanQueryBuilder(SpanQueryBuilder queryBuilder, String fieldName) {
this.queryBuilder = Objects.requireNonNull(queryBuilder);
this.fieldName = fieldName;
this.fieldName = Objects.requireNonNull(fieldName);
}
/**
@ -73,7 +74,8 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "query", queryBuilder, params);
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("field", fieldName);
printBoostAndQueryName(builder);
builder.endObject();

View File

@ -37,10 +37,14 @@ public class NotQueryBuilder extends AbstractQueryBuilder<NotQueryBuilder> {
private final QueryBuilder filter;
static final NotQueryBuilder PROTOTYPE = new NotQueryBuilder(null);
static final NotQueryBuilder PROTOTYPE = new NotQueryBuilder();
public NotQueryBuilder(QueryBuilder filter) {
this.filter = filter;
this.filter = Objects.requireNonNull(filter);
}
private NotQueryBuilder() {
this.filter = null;
}
/**
@ -53,16 +57,14 @@ public class NotQueryBuilder extends AbstractQueryBuilder<NotQueryBuilder> {
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "query", filter, params);
builder.field("query");
filter.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
if (filter == null) {
return null;
}
Query luceneQuery = filter.toQuery(parseContext);
if (luceneQuery == null) {
return null;

View File

@ -95,6 +95,10 @@ public class OrQueryBuilder extends AbstractQueryBuilder<OrQueryBuilder> {
query.add(innerQuery, Occur.SHOULD);
}
}
if (query.clauses().isEmpty()) {
// no inner lucene query exists, ignore upstream
return null;
}
return query;
}

View File

@ -277,8 +277,8 @@ public abstract class QueryBuilders {
* Unlike the "NOT" clause, this still selects documents that contain undesirable terms,
* but reduces their overall score:
*/
public static BoostingQueryBuilder boostingQuery() {
return new BoostingQueryBuilder();
public static BoostingQueryBuilder boostingQuery(QueryBuilder positiveQuery, QueryBuilder negativeQuery) {
return new BoostingQueryBuilder(positiveQuery, negativeQuery);
}
/**

View File

@ -40,7 +40,7 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
private final QueryBuilder queryBuilder;
static final QueryFilterBuilder PROTOTYPE = new QueryFilterBuilder(null);
static final QueryFilterBuilder PROTOTYPE = new QueryFilterBuilder();
/**
* A filter that simply wraps a query.
@ -48,7 +48,11 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
* @param queryBuilder The query to wrap as a filter
*/
public QueryFilterBuilder(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.queryBuilder = Objects.requireNonNull(queryBuilder);
}
private QueryFilterBuilder() {
this.queryBuilder = null;
}
/**
@ -72,15 +76,13 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
doXContentInnerBuilder(builder, NAME, queryBuilder, params);
builder.field(NAME);
queryBuilder.toXContent(builder, params);
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// inner query builder can potentially be `null`, in that case we ignore it
if (this.queryBuilder == null) {
return null;
}
Query innerQuery = this.queryBuilder.toQuery(parseContext);
if (innerQuery == null) {
return null;

View File

@ -232,7 +232,7 @@ public class QueryParseContext {
token = parser.nextToken();
if (token == XContentParser.Token.END_OBJECT) {
// empty query
return null;
return EmptyQueryBuilder.PROTOTYPE;
}
if (token != XContentParser.Token.FIELD_NAME) {
throw new QueryParsingException(this, "[_na] query malformed, no field after start_object");

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import java.util.Map;
@ -45,6 +46,9 @@ public class IndicesQueriesRegistry extends AbstractComponent {
}
namedWriteableRegistry.registerPrototype(queryParser.getBuilderPrototype());
}
// EmptyQueryBuilder is not registered as query parser but used internally.
// We need to register it with the NamedWriteableRegistry in order to serialize it
namedWriteableRegistry.registerPrototype(EmptyQueryBuilder.PROTOTYPE);
this.queryParsers = ImmutableMap.copyOf(queryParsers);
}

View File

@ -43,6 +43,9 @@ public class AndQueryBuilderTest extends BaseQueryTestCase<AndQueryBuilder> {
query.add(innerQuery, Occur.MUST);
}
}
if (query.clauses().isEmpty()) {
return null;
}
return query;
}
@ -78,6 +81,16 @@ public class AndQueryBuilderTest extends BaseQueryTestCase<AndQueryBuilder> {
context.indexQueryParserService().queryParser(AndQueryBuilder.PROTOTYPE.getName()).fromXContent(context);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new AndQueryBuilder(EmptyQueryBuilder.PROTOTYPE, null);
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new AndQueryBuilder(EmptyQueryBuilder.PROTOTYPE).add(null);
}
@Test
public void testValidate() {
AndQueryBuilder andQuery = new AndQueryBuilder();

View File

@ -234,14 +234,22 @@ public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> ext
Query expectedQuery = createExpectedQuery(testQuery, context);
Query actualQuery = testQuery.toQuery(context);
assertThat(actualQuery, instanceOf(expectedQuery.getClass()));
assertThat(actualQuery, equalTo(expectedQuery));
assertLuceneQuery(testQuery, actualQuery, context);
// expectedQuery can be null, e.g. in case of BoostingQueryBuilder
// with inner clause that returns null itself
if (expectedQuery == null) {
assertNull("Expected a null query, saw some object.", actualQuery);
} else {
assertThat(actualQuery, instanceOf(expectedQuery.getClass()));
assertThat(actualQuery, equalTo(expectedQuery));
assertLuceneQuery(testQuery, actualQuery, context);
}
}
protected final Query createExpectedQuery(QB queryBuilder, QueryParseContext context) throws IOException {
Query expectedQuery = doCreateExpectedQuery(queryBuilder, context);
expectedQuery.setBoost(queryBuilder.boost());
if (expectedQuery != null) {
expectedQuery.setBoost(queryBuilder.boost());
}
return expectedQuery;
}

View File

@ -72,12 +72,14 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
}
BooleanQuery boolQuery = new BooleanQuery(queryBuilder.disableCoord());
boolQuery.setBoost(queryBuilder.boost());
addBooleanClauses(context, boolQuery, queryBuilder.must(), BooleanClause.Occur.MUST);
addBooleanClauses(context, boolQuery, queryBuilder.mustNot(), BooleanClause.Occur.MUST_NOT);
addBooleanClauses(context, boolQuery, queryBuilder.should(), BooleanClause.Occur.SHOULD);
addBooleanClauses(context, boolQuery, queryBuilder.filter(), BooleanClause.Occur.FILTER);
if (boolQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery();
}
Queries.applyMinimumShouldMatch(boolQuery, queryBuilder.minimumNumberShouldMatch());
return queryBuilder.adjustPureNegative() ? fixNegativeQueryIfNeeded(boolQuery) : boolQuery;
}
@ -85,7 +87,10 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
private static void addBooleanClauses(QueryParseContext parseContext, BooleanQuery booleanQuery, List<QueryBuilder> clauses, Occur occurs)
throws IOException {
for (QueryBuilder query : clauses) {
booleanQuery.add(new BooleanClause(query.toQuery(parseContext), occurs));
Query innerQuery = query.toQuery(parseContext);
if (innerQuery != null) {
booleanQuery.add(new BooleanClause(innerQuery, occurs));
}
}
}
@ -129,4 +134,24 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
}
assertValidate(booleanQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testAddNullMust() {
new BoolQueryBuilder().must(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullMustNot() {
new BoolQueryBuilder().mustNot(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullShould() {
new BoolQueryBuilder().should(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullFilter() {
new BoolQueryBuilder().filter(null);
}
}

View File

@ -21,9 +21,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.queries.BoostingQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
@ -32,9 +29,7 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
@Override
protected BoostingQueryBuilder doCreateTestQueryBuilder() {
BoostingQueryBuilder query = new BoostingQueryBuilder();
query.positive(RandomQueryBuilder.createQuery(random()));
query.negative(RandomQueryBuilder.createQuery(random()));
BoostingQueryBuilder query = new BoostingQueryBuilder(RandomQueryBuilder.createQuery(random()), RandomQueryBuilder.createQuery(random()));
query.negativeBoost(2.0f / randomIntBetween(1, 20));
return query;
}
@ -43,89 +38,30 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
protected Query doCreateExpectedQuery(BoostingQueryBuilder queryBuilder, QueryParseContext context) throws IOException {
Query positive = queryBuilder.positive().toQuery(context);
Query negative = queryBuilder.negative().toQuery(context);
if (positive == null || negative == null) {
return null;
}
return new BoostingQuery(positive, negative, queryBuilder.negativeBoost());
}
/**
* test that setting a null negative/positive clause renders a parseable query
*/
@Test
public void testInnerClauseNull() throws IOException {
BoostingQueryBuilder boostingQueryBuilder = new BoostingQueryBuilder().negativeBoost(0.1f);
if (randomBoolean()) {
boostingQueryBuilder.positive(new MatchAllQueryBuilder());
} else {
boostingQueryBuilder.negative(new MatchAllQueryBuilder());
}
String contentString = boostingQueryBuilder.toString();
XContentParser parser = XContentFactory.xContent(contentString).createParser(contentString);
QueryParseContext context = createContext();
context.reset(parser);
assertQueryHeader(parser, boostingQueryBuilder.getName());
QueryBuilder parsedBuilder = context.indexQueryParserService().queryParser(boostingQueryBuilder.getName()).fromXContent(context);
assertNotNull(parsedBuilder);
assertNotSame(parsedBuilder, boostingQueryBuilder);
assertEquals(parsedBuilder, boostingQueryBuilder);
}
/**
* tests that we signal upstream queries to ignore this query by returning <tt>null</tt>
* if any of the inner query builder is not set
*/
@Test
public void testInnerQueryBuilderNull() throws IOException {
BoostingQueryBuilder boostingQueryBuilder = new BoostingQueryBuilder();
if (randomBoolean()) {
boostingQueryBuilder.positive(new MatchAllQueryBuilder()).negative(null);
} else {
boostingQueryBuilder.positive(null).negative(new MatchAllQueryBuilder());
}
assertNull(boostingQueryBuilder.toQuery(createContext()));
}
@Test
public void testInnerQueryBuilderReturnsNull() throws IOException {
QueryBuilder noOpBuilder = new AbstractQueryBuilder() {
@Override
public String getName() {
return "dummy";
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
return null;
}
};
BoostingQueryBuilder boostingQueryBuilder;
if (randomBoolean()) {
boostingQueryBuilder = new BoostingQueryBuilder().positive(new MatchAllQueryBuilder()).negative(noOpBuilder);
} else {
boostingQueryBuilder = new BoostingQueryBuilder().positive(noOpBuilder).negative(new MatchAllQueryBuilder());
}
assertNull(boostingQueryBuilder.toQuery(createContext()));
}
@Test
public void testValidate() {
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
int totalExpectedErrors = 0;
if (randomBoolean()) {
boostingQuery.negative(RandomQueryBuilder.createInvalidQuery(random()));
QueryBuilder positive;
QueryBuilder negative;
if (frequently()) {
negative = RandomQueryBuilder.createInvalidQuery(random());
totalExpectedErrors++;
} else if(rarely()) {
boostingQuery.negative(RandomQueryBuilder.createQuery(random()));
} else {
negative = RandomQueryBuilder.createQuery(random());
}
if (randomBoolean()) {
boostingQuery.positive(RandomQueryBuilder.createInvalidQuery(random()));
if (frequently()) {
positive = RandomQueryBuilder.createInvalidQuery(random());
totalExpectedErrors++;
} else if(rarely()) {
boostingQuery.positive(RandomQueryBuilder.createQuery(random()));
} else {
positive = RandomQueryBuilder.createQuery(random());
}
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positive, negative);
if (frequently()) {
boostingQuery.negativeBoost(0.5f);
} else {
@ -134,4 +70,13 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
}
assertValidate(boostingQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructorArgument() {
if (randomBoolean()) {
new BoostingQueryBuilder(null, RandomQueryBuilder.createQuery(random()));
} else {
new BoostingQueryBuilder(RandomQueryBuilder.createQuery(random()), null);
}
}
}

View File

@ -31,7 +31,11 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
@Override
protected Query doCreateExpectedQuery(ConstantScoreQueryBuilder testBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(testBuilder.query().toQuery(context));
Query innerQuery = testBuilder.query().toQuery(context);
if (innerQuery != null) {
return new ConstantScoreQuery(innerQuery);
}
return null;
}
/**
@ -46,7 +50,7 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
* test that missing "filter" element causes {@link QueryParsingException}
*/
@Test(expected=QueryParsingException.class)
public void testNoFilterElement() throws IOException {
public void testFilterElement() throws IOException {
QueryParseContext context = createContext();
String queryId = ConstantScoreQueryBuilder.PROTOTYPE.getName();
String queryString = "{ \""+queryId+"\" : {}";
@ -56,24 +60,6 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
context.indexQueryParserService().queryParser(queryId).fromXContent(context);
}
/**
* Test empty "filter" element.
* Current DSL allows inner filter element to be empty, returning a `null` inner filter builder
*/
@Test
public void testEmptyFilterElement() throws IOException {
QueryParseContext context = createContext();
String queryId = ConstantScoreQueryBuilder.PROTOTYPE.getName();
String queryString = "{ \""+queryId+"\" : { \"filter\" : { } }";
XContentParser parser = XContentFactory.xContent(queryString).createParser(queryString);
context.reset(parser);
assertQueryHeader(parser, queryId);
ConstantScoreQueryBuilder queryBuilder = (ConstantScoreQueryBuilder) context.indexQueryParserService()
.queryParser(queryId).fromXContent(context);
assertNull(queryBuilder.query());
assertNull(queryBuilder.toQuery(createContext()));
}
@Test
public void testValidate() {
QueryBuilder innerQuery;
@ -87,4 +73,9 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
ConstantScoreQueryBuilder constantScoreQuery = new ConstantScoreQueryBuilder(innerQuery);
assertValidate(constantScoreQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new ConstantScoreQueryBuilder(null);
}
}

View File

@ -26,12 +26,17 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
import java.util.Collection;
public class DisMaxQueryBuilderTest extends BaseQueryTestCase<DisMaxQueryBuilder> {
@Override
protected Query doCreateExpectedQuery(DisMaxQueryBuilder testBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new DisjunctionMaxQuery(AbstractQueryBuilder.toQueries(testBuilder.queries(), context), testBuilder.tieBreaker());
Collection<Query> queries = AbstractQueryBuilder.toQueries(testBuilder.queries(), context);
if (queries.isEmpty()) {
return null;
}
return new DisjunctionMaxQuery(queries, testBuilder.tieBreaker());
}
/**
@ -82,6 +87,11 @@ public class DisMaxQueryBuilderTest extends BaseQueryTestCase<DisMaxQueryBuilder
assertNull(disMaxBuilder.toQuery(context));
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new DisMaxQueryBuilder().add(null);
}
@Test
public void testValidate() {
DisMaxQueryBuilder disMaxQuery = new DisMaxQueryBuilder();

View File

@ -32,11 +32,15 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
@Override
protected Query doCreateExpectedQuery(FQueryFilterBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(queryBuilder.innerQuery().toQuery(context));
Query query = queryBuilder.innerQuery().toQuery(context);
if (query != null) {
return new ConstantScoreQuery(query);
}
return null;
}
/**
* @return a AndQueryBuilder with random limit between 0 and 20
* @return a FQueryFilterBuilder with random inner query
*/
@Override
protected FQueryFilterBuilder doCreateTestQueryBuilder() {
@ -49,7 +53,7 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
*/
@Test
public void testNoInnerQuery() throws QueryParsingException, IOException {
FQueryFilterBuilder queryFilterQuery = new FQueryFilterBuilder(null);
FQueryFilterBuilder queryFilterQuery = new FQueryFilterBuilder(EmptyQueryBuilder.PROTOTYPE);
assertNull(queryFilterQuery.toQuery(createContext()));
}
@ -85,4 +89,9 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
FQueryFilterBuilder fQueryFilter = new FQueryFilterBuilder(innerQuery);
assertValidate(fQueryFilter, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructo() {
new FQueryFilterBuilder(null);
}
}

View File

@ -61,7 +61,7 @@ public class FieldMaskingSpanQueryBuilderTest extends BaseQueryTestCase<FieldMas
if (randomBoolean()) {
fieldName = "fieldName";
} else {
fieldName = randomBoolean() ? "" : null;
fieldName = "";
totalExpectedErrors++;
}
if (randomBoolean()) {
@ -73,4 +73,14 @@ public class FieldMaskingSpanQueryBuilderTest extends BaseQueryTestCase<FieldMas
FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder(spanQueryBuilder, fieldName);
assertValidate(queryBuilder, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullFieldName() {
new FieldMaskingSpanQueryBuilder(new SpanTermQueryBuilder("name", "value"), null);
}
@Test(expected=NullPointerException.class)
public void testNullInnerQuery() {
new FieldMaskingSpanQueryBuilder(null, "");
}
}

View File

@ -31,7 +31,7 @@ public class NotQueryBuilderTest extends BaseQueryTestCase<NotQueryBuilder> {
@Override
protected Query doCreateExpectedQuery(NotQueryBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
if (queryBuilder.filter() == null) {
if (queryBuilder.filter().toQuery(context) == null) {
return null;
}
return Queries.not(queryBuilder.filter().toQuery(context));
@ -45,25 +45,9 @@ public class NotQueryBuilderTest extends BaseQueryTestCase<NotQueryBuilder> {
return new NotQueryBuilder(RandomQueryBuilder.createQuery(random()));
}
/**
* test corner case where no inner query exist
*/
@Test
public void testNoInnerQuerry() throws QueryParsingException, IOException {
NotQueryBuilder notQuery = new NotQueryBuilder(null);
assertNull(notQuery.toQuery(createContext()));
}
/**
* Test corner case where inner queries returns null.
* This is legal, e.g. here {@link ConstantScoreQueryBuilder} can wrap a filter
* element that itself parses to <tt>null</tt>, e.g.
* '{ "constant_score" : "filter {} }'
*/
@Test
public void testInnerQuery() throws QueryParsingException, IOException {
NotQueryBuilder notQuery = new NotQueryBuilder(new ConstantScoreQueryBuilder(null));
assertNull(notQuery.toQuery(createContext()));
@Test(expected=NullPointerException.class)
public void testNotQueryBuilderNull() {
new NotQueryBuilder(null);
}
/**

View File

@ -44,6 +44,9 @@ public class OrQueryBuilderTest extends BaseQueryTestCase<OrQueryBuilder> {
query.add(innerQuery, Occur.SHOULD);
}
}
if (query.clauses().isEmpty()) {
return null;
}
return query;
}
@ -94,4 +97,14 @@ public class OrQueryBuilderTest extends BaseQueryTestCase<OrQueryBuilder> {
}
assertValidate(orQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new OrQueryBuilder(EmptyQueryBuilder.PROTOTYPE, null);
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new OrQueryBuilder().add(null);
}
}

View File

@ -21,8 +21,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
@ -32,11 +30,12 @@ public class QueryFilterBuilderTest extends BaseQueryTestCase<QueryFilterBuilder
@Override
protected Query doCreateExpectedQuery(QueryFilterBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(queryBuilder.innerQuery().toQuery(context));
Query query = queryBuilder.innerQuery().toQuery(context);
return query != null ? new ConstantScoreQuery(query) : query;
}
/**
* @return a AndQueryBuilder with random limit between 0 and 20
* @return a QueryFilterBuilder with random inner query
*/
@Override
protected QueryFilterBuilder doCreateTestQueryBuilder() {
@ -44,32 +43,9 @@ public class QueryFilterBuilderTest extends BaseQueryTestCase<QueryFilterBuilder
return new QueryFilterBuilder(innerQuery);
}
/**
* test corner case where no inner query exist
*/
@Test
public void testNoInnerQuery() throws QueryParsingException, IOException {
QueryFilterBuilder queryFilterQuery = new QueryFilterBuilder(null);
assertNull(queryFilterQuery.toQuery(createContext()));
}
/**
* test wrapping an inner filter that returns null also returns <tt>null</null> to pass on upwards
*/
@Test
public void testInnerQueryReturnsNull() throws IOException {
QueryParseContext context = createContext();
// create inner filter
String queryString = "{ \"constant_score\" : { \"filter\" : {} }";
XContentParser parser = XContentFactory.xContent(queryString).createParser(queryString);
context.reset(parser);
assertQueryHeader(parser, ConstantScoreQueryBuilder.PROTOTYPE.getName());
QueryBuilder innerQuery = context.indexQueryParserService().queryParser(ConstantScoreQueryBuilder.PROTOTYPE.getName()).fromXContent(context);
// check that when wrapping this filter, toQuery() returns null
QueryFilterBuilder queryFilterQuery = new QueryFilterBuilder(innerQuery);
assertNull(queryFilterQuery.toQuery(createContext()));
@Test(expected=NullPointerException.class)
public void testQueryFilterBuilderNull() {
new QueryFilterBuilder(null);
}
@Test

View File

@ -36,13 +36,15 @@ public class RandomQueryBuilder {
* @return a random {@link QueryBuilder}
*/
public static QueryBuilder createQuery(Random r) {
switch (RandomInts.randomIntBetween(r, 0, 2)) {
switch (RandomInts.randomIntBetween(r, 0, 3)) {
case 0:
return new MatchAllQueryBuilderTest().createTestQueryBuilder();
case 1:
return new TermQueryBuilderTest().createTestQueryBuilder();
case 2:
return new IdsQueryBuilderTest().createTestQueryBuilder();
case 3:
return EmptyQueryBuilder.PROTOTYPE;
default:
throw new UnsupportedOperationException();
}
@ -59,7 +61,7 @@ public class RandomQueryBuilder {
case 0:
return new TermQueryBuilder("", "test");
case 1:
return new BoostingQueryBuilder().negativeBoost(-1f);
return new BoostingQueryBuilder(new MatchAllQueryBuilder(), new MatchAllQueryBuilder()).negativeBoost(-1f);
case 2:
return new CommonTermsQueryBuilder("", "text");
case 3:

View File

@ -964,7 +964,7 @@ public class SimpleIndexQueryParserTests extends ElasticsearchSingleNodeTest {
@Test
public void testBoostingQueryBuilder() throws IOException {
IndexQueryParserService queryParser = queryParser();
Query parsedQuery = queryParser.parse(boostingQuery().positive(termQuery("field1", "value1")).negative(termQuery("field1", "value2")).negativeBoost(0.2f)).query();
Query parsedQuery = queryParser.parse(boostingQuery(termQuery("field1", "value1"), termQuery("field1", "value2")).negativeBoost(0.2f)).query();
assertThat(parsedQuery, instanceOf(BoostingQuery.class));
}

View File

@ -50,7 +50,6 @@ import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.highlight;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.test.hamcrest.RegexMatcher.matches;
import static org.hamcrest.Matchers.*;
@ -1368,7 +1367,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").order("score").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().prepareSearch("test").setSource(source.buildAsBytes()).get();
@ -1386,7 +1385,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").order("score").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().prepareSearch("test").setSource(source.buildAsBytes()).get();
@ -2300,7 +2299,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().search(searchRequest("test").source(source)).actionGet();
@ -2603,10 +2602,10 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
phrase.boost(1);
// Try with a boosting query
response = search.setQuery(boostingQuery().positive(phrase).negative(terms).boost(boost).negativeBoost(1)).get();
response = search.setQuery(boostingQuery(phrase, terms).boost(boost).negativeBoost(1)).get();
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
// Try with a boosting query using a negative boost
response = search.setQuery(boostingQuery().positive(phrase).negative(terms).boost(1).negativeBoost(1/boost)).get();
response = search.setQuery(boostingQuery(phrase, terms).boost(1).negativeBoost(1/boost)).get();
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
}
}

View File

@ -6,6 +6,11 @@ on the query-refactoring feature branch.
=== Java-API
==== BoostingQueryBuilder
Removed setters for mandatory positive/negative query. Both arguments now have
to be supplied at construction time already and have to be non-null.
==== QueryFilterBuilder
Removed the setter `queryName(String queryName)` since this field is not supported