Aggregations: Fixes Filter and FiltersAggregation to work with empty query

This fix ensures the filter and filters aggregation will not throw a NPE when `{}` is passed in as a filter. Instead `{}` is interpreted as a MatchAllDocsQuery.

Closes #17518
This commit is contained in:
Colin Goodheart-Smithe 2016-04-05 16:16:07 +01:00
parent 7037670aeb
commit 65a5366cba
5 changed files with 53 additions and 7 deletions

View File

@ -23,6 +23,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.AggregatorBuilder; import org.elasticsearch.search.aggregations.AggregatorBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories;
@ -51,8 +52,12 @@ public class FilterAggregatorBuilder extends AggregatorBuilder<FilterAggregatorB
if (filter == null) { if (filter == null) {
throw new IllegalArgumentException("[filter] must not be null: [" + name + "]"); throw new IllegalArgumentException("[filter] must not be null: [" + name + "]");
} }
if (filter instanceof EmptyQueryBuilder) {
this.filter = new MatchAllQueryBuilder();
} else {
this.filter = filter; this.filter = filter;
} }
}
@Override @Override
protected AggregatorFactory<?> doBuild(AggregationContext context, AggregatorFactory<?> parent, protected AggregatorFactory<?> doBuild(AggregationContext context, AggregatorFactory<?> parent,

View File

@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.bucket.filter;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.Aggregator;
@ -45,9 +44,7 @@ public class FilterParser implements Aggregator.Parser {
throw new ParsingException(null, "filter cannot be null in filter aggregation [{}]", aggregationName); throw new ParsingException(null, "filter cannot be null in filter aggregation [{}]", aggregationName);
} }
FilterAggregatorBuilder factory = new FilterAggregatorBuilder(aggregationName, return new FilterAggregatorBuilder(aggregationName, filter);
filter == null ? new MatchAllQueryBuilder() : filter);
return factory;
} }
@Override @Override

View File

@ -30,6 +30,7 @@ import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories;
@ -70,8 +71,12 @@ public class FiltersAggregator extends BucketsAggregator {
throw new IllegalArgumentException("[filter] must not be null"); throw new IllegalArgumentException("[filter] must not be null");
} }
this.key = key; this.key = key;
if (filter instanceof EmptyQueryBuilder) {
this.filter = new MatchAllQueryBuilder();
} else {
this.filter = filter; this.filter = filter;
} }
}
public String key() { public String key() {
return key; return key;

View File

@ -23,6 +23,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.filter.Filter;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
@ -108,7 +109,18 @@ public class FilterIT extends ESIntegTestCase {
// See NullPointer issue when filters are empty: // See NullPointer issue when filters are empty:
// https://github.com/elastic/elasticsearch/issues/8438 // https://github.com/elastic/elasticsearch/issues/8438
public void testEmptyFilterDeclarations() throws Exception { public void testEmptyFilterDeclarations() throws Exception {
QueryBuilder emptyFilter = new BoolQueryBuilder(); QueryBuilder<?> emptyFilter = new BoolQueryBuilder();
SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).execute().actionGet();
assertSearchResponse(response);
Filter filter = response.getAggregations().get("tag1");
assertThat(filter, notNullValue());
assertThat(filter.getDocCount(), equalTo((long) numDocs));
}
public void testEmptyFilter() throws Exception {
QueryBuilder<?> emptyFilter = new EmptyQueryBuilder();
SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).execute().actionGet(); SearchResponse response = client().prepareSearch("idx").addAggregation(filter("tag1", emptyFilter)).execute().actionGet();
assertSearchResponse(response); assertSearchResponse(response);

View File

@ -24,6 +24,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.aggregations.bucket.filters.Filters; import org.elasticsearch.search.aggregations.bucket.filters.Filters;
import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregator.KeyedFilter; import org.elasticsearch.search.aggregations.bucket.filters.FiltersAggregator.KeyedFilter;
@ -201,6 +202,32 @@ public class FiltersIT extends ESIntegTestCase {
assertThat((double) propertiesCounts[1], equalTo((double) sum / numTag2Docs)); assertThat((double) propertiesCounts[1], equalTo((double) sum / numTag2Docs));
} }
public void testEmptyFilter() throws Exception {
QueryBuilder<?> emptyFilter = new EmptyQueryBuilder();
SearchResponse response = client().prepareSearch("idx").addAggregation(filters("tag1", emptyFilter)).execute().actionGet();
assertSearchResponse(response);
Filters filter = response.getAggregations().get("tag1");
assertThat(filter, notNullValue());
assertThat(filter.getBuckets().size(), equalTo(1));
assertThat(filter.getBuckets().get(0).getDocCount(), equalTo((long) numDocs));
}
public void testEmptyKeyedFilter() throws Exception {
QueryBuilder<?> emptyFilter = new EmptyQueryBuilder();
SearchResponse response = client().prepareSearch("idx").addAggregation(filters("tag1", new KeyedFilter("foo", emptyFilter)))
.execute().actionGet();
assertSearchResponse(response);
Filters filter = response.getAggregations().get("tag1");
assertThat(filter, notNullValue());
assertThat(filter.getBuckets().size(), equalTo(1));
assertThat(filter.getBuckets().get(0).getKey(), equalTo("foo"));
assertThat(filter.getBuckets().get(0).getDocCount(), equalTo((long) numDocs));
}
public void testAsSubAggregation() { public void testAsSubAggregation() {
SearchResponse response = client().prepareSearch("idx") SearchResponse response = client().prepareSearch("idx")
.addAggregation( .addAggregation(