Query DSL: Remove NotQueryBuilder

The NotQueryBuilder has been deprecated on the 2.x branches
and can be removed with the next major version. It can be
replaced by boolean query with added mustNot() clause.

Closes #13761
This commit is contained in:
Christoph Büscher 2015-10-20 13:56:50 +02:00
parent d3db061b3a
commit 5d25bc30cd
12 changed files with 70 additions and 447 deletions

View File

@ -1,99 +0,0 @@
/*
* 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.lucene.search.Queries;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Objects;
/**
* A filter that matches documents matching boolean combinations of other filters.
*/
public class NotQueryBuilder extends AbstractQueryBuilder<NotQueryBuilder> {
public static final String NAME = "not";
private final QueryBuilder filter;
static final NotQueryBuilder PROTOTYPE = new NotQueryBuilder(EmptyQueryBuilder.PROTOTYPE);
public NotQueryBuilder(QueryBuilder filter) {
if (filter == null) {
throw new IllegalArgumentException("inner filter cannot be null");
}
this.filter = filter;
}
/**
* @return the query added to "not".
*/
public QueryBuilder innerQuery() {
return this.filter;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
builder.field("query");
filter.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
Query luceneQuery = filter.toFilter(context);
if (luceneQuery == null) {
return null;
}
return Queries.not(luceneQuery);
}
@Override
protected int doHashCode() {
return Objects.hash(filter);
}
@Override
protected boolean doEquals(NotQueryBuilder other) {
return Objects.equals(filter, other.filter);
}
@Override
protected NotQueryBuilder doReadFrom(StreamInput in) throws IOException {
QueryBuilder queryBuilder = in.readQuery();
return new NotQueryBuilder(queryBuilder);
}
@Override
protected void doWriteTo(StreamOutput out) throws IOException {
out.writeQuery(filter);
}
@Override
public String getWriteableName() {
return NAME;
}
}

View File

@ -1,90 +0,0 @@
/*
* 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.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
/**
* Parser for not query
*/
public class NotQueryParser implements QueryParser<NotQueryBuilder> {
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
@Override
public String[] names() {
return new String[]{NotQueryBuilder.NAME};
}
@Override
public NotQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser();
QueryBuilder query = null;
boolean queryFound = false;
String queryName = null;
String currentFieldName = null;
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (parseContext.isDeprecatedSetting(currentFieldName)) {
// skip
} else if (token == XContentParser.Token.START_OBJECT) {
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
query = parseContext.parseInnerQueryBuilder();
queryFound = true;
} else {
queryFound = true;
// its the filter, and the name is the field
query = parseContext.parseInnerQueryBuilderByName(currentFieldName);
}
} else if (token.isValue()) {
if ("_name".equals(currentFieldName)) {
queryName = parser.text();
} else if ("boost".equals(currentFieldName)) {
boost = parser.floatValue();
} else {
throw new ParsingException(parser.getTokenLocation(), "[not] query does not support [" + currentFieldName + "]");
}
}
}
if (!queryFound) {
throw new ParsingException(parser.getTokenLocation(), "query is required when using `not` query");
}
NotQueryBuilder notQueryBuilder = new NotQueryBuilder(query);
notQueryBuilder.queryName(queryName);
notQueryBuilder.boost(boost);
return notQueryBuilder;
}
@Override
public NotQueryBuilder getBuilderPrototype() {
return NotQueryBuilder.PROTOTYPE;
}
}

View File

@ -831,10 +831,6 @@ public abstract class QueryBuilders {
return new MissingQueryBuilder(name, nullValue, existence);
}
public static NotQueryBuilder notQuery(QueryBuilder filter) {
return new NotQueryBuilder(filter);
}
private QueryBuilders() {
}

View File

@ -91,7 +91,12 @@ public class QueryParseContext {
throw new ParsingException(parser.getTokenLocation(), "[_na] query malformed, no field after start_object");
}
QueryBuilder result = parseInnerQueryBuilderByName(queryName);
QueryParser queryParser = queryParser(queryName);
if (queryParser == null) {
throw new ParsingException(parser.getTokenLocation(), "No query registered for [" + queryName + "]");
}
QueryBuilder result = queryParser.fromXContent(this);
if (parser.currentToken() == XContentParser.Token.END_OBJECT || parser.currentToken() == XContentParser.Token.END_ARRAY) {
// if we are at END_OBJECT, move to the next one...
parser.nextToken();
@ -99,14 +104,6 @@ public class QueryParseContext {
return result;
}
public QueryBuilder parseInnerQueryBuilderByName(String queryName) throws IOException {
QueryParser queryParser = queryParser(queryName);
if (queryParser == null) {
throw new ParsingException(parser.getTokenLocation(), "No query registered for [" + queryName + "]");
}
return queryParser.fromXContent(this);
}
public ParseFieldMatcher parseFieldMatcher() {
return parseFieldMatcher;
}

View File

@ -108,7 +108,6 @@ public class IndicesModule extends AbstractModule {
registerQueryParser(GeohashCellQuery.Parser.class);
registerQueryParser(GeoPolygonQueryParser.class);
registerQueryParser(QueryFilterParser.class);
registerQueryParser(NotQueryParser.class);
registerQueryParser(ExistsQueryParser.class);
registerQueryParser(MissingQueryParser.class);
registerQueryParser(MatchNoneQueryParser.class);

View File

@ -1,112 +0,0 @@
/*
* 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.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.nullValue;
public class NotQueryBuilderTests extends AbstractQueryTestCase<NotQueryBuilder> {
/**
* @return a NotQueryBuilder with random limit between 0 and 20
*/
@Override
protected NotQueryBuilder doCreateTestQueryBuilder() {
return new NotQueryBuilder(RandomQueryBuilder.createQuery(random()));
}
@Override
protected void doAssertLuceneQuery(NotQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
Query filter = queryBuilder.innerQuery().toQuery(context);
if (filter == null) {
assertThat(query, nullValue());
} else {
assertThat(query, instanceOf(BooleanQuery.class));
BooleanQuery booleanQuery = (BooleanQuery) query;
assertThat(booleanQuery.clauses().size(), equalTo(2));
assertThat(booleanQuery.clauses().get(0).getOccur(), equalTo(BooleanClause.Occur.MUST));
assertThat(booleanQuery.clauses().get(0).getQuery(), instanceOf(MatchAllDocsQuery.class));
assertThat(booleanQuery.clauses().get(1).getOccur(), equalTo(BooleanClause.Occur.MUST_NOT));
assertThat(booleanQuery.clauses().get(1).getQuery(), instanceOf(filter.getClass()));
}
}
@Test(expected=ParsingException.class)
public void testMissingFilterSection() throws IOException {
String queryString = "{ \"not\" : {}";
parseQuery(queryString);
}
@Override
protected Map<String, NotQueryBuilder> getAlternateVersions() {
Map<String, NotQueryBuilder> alternateVersions = new HashMap<>();
QueryBuilder innerQuery = createTestQueryBuilder().innerQuery();
//not doesn't support empty query when query/filter element is not specified
if (innerQuery != EmptyQueryBuilder.PROTOTYPE) {
NotQueryBuilder testQuery2 = new NotQueryBuilder(innerQuery);
String contentString2 = "{\n" +
" \"not\" : " + testQuery2.innerQuery().toString() + "\n}";
alternateVersions.put(contentString2, testQuery2);
}
return alternateVersions;
}
public void testDeprecatedXContent() throws IOException {
String deprecatedJson = "{\n" +
" \"not\" : {\n" +
" \"filter\" : " + EmptyQueryBuilder.PROTOTYPE.toString() + "\n" +
" }\n" +
"}";
try {
parseQuery(deprecatedJson);
fail("filter is deprecated");
} catch (IllegalArgumentException ex) {
assertEquals("Deprecated field [filter] used, expected [query] instead", ex.getMessage());
}
NotQueryBuilder queryBuilder = (NotQueryBuilder) parseQuery(deprecatedJson, ParseFieldMatcher.EMPTY);
assertEquals(EmptyQueryBuilder.PROTOTYPE, queryBuilder.innerQuery());
}
@Test
public void testValidate() {
try {
new NotQueryBuilder(null);
fail("cannot be null");
} catch (IllegalArgumentException e) {
// expected
}
}
}

View File

@ -250,11 +250,6 @@ public class QueryDSLDocumentationTests extends ESTestCase {
.scoreMode(ScoreMode.Avg);
}
@Test
public void testNot() {
notQuery(rangeQuery("price").from("1").to("2"));
}
@Test
public void testPrefix() {
prefixQuery("brand", "heine");

View File

@ -25,7 +25,6 @@ import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.explain.ExplainResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery;
@ -70,7 +69,6 @@ import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.notQuery;
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
@ -1242,13 +1240,13 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
ScoreMode scoreMode = randomFrom(ScoreMode.values());
SearchResponse searchResponse = client().prepareSearch("test")
.setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "blue")).scoreMode(scoreMode)).filter(notQuery(termQuery("p_field", "3"))))
.setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "blue")).scoreMode(scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3"))))
.get();
assertNoFailures(searchResponse);
assertThat(searchResponse.getHits().totalHits(), equalTo(1l));
searchResponse = client().prepareSearch("test")
.setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "red")).scoreMode(scoreMode)).filter(notQuery(termQuery("p_field", "3"))))
.setQuery(boolQuery().must(QueryBuilders.hasChildQuery("child", termQuery("c_field", "red")).scoreMode(scoreMode)).filter(boolQuery().mustNot(termQuery("p_field", "3"))))
.get();
assertNoFailures(searchResponse);
assertThat(searchResponse.getHits().totalHits(), equalTo(2l));
@ -1520,12 +1518,12 @@ public class ChildQuerySearchIT extends ESIntegTestCase {
indexRandom(true, indexRequests);
SearchResponse searchResponse = client().prepareSearch("test")
.setQuery(constantScoreQuery(hasParentQuery("parent", notQuery(termQuery("field1", "a")))))
.setQuery(constantScoreQuery(hasParentQuery("parent", boolQuery().mustNot(termQuery("field1", "a")))))
.get();
assertHitCount(searchResponse, 0l);
searchResponse = client().prepareSearch("test")
.setQuery(hasParentQuery("parent", constantScoreQuery(notQuery(termQuery("field1", "a")))))
.setQuery(hasParentQuery("parent", constantScoreQuery(boolQuery().mustNot(termQuery("field1", "a")))))
.get();
assertHitCount(searchResponse, 0l);

View File

@ -1,15 +0,0 @@
[[java-query-dsl-not-query]]
==== Not Query
See {ref}/query-dsl-not-query.html[Not Query]
[source,java]
--------------------------------------------------
QueryBuilder qb = notQuery(
rangeQuery("price").from("1").to("2") <1>
);
--------------------------------------------------
<1> query

View File

@ -285,6 +285,12 @@ The two individual setters for existence() and nullValue() were removed in favou
optional constructor settings in order to better capture and validate their interdependent
settings at construction time.
==== NotQueryBuilder
The NotQueryBuilder which was deprecated in 2.1.0 is removed. As a replacement use BoolQueryBuilder
with added mustNot() clause. So instead of using `new NotQueryBuilder(filter)` now use
`new BoolQueryBuilder().mustNot(filter)`.
==== TermsQueryBuilder
Remove the setter for `termsLookup()`, making it only possible to either use a TermsLookup object or

View File

@ -1,51 +0,0 @@
[[query-dsl-not-query]]
=== Not Query
A query that filters out matched documents using a query. For example:
[source,js]
--------------------------------------------------
{
"bool" : {
"must" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"not" : {
"range" : {
"postDate" : {
"from" : "2010-03-01",
"to" : "2010-04-01"
}
}
}
}
}
}
--------------------------------------------------
Or, in a longer form with a `filter` element:
[source,js]
--------------------------------------------------
{
"bool" : {
"must" : {
"term" : { "name.first" : "shay" }
},
"filter" : {
"not" : {
"filter" : {
"range" : {
"postDate" : {
"from" : "2010-03-01",
"to" : "2010-04-01"
}
}
}
}
}
}
}
--------------------------------------------------

View File

@ -73,7 +73,6 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.missingQuery;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.notQuery;
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
@ -180,7 +179,7 @@ public class SearchQueryIT extends ESIntegTestCase {
.setPostFilter(
boolQuery().must(
matchAllQuery()).must(
notQuery(boolQuery().must(termQuery("field1", "value1")).must(
boolQuery().mustNot(boolQuery().must(termQuery("field1", "value1")).must(
termQuery("field1", "value2"))))).get(),
3l);
assertHitCount(
@ -189,11 +188,11 @@ public class SearchQueryIT extends ESIntegTestCase {
boolQuery().must(
boolQuery().should(termQuery("field1", "value1")).should(termQuery("field1", "value2"))
.should(termQuery("field1", "value3"))).filter(
notQuery(boolQuery().must(termQuery("field1", "value1")).must(
boolQuery().mustNot(boolQuery().must(termQuery("field1", "value1")).must(
termQuery("field1", "value2"))))).get(),
3l);
assertHitCount(
client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(notQuery(termQuery("field1", "value3"))).get(),
client().prepareSearch().setQuery(matchAllQuery()).setPostFilter(boolQuery().mustNot(termQuery("field1", "value3"))).get(),
2l);
}