diff --git a/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java b/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java index d41daa7363f..b94f657de94 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java +++ b/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java @@ -122,12 +122,13 @@ public abstract class AggregationBuilder> extend internalXContent(builder, params); if (aggregations != null || aggregationsBinary != null) { - builder.startObject("aggregations"); if (aggregations != null) { + builder.startObject("aggregations"); for (AbstractAggregationBuilder subAgg : aggregations) { subAgg.toXContent(builder, params); } + builder.endObject(); } if (aggregationsBinary != null) { @@ -138,7 +139,6 @@ public abstract class AggregationBuilder> extend } } - builder.endObject(); } return builder.endObject(); diff --git a/src/main/java/org/elasticsearch/search/aggregations/AggregatorParsers.java b/src/main/java/org/elasticsearch/search/aggregations/AggregatorParsers.java index d299b099b9d..105f46187de 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/AggregatorParsers.java +++ b/src/main/java/org/elasticsearch/search/aggregations/AggregatorParsers.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; @@ -140,45 +141,66 @@ public class AggregatorParsers { final String fieldName = parser.currentName(); token = parser.nextToken(); - if (token != XContentParser.Token.START_OBJECT) { - throw new SearchParseException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] under [" + fieldName - + "], but got a [" + token + "] in [" + aggregationName + "]", parser.getTokenLocation()); - } - - switch (fieldName) { + if ("aggregations_binary".equals(fieldName)) { + if (subFactories != null) { + throw new SearchParseException(context, "Found two sub aggregation definitions under [" + aggregationName + "]", + parser.getTokenLocation()); + } + XContentParser binaryParser = null; + if (token == XContentParser.Token.VALUE_STRING || token == XContentParser.Token.VALUE_EMBEDDED_OBJECT) { + byte[] source = parser.binaryValue(); + binaryParser = XContentFactory.xContent(source).createParser(source); + } else { + throw new SearchParseException(context, "Expected [" + XContentParser.Token.VALUE_STRING + " or " + + XContentParser.Token.VALUE_EMBEDDED_OBJECT + "] for [" + fieldName + "], but got a [" + token + "] in [" + + aggregationName + "]", parser.getTokenLocation()); + } + XContentParser.Token binaryToken = binaryParser.nextToken(); + if (binaryToken != XContentParser.Token.START_OBJECT) { + throw new SearchParseException(context, "Expected [" + XContentParser.Token.START_OBJECT + + "] as first token when parsing [" + fieldName + "], but got a [" + binaryToken + "] in [" + + aggregationName + "]", parser.getTokenLocation()); + } + subFactories = parseAggregators(binaryParser, context, level + 1); + } else if (token == XContentParser.Token.START_OBJECT) { + switch (fieldName) { case "meta": metaData = parser.map(); break; case "aggregations": case "aggs": if (subFactories != null) { - throw new SearchParseException(context, "Found two sub aggregation definitions under [" + aggregationName + "]", - parser.getTokenLocation()); + throw new SearchParseException(context, + "Found two sub aggregation definitions under [" + aggregationName + "]", parser.getTokenLocation()); } - subFactories = parseAggregators(parser, context, level+1); + subFactories = parseAggregators(parser, context, level + 1); break; default: if (aggFactory != null) { - throw new SearchParseException(context, "Found two aggregation type definitions in [" + aggregationName + "]: [" - + aggFactory.type + "] and [" + fieldName + "]", parser.getTokenLocation()); + throw new SearchParseException(context, "Found two aggregation type definitions in [" + aggregationName + + "]: [" + aggFactory.type + "] and [" + fieldName + "]", parser.getTokenLocation()); } - if (pipelineAggregatorFactory != null) { - throw new SearchParseException(context, "Found two aggregation type definitions in [" + aggregationName + "]: [" - + pipelineAggregatorFactory + "] and [" + fieldName + "]", parser.getTokenLocation()); + if (pipelineAggregatorFactory != null) { + throw new SearchParseException(context, "Found two aggregation type definitions in [" + aggregationName + + "]: [" + pipelineAggregatorFactory + "] and [" + fieldName + "]", parser.getTokenLocation()); } Aggregator.Parser aggregatorParser = parser(fieldName); if (aggregatorParser == null) { - PipelineAggregator.Parser pipelineAggregatorParser = pipelineAggregator(fieldName); - if (pipelineAggregatorParser == null) { + PipelineAggregator.Parser pipelineAggregatorParser = pipelineAggregator(fieldName); + if (pipelineAggregatorParser == null) { throw new SearchParseException(context, "Could not find aggregator type [" + fieldName + "] in [" - + aggregationName + "]", parser.getTokenLocation()); + + aggregationName + "]", parser.getTokenLocation()); } else { - pipelineAggregatorFactory = pipelineAggregatorParser.parse(aggregationName, parser, context); + pipelineAggregatorFactory = pipelineAggregatorParser.parse(aggregationName, parser, context); } } else { aggFactory = aggregatorParser.parse(aggregationName, parser, context); } + } + } else { + throw new SearchParseException(context, "Expected [" + XContentParser.Token.START_OBJECT + "] under [" + fieldName + + "], but got a [" + token + "] in [" + aggregationName + "]", parser.getTokenLocation()); } } diff --git a/src/test/java/org/elasticsearch/search/aggregations/AggregationsBinaryTests.java b/src/test/java/org/elasticsearch/search/aggregations/AggregationsBinaryTests.java new file mode 100644 index 00000000000..2e27c683046 --- /dev/null +++ b/src/test/java/org/elasticsearch/search/aggregations/AggregationsBinaryTests.java @@ -0,0 +1,142 @@ +/* + * 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.search.aggregations; + +import org.elasticsearch.action.index.IndexRequestBuilder; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.Requests; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.search.aggregations.bucket.terms.Terms; +import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; +import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; +import org.elasticsearch.test.ElasticsearchIntegrationTest; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; + +@ElasticsearchIntegrationTest.SuiteScopeTest +public class AggregationsBinaryTests extends ElasticsearchIntegrationTest { + + private static final String STRING_FIELD_NAME = "s_value"; + private static final String INT_FIELD_NAME = "i_value"; + + @Override + public void setupSuiteScopeCluster() throws Exception { + createIndex("idx"); + List builders = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + builders.add(client().prepareIndex("idx", "type").setSource( + jsonBuilder().startObject().field(STRING_FIELD_NAME, "val" + i).field(INT_FIELD_NAME, i).endObject())); + } + indexRandom(true, builders); + ensureSearchable(); + } + + @Test + public void testAggregationsBinary() throws Exception { + TermsBuilder termsBuilder = AggregationBuilders.terms("terms").field(STRING_FIELD_NAME); + TermsBuilder subTerm = AggregationBuilders.terms("subterms").field(INT_FIELD_NAME); + + // Create an XContentBuilder from sub aggregation + XContentBuilder subTermContentBuilder = JsonXContent.contentBuilder().startObject(); + subTerm.toXContent(subTermContentBuilder, ToXContent.EMPTY_PARAMS); + subTermContentBuilder.endObject(); + + // Add sub aggregation as a XContentBuilder (binary_aggregation) + termsBuilder.subAggregation(subTermContentBuilder); + + SearchResponse response = client().prepareSearch("idx").setTypes("type").addAggregation(termsBuilder).execute().actionGet(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + for (int i = 0; i < 5; i++) { + Terms.Bucket bucket = terms.getBucketByKey("val" + i); + assertThat(bucket, notNullValue()); + assertThat(bucket.getKeyAsString(), equalTo("val" + i)); + assertThat(bucket.getDocCount(), equalTo(1l)); + Aggregations subAggs = bucket.getAggregations(); + assertThat(subAggs, notNullValue()); + assertThat(subAggs.asList().size(), equalTo(1)); + Terms subTerms = subAggs.get("subterms"); + assertThat(subTerms, notNullValue()); + List subTermsBuckets = subTerms.getBuckets(); + assertThat(subTermsBuckets, notNullValue()); + assertThat(subTermsBuckets.size(), equalTo(1)); + assertThat(((Number) subTermsBuckets.get(0).getKey()).intValue(), equalTo(i)); + assertThat(subTermsBuckets.get(0).getDocCount(), equalTo(1l)); + } + } + + @Test + public void testAggregationsBinarySameContentType() throws Exception { + TermsBuilder termsBuilder = AggregationBuilders.terms("terms").field(STRING_FIELD_NAME); + TermsBuilder subTerm = AggregationBuilders.terms("subterms").field(INT_FIELD_NAME); + + // Create an XContentBuilder from sub aggregation + + XContentBuilder subTermContentBuilder = XContentFactory.contentBuilder(Requests.CONTENT_TYPE); + subTermContentBuilder.startObject(); + subTerm.toXContent(subTermContentBuilder, ToXContent.EMPTY_PARAMS); + subTermContentBuilder.endObject(); + + // Add sub aggregation as a XContentBuilder (binary_aggregation) + termsBuilder.subAggregation(subTermContentBuilder); + + SearchResponse response = client().prepareSearch("idx").setTypes("type").addAggregation(termsBuilder).execute().actionGet(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + for (int i = 0; i < 5; i++) { + Terms.Bucket bucket = terms.getBucketByKey("val" + i); + assertThat(bucket, notNullValue()); + assertThat(bucket.getKeyAsString(), equalTo("val" + i)); + assertThat(bucket.getDocCount(), equalTo(1l)); + Aggregations subAggs = bucket.getAggregations(); + assertThat(subAggs, notNullValue()); + assertThat(subAggs.asList().size(), equalTo(1)); + Terms subTerms = subAggs.get("subterms"); + assertThat(subTerms, notNullValue()); + List subTermsBuckets = subTerms.getBuckets(); + assertThat(subTermsBuckets, notNullValue()); + assertThat(subTermsBuckets.size(), equalTo(1)); + assertThat(((Number) subTermsBuckets.get(0).getKey()).intValue(), equalTo(i)); + assertThat(subTermsBuckets.get(0).getDocCount(), equalTo(1l)); + } + } +}