Skip to content

Commit

Permalink
Merge pull request #11142 from jpountz/feature/bool_filter
Browse files Browse the repository at this point in the history
Query DSL: Add `filter` clauses to `bool` queries.
  • Loading branch information
jpountz committed May 13, 2015
2 parents ba20d4b + 6307579 commit 472cc0a
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 212 deletions.
32 changes: 32 additions & 0 deletions docs/reference/migration/migrate_2_0.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,38 @@ Change to ranking behaviour: single-term queries on numeric fields now score in
Previously, term queries on numeric fields were deliberately prevented from using the usual Lucene scoring logic and this behaviour was undocumented and, to some, unexpected.
If the introduction of scoring to numeric fields is undesirable for your query clauses the fix is simple: wrap them in a `constant_score` or use a `filter` expression instead.

The `filtered` query is deprecated. Instead you should use a `bool` query with
a `must` clause for the query and a `filter` clause for the filter. For instance
the below query:

[source,json]
---------------
{
"filtered": {
"query": {
// query
},
"filter": {
// filter
}
}
}
---------------
can be replaced with
[source,json]
---------------
{
"bool": {
"must": {
// query
},
"filter": {
// filter
}
}
}
---------------
and will produce the same scores.

The `fuzzy_like_this` and `fuzzy_like_this_field` queries have been removed.

Expand Down
9 changes: 8 additions & 1 deletion docs/reference/query-dsl/bool-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ occurrence types are:
[cols="<,<",options="header",]
|=======================================================================
|Occur |Description
|`must` |The clause (query) must appear in matching documents.
|`must` |The clause (query) must appear in matching documents and will
contribute to the score.

|`filter` |The clause (query) must appear in matching documents. However unlike
`must` the score of the query will be ignored.

|`should` |The clause (query) should appear in the matching document. In
a boolean query with no `must` clauses, one or more `should` clauses
Expand Down Expand Up @@ -41,6 +45,9 @@ final `_score` for each document.
"must" : {
"term" : { "user" : "kimchy" }
},
"filter": {
"term" : { "tag" : "tech" }
}
"must_not" : {
"range" : {
"age" : { "from" : 10, "to" : 20 }
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/query-dsl/filtered-query.asciidoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[[query-dsl-filtered-query]]
== Filtered Query

deprecated[2.0.0, Use the `bool` query instead with a `must` clause for the query and a `filter` clause for the filter]

The `filtered` query is used to combine a query which will be used for
scoring with another query which will only be used for filtering the result
set.
Expand Down
24 changes: 19 additions & 5 deletions src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
*/
public class BoolQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<BoolQueryBuilder> {

private ArrayList<QueryBuilder> mustClauses = new ArrayList<>();
private final List<QueryBuilder> mustClauses = new ArrayList<>();

private ArrayList<QueryBuilder> mustNotClauses = new ArrayList<>();
private final List<QueryBuilder> mustNotClauses = new ArrayList<>();

private ArrayList<QueryBuilder> shouldClauses = new ArrayList<>();
private final List<QueryBuilder> filterClauses = new ArrayList<>();

private final List<QueryBuilder> shouldClauses = new ArrayList<>();

private float boost = -1;

Expand All @@ -48,15 +50,26 @@ public class BoolQueryBuilder extends BaseQueryBuilder implements BoostableQuery
private String queryName;

/**
* Adds a query that <b>must</b> appear in the matching documents.
* Adds a query that <b>must</b> appear in the matching documents and will
* contribute to scoring.
*/
public BoolQueryBuilder must(QueryBuilder queryBuilder) {
mustClauses.add(queryBuilder);
return this;
}

/**
* Adds a query that <b>must not</b> appear in the matching documents.
* Adds a query that <b>must</b> appear in the matching documents but will
* not contribute to scoring.
*/
public BoolQueryBuilder filter(QueryBuilder queryBuilder) {
filterClauses.add(queryBuilder);
return this;
}

/**
* Adds a query that <b>must not</b> appear in the matching documents and
* will not contribute to scoring.
*/
public BoolQueryBuilder mustNot(QueryBuilder queryBuilder) {
mustNotClauses.add(queryBuilder);
Expand Down Expand Up @@ -149,6 +162,7 @@ public BoolQueryBuilder queryName(String queryName) {
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("bool");
doXArrayContent("must", mustClauses, builder, params);
doXArrayContent("filter", filterClauses, builder, params);
doXArrayContent("must_not", mustNotClauses, builder, params);
doXArrayContent("should", shouldClauses, builder, params);
if (boost != -1) {
Expand Down
66 changes: 42 additions & 24 deletions src/main/java/org/elasticsearch/index/query/BoolQueryParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,54 +71,72 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
} else if (parseContext.isDeprecatedSetting(currentFieldName)) {
// skip
} else if (token == XContentParser.Token.START_OBJECT) {
if ("must".equals(currentFieldName)) {
switch (currentFieldName) {
case "must":
Query query = parseContext.parseInnerQuery();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
}
} else if ("must_not".equals(currentFieldName) || "mustNot".equals(currentFieldName)) {
Query query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
}
} else if ("should".equals(currentFieldName)) {
Query query = parseContext.parseInnerQuery();
break;
case "should":
query = parseContext.parseInnerQuery();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
}
} else {
break;
case "filter":
query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
}
break;
default:
throw new QueryParsingException(parseContext, "[bool] query does not support [" + currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_ARRAY) {
if ("must".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
switch (currentFieldName) {
case "must":
Query query = parseContext.parseInnerQuery();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
}
}
} else if ("must_not".equals(currentFieldName) || "mustNot".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
Query query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
}
}
} else if ("should".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
Query query = parseContext.parseInnerQuery();
break;
case "should":
query = parseContext.parseInnerQuery();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
}
break;
case "filter":
query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilter();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
}
break;
default:
throw new QueryParsingException(parseContext, "bool query does not support [" + currentFieldName + "]");
}
} else {
throw new QueryParsingException(parseContext, "bool query does not support [" + currentFieldName + "]");
}
} else if (token.isValue()) {
if ("disable_coord".equals(currentFieldName) || "disableCoord".equals(currentFieldName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@

/**
* A query that applies a filter to the results of another query.
* @deprecated Use {@link BoolQueryBuilder} instead.
*/
@Deprecated
public class FilteredQueryBuilder extends BaseQueryBuilder implements BoostableQueryBuilder<FilteredQueryBuilder> {

private final QueryBuilder queryBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

package org.elasticsearch.index.query;

import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.xcontent.XContentParser;
Expand All @@ -33,6 +32,7 @@
/**
*
*/
@Deprecated
public class FilteredQueryParser implements QueryParser {

public static final String NAME = "filtered";
Expand All @@ -58,7 +58,6 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars

String currentFieldName = null;
XContentParser.Token token;
FilteredQuery.FilterStrategy filterStrategy = FilteredQuery.RANDOM_ACCESS_FILTER_STRATEGY;

while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
Expand All @@ -76,24 +75,7 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
}
} else if (token.isValue()) {
if ("strategy".equals(currentFieldName)) {
String value = parser.text();
if ("query_first".equals(value) || "queryFirst".equals(value)) {
filterStrategy = FilteredQuery.QUERY_FIRST_FILTER_STRATEGY;
} else if ("random_access_always".equals(value) || "randomAccessAlways".equals(value)) {
filterStrategy = FilteredQuery.RANDOM_ACCESS_FILTER_STRATEGY;
} else if ("leap_frog".equals(value) || "leapFrog".equals(value)) {
filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
} else if (value.startsWith("random_access_")) {
filterStrategy = FilteredQuery.RANDOM_ACCESS_FILTER_STRATEGY;
} else if (value.startsWith("randomAccess")) {
filterStrategy = FilteredQuery.RANDOM_ACCESS_FILTER_STRATEGY;
} else if ("leap_frog_query_first".equals(value) || "leapFrogQueryFirst".equals(value)) {
filterStrategy = FilteredQuery.LEAP_FROG_QUERY_FIRST_STRATEGY;
} else if ("leap_frog_filter_first".equals(value) || "leapFrogFilterFirst".equals(value)) {
filterStrategy = FilteredQuery.LEAP_FROG_FILTER_FIRST_STRATEGY;
} else {
throw new QueryParsingException(parseContext, "[filtered] strategy value not supported [" + value + "]");
}
// ignore
} else if ("_name".equals(currentFieldName)) {
queryName = parser.text();
} else if ("boost".equals(currentFieldName)) {
Expand Down Expand Up @@ -131,10 +113,8 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
return q;
}

if (filter instanceof Filter == false) {
filter = new QueryWrapperFilter(filter);
}
FilteredQuery filteredQuery = new FilteredQuery(query, (Filter) filter, filterStrategy);
BooleanQuery filteredQuery = Queries.filtered(query, filter);

filteredQuery.setBoost(boost);
if (queryName != null) {
parseContext.addNamedQuery(queryName, filteredQuery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,10 @@ public static FieldMaskingSpanQueryBuilder fieldMaskingSpanQuery(SpanQueryBuilde
*
* @param queryBuilder The query to apply the filter to
* @param filterBuilder The filter to apply on the query
* @deprecated Use {@link #boolQuery()} instead with a {@code must} clause
* for the query and a {@code filter} clause for the filter.
*/
@Deprecated
public static FilteredQueryBuilder filteredQuery(@Nullable QueryBuilder queryBuilder, @Nullable QueryBuilder filterBuilder) {
return new FilteredQueryBuilder(queryBuilder, filterBuilder);
}
Expand Down

0 comments on commit 472cc0a

Please sign in to comment.