/* * 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.bucket; import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.bucket.filter.Filter; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.nested.Nested; import org.elasticsearch.search.aggregations.bucket.terms.LongTerms; import org.elasticsearch.search.aggregations.bucket.terms.StringTerms; import org.elasticsearch.search.aggregations.bucket.terms.Terms; import org.elasticsearch.search.aggregations.bucket.terms.Terms.Bucket; import org.elasticsearch.search.aggregations.metrics.max.Max; import org.elasticsearch.search.aggregations.metrics.stats.Stats; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.test.ESIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; import java.util.List; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; import static org.elasticsearch.index.query.QueryBuilders.nestedQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; import static org.elasticsearch.search.aggregations.AggregationBuilders.filter; import static org.elasticsearch.search.aggregations.AggregationBuilders.histogram; import static org.elasticsearch.search.aggregations.AggregationBuilders.max; import static org.elasticsearch.search.aggregations.AggregationBuilders.nested; import static org.elasticsearch.search.aggregations.AggregationBuilders.stats; import static org.elasticsearch.search.aggregations.AggregationBuilders.sum; import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.hamcrest.core.IsNull.notNullValue; @ESIntegTestCase.SuiteScopeTestCase public class NestedIT extends ESIntegTestCase { private static int numParents; private static int[] numChildren; private static SubAggCollectionMode aggCollectionMode; @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(prepareCreate("idx") .addMapping("type", "nested", "type=nested", "incorrect", "type=object")); ensureGreen("idx"); List<IndexRequestBuilder> builders = new ArrayList<>(); numParents = randomIntBetween(3, 10); numChildren = new int[numParents]; aggCollectionMode = randomFrom(SubAggCollectionMode.values()); logger.info("AGG COLLECTION MODE: {}", aggCollectionMode); int totalChildren = 0; for (int i = 0; i < numParents; ++i) { if (i == numParents - 1 && totalChildren == 0) { // we need at least one child overall numChildren[i] = randomIntBetween(1, 5); } else { numChildren[i] = randomInt(5); } totalChildren += numChildren[i]; } assertTrue(totalChildren > 0); for (int i = 0; i < numParents; i++) { XContentBuilder source = jsonBuilder() .startObject() .field("value", i + 1) .startArray("nested"); for (int j = 0; j < numChildren[i]; ++j) { source = source.startObject().field("value", i + 1 + j).endObject(); } source = source.endArray().endObject(); builders.add(client().prepareIndex("idx", "type", ""+i+1).setSource(source)); } prepareCreate("empty_bucket_idx").addMapping("type", "value", "type=integer", "nested", "type=nested").execute().actionGet(); ensureGreen("empty_bucket_idx"); for (int i = 0; i < 2; i++) { builders.add(client().prepareIndex("empty_bucket_idx", "type", ""+i).setSource(jsonBuilder() .startObject() .field("value", i*2) .startArray("nested") .startObject().field("value", i + 1).endObject() .startObject().field("value", i + 2).endObject() .startObject().field("value", i + 3).endObject() .startObject().field("value", i + 4).endObject() .startObject().field("value", i + 5).endObject() .endArray() .endObject())); } assertAcked(prepareCreate("idx_nested_nested_aggs") .addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties") .startObject("nested1") .field("type", "nested") .startObject("properties") .startObject("nested2") .field("type", "nested") .endObject() .endObject() .endObject() .endObject().endObject().endObject())); ensureGreen("idx_nested_nested_aggs"); builders.add( client().prepareIndex("idx_nested_nested_aggs", "type", "1") .setSource(jsonBuilder().startObject() .startArray("nested1") .startObject() .field("a", "a") .startArray("nested2") .startObject() .field("b", 2) .endObject() .endArray() .endObject() .startObject() .field("a", "b") .startArray("nested2") .startObject() .field("b", 2) .endObject() .endArray() .endObject() .endArray() .endObject()) ); indexRandom(true, builders); ensureSearchable(); } public void testSimple() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(nested("nested", "nested") .subAggregation(stats("nested_value_stats").field("nested.value"))) .execute().actionGet(); assertSearchResponse(response); double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; long sum = 0; long count = 0; for (int i = 0; i < numParents; ++i) { for (int j = 0; j < numChildren[i]; ++j) { final long value = i + 1 + j; min = Math.min(min, value); max = Math.max(max, value); sum += value; ++count; } } Nested nested = response.getAggregations().get("nested"); assertThat(nested, notNullValue()); assertThat(nested.getName(), equalTo("nested")); assertThat(nested.getDocCount(), equalTo(count)); assertThat(nested.getAggregations().asList().isEmpty(), is(false)); Stats stats = nested.getAggregations().get("nested_value_stats"); assertThat(stats, notNullValue()); assertThat(stats.getMin(), equalTo(min)); assertThat(stats.getMax(), equalTo(max)); assertThat(stats.getCount(), equalTo(count)); assertThat(stats.getSum(), equalTo((double) sum)); assertThat(stats.getAvg(), equalTo((double) sum / count)); } public void testNonExistingNestedField() throws Exception { SearchResponse searchResponse = client().prepareSearch("idx") .addAggregation(nested("nested", "value") .subAggregation(stats("nested_value_stats").field("nested.value"))) .execute().actionGet(); Nested nested = searchResponse.getAggregations().get("nested"); assertThat(nested, Matchers.notNullValue()); assertThat(nested.getName(), equalTo("nested")); assertThat(nested.getDocCount(), is(0L)); } public void testNestedWithSubTermsAgg() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(nested("nested", "nested") .subAggregation(terms("values").field("nested.value").size(100) .collectMode(aggCollectionMode))) .execute().actionGet(); assertSearchResponse(response); long docCount = 0; long[] counts = new long[numParents + 6]; for (int i = 0; i < numParents; ++i) { for (int j = 0; j < numChildren[i]; ++j) { final int value = i + 1 + j; ++counts[value]; ++docCount; } } int uniqueValues = 0; for (long count : counts) { if (count > 0) { ++uniqueValues; } } Nested nested = response.getAggregations().get("nested"); assertThat(nested, notNullValue()); assertThat(nested.getName(), equalTo("nested")); assertThat(nested.getDocCount(), equalTo(docCount)); assertThat(((InternalAggregation)nested).getProperty("_count"), equalTo(docCount)); assertThat(nested.getAggregations().asList().isEmpty(), is(false)); LongTerms values = nested.getAggregations().get("values"); assertThat(values, notNullValue()); assertThat(values.getName(), equalTo("values")); assertThat(values.getBuckets(), notNullValue()); assertThat(values.getBuckets().size(), equalTo(uniqueValues)); for (int i = 0; i < counts.length; ++i) { final String key = Long.toString(i); if (counts[i] == 0) { assertNull(values.getBucketByKey(key)); } else { Bucket bucket = values.getBucketByKey(key); assertNotNull(bucket); assertEquals(counts[i], bucket.getDocCount()); } } assertThat(((InternalAggregation)nested).getProperty("values"), sameInstance(values)); } public void testNestedAsSubAggregation() throws Exception { SearchResponse response = client().prepareSearch("idx") .addAggregation(terms("top_values").field("value").size(100) .collectMode(aggCollectionMode) .subAggregation(nested("nested", "nested") .subAggregation(max("max_value").field("nested.value")))) .execute().actionGet(); assertSearchResponse(response); LongTerms values = response.getAggregations().get("top_values"); assertThat(values, notNullValue()); assertThat(values.getName(), equalTo("top_values")); assertThat(values.getBuckets(), notNullValue()); assertThat(values.getBuckets().size(), equalTo(numParents)); for (int i = 0; i < numParents; i++) { String topValue = "" + (i + 1); assertThat(values.getBucketByKey(topValue), notNullValue()); Nested nested = values.getBucketByKey(topValue).getAggregations().get("nested"); assertThat(nested, notNullValue()); Max max = nested.getAggregations().get("max_value"); assertThat(max, notNullValue()); assertThat(max.getValue(), equalTo(numChildren[i] == 0 ? Double.NEGATIVE_INFINITY : (double) i + numChildren[i])); } } public void testNestNestedAggs() throws Exception { SearchResponse response = client().prepareSearch("idx_nested_nested_aggs") .addAggregation(nested("level1", "nested1") .subAggregation(terms("a").field("nested1.a.keyword") .collectMode(aggCollectionMode) .subAggregation(nested("level2", "nested1.nested2") .subAggregation(sum("sum").field("nested1.nested2.b"))))) .get(); assertSearchResponse(response); Nested level1 = response.getAggregations().get("level1"); assertThat(level1, notNullValue()); assertThat(level1.getName(), equalTo("level1")); assertThat(level1.getDocCount(), equalTo(2L)); StringTerms a = level1.getAggregations().get("a"); Terms.Bucket bBucket = a.getBucketByKey("a"); assertThat(bBucket.getDocCount(), equalTo(1L)); Nested level2 = bBucket.getAggregations().get("level2"); assertThat(level2.getDocCount(), equalTo(1L)); Sum sum = level2.getAggregations().get("sum"); assertThat(sum.getValue(), equalTo(2d)); a = level1.getAggregations().get("a"); bBucket = a.getBucketByKey("b"); assertThat(bBucket.getDocCount(), equalTo(1L)); level2 = bBucket.getAggregations().get("level2"); assertThat(level2.getDocCount(), equalTo(1L)); sum = level2.getAggregations().get("sum"); assertThat(sum.getValue(), equalTo(2d)); } public void testEmptyAggregation() throws Exception { SearchResponse searchResponse = client().prepareSearch("empty_bucket_idx") .setQuery(matchAllQuery()) .addAggregation(histogram("histo").field("value").interval(1L).minDocCount(0) .subAggregation(nested("nested", "nested"))) .execute().actionGet(); assertThat(searchResponse.getHits().getTotalHits(), equalTo(2L)); Histogram histo = searchResponse.getAggregations().get("histo"); assertThat(histo, Matchers.notNullValue()); Histogram.Bucket bucket = histo.getBuckets().get(1); assertThat(bucket, Matchers.notNullValue()); Nested nested = bucket.getAggregations().get("nested"); assertThat(nested, Matchers.notNullValue()); assertThat(nested.getName(), equalTo("nested")); assertThat(nested.getDocCount(), is(0L)); } public void testNestedOnObjectField() throws Exception { try { client().prepareSearch("idx") .setQuery(matchAllQuery()) .addAggregation(nested("object_field", "incorrect")) .execute().actionGet(); fail(); } catch (SearchPhaseExecutionException e) { assertThat(e.toString(), containsString("[nested] nested path [incorrect] is not nested")); } } // Test based on: https://github.com/elastic/elasticsearch/issues/9280 public void testParentFilterResolvedCorrectly() throws Exception { XContentBuilder mapping = jsonBuilder().startObject().startObject("provider").startObject("properties") .startObject("comments") .field("type", "nested") .startObject("properties") .startObject("cid").field("type", "long").endObject() .startObject("identifier").field("type", "keyword").endObject() .startObject("tags") .field("type", "nested") .startObject("properties") .startObject("tid").field("type", "long").endObject() .startObject("name").field("type", "keyword").endObject() .endObject() .endObject() .endObject() .endObject() .startObject("dates") .field("type", "object") .startObject("properties") .startObject("day").field("type", "date").field("format", "dateOptionalTime").endObject() .startObject("month") .field("type", "object") .startObject("properties") .startObject("end").field("type", "date").field("format", "dateOptionalTime").endObject() .startObject("start").field("type", "date").field("format", "dateOptionalTime").endObject() .startObject("label").field("type", "keyword").endObject() .endObject() .endObject() .endObject() .endObject() .endObject().endObject().endObject(); assertAcked(prepareCreate("idx2") .setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)) .addMapping("provider", mapping)); ensureGreen("idx2"); List<IndexRequestBuilder> indexRequests = new ArrayList<>(2); indexRequests.add(client().prepareIndex("idx2", "provider", "1").setSource("{\"dates\": {\"month\": {\"label\": \"2014-11\", \"end\": \"2014-11-30\", \"start\": \"2014-11-01\"}, \"day\": \"2014-11-30\"}, \"comments\": [{\"cid\": 3,\"identifier\": \"29111\"}, {\"cid\": 4,\"tags\": [{\"tid\" :44,\"name\": \"Roles\"}], \"identifier\": \"29101\"}]}", XContentType.JSON)); indexRequests.add(client().prepareIndex("idx2", "provider", "2").setSource("{\"dates\": {\"month\": {\"label\": \"2014-12\", \"end\": \"2014-12-31\", \"start\": \"2014-12-01\"}, \"day\": \"2014-12-03\"}, \"comments\": [{\"cid\": 1, \"identifier\": \"29111\"}, {\"cid\": 2,\"tags\": [{\"tid\" : 22, \"name\": \"DataChannels\"}], \"identifier\": \"29101\"}]}", XContentType.JSON)); indexRandom(true, indexRequests); SearchResponse response = client().prepareSearch("idx2").setTypes("provider") .addAggregation( terms("startDate").field("dates.month.start").subAggregation( terms("endDate").field("dates.month.end").subAggregation( terms("period").field("dates.month.label").subAggregation( nested("ctxt_idfier_nested", "comments") .subAggregation(filter("comment_filter", termQuery("comments.identifier", "29111")) .subAggregation(nested("nested_tags", "comments.tags") .subAggregation( terms("tag").field("comments.tags.name") ) ) ) ) ) ) ).get(); assertNoFailures(response); assertHitCount(response, 2); Terms startDate = response.getAggregations().get("startDate"); assertThat(startDate.getBuckets().size(), equalTo(2)); Terms.Bucket bucket = startDate.getBucketByKey("2014-11-01T00:00:00.000Z"); assertThat(bucket.getDocCount(), equalTo(1L)); Terms endDate = bucket.getAggregations().get("endDate"); bucket = endDate.getBucketByKey("2014-11-30T00:00:00.000Z"); assertThat(bucket.getDocCount(), equalTo(1L)); Terms period = bucket.getAggregations().get("period"); bucket = period.getBucketByKey("2014-11"); assertThat(bucket.getDocCount(), equalTo(1L)); Nested comments = bucket.getAggregations().get("ctxt_idfier_nested"); assertThat(comments.getDocCount(), equalTo(2L)); Filter filter = comments.getAggregations().get("comment_filter"); assertThat(filter.getDocCount(), equalTo(1L)); Nested nestedTags = filter.getAggregations().get("nested_tags"); assertThat(nestedTags.getDocCount(), equalTo(0L)); // This must be 0 Terms tags = nestedTags.getAggregations().get("tag"); assertThat(tags.getBuckets().size(), equalTo(0)); // and this must be empty bucket = startDate.getBucketByKey("2014-12-01T00:00:00.000Z"); assertThat(bucket.getDocCount(), equalTo(1L)); endDate = bucket.getAggregations().get("endDate"); bucket = endDate.getBucketByKey("2014-12-31T00:00:00.000Z"); assertThat(bucket.getDocCount(), equalTo(1L)); period = bucket.getAggregations().get("period"); bucket = period.getBucketByKey("2014-12"); assertThat(bucket.getDocCount(), equalTo(1L)); comments = bucket.getAggregations().get("ctxt_idfier_nested"); assertThat(comments.getDocCount(), equalTo(2L)); filter = comments.getAggregations().get("comment_filter"); assertThat(filter.getDocCount(), equalTo(1L)); nestedTags = filter.getAggregations().get("nested_tags"); assertThat(nestedTags.getDocCount(), equalTo(0L)); // This must be 0 tags = nestedTags.getAggregations().get("tag"); assertThat(tags.getBuckets().size(), equalTo(0)); // and this must be empty } public void testNestedSameDocIdProcessedMultipleTime() throws Exception { assertAcked( prepareCreate("idx4") .setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)) .addMapping("product", "categories", "type=keyword", "name", "type=text", "property", "type=nested") ); ensureGreen("idx4"); client().prepareIndex("idx4", "product", "1").setSource(jsonBuilder().startObject() .field("name", "product1") .array("categories", "1", "2", "3", "4") .startArray("property") .startObject().field("id", 1).endObject() .startObject().field("id", 2).endObject() .startObject().field("id", 3).endObject() .endArray() .endObject()).get(); client().prepareIndex("idx4", "product", "2").setSource(jsonBuilder().startObject() .field("name", "product2") .array("categories", "1", "2") .startArray("property") .startObject().field("id", 1).endObject() .startObject().field("id", 5).endObject() .startObject().field("id", 4).endObject() .endArray() .endObject()).get(); refresh(); SearchResponse response = client().prepareSearch("idx4").setTypes("product") .addAggregation(terms("category").field("categories").subAggregation( nested("property", "property").subAggregation( terms("property_id").field("property.id") ) )) .get(); assertNoFailures(response); assertHitCount(response, 2); Terms category = response.getAggregations().get("category"); assertThat(category.getBuckets().size(), equalTo(4)); Terms.Bucket bucket = category.getBucketByKey("1"); assertThat(bucket.getDocCount(), equalTo(2L)); Nested property = bucket.getAggregations().get("property"); assertThat(property.getDocCount(), equalTo(6L)); Terms propertyId = property.getAggregations().get("property_id"); assertThat(propertyId.getBuckets().size(), equalTo(5)); assertThat(propertyId.getBucketByKey("1").getDocCount(), equalTo(2L)); assertThat(propertyId.getBucketByKey("2").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("3").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("4").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("5").getDocCount(), equalTo(1L)); bucket = category.getBucketByKey("2"); assertThat(bucket.getDocCount(), equalTo(2L)); property = bucket.getAggregations().get("property"); assertThat(property.getDocCount(), equalTo(6L)); propertyId = property.getAggregations().get("property_id"); assertThat(propertyId.getBuckets().size(), equalTo(5)); assertThat(propertyId.getBucketByKey("1").getDocCount(), equalTo(2L)); assertThat(propertyId.getBucketByKey("2").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("3").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("4").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("5").getDocCount(), equalTo(1L)); bucket = category.getBucketByKey("3"); assertThat(bucket.getDocCount(), equalTo(1L)); property = bucket.getAggregations().get("property"); assertThat(property.getDocCount(), equalTo(3L)); propertyId = property.getAggregations().get("property_id"); assertThat(propertyId.getBuckets().size(), equalTo(3)); assertThat(propertyId.getBucketByKey("1").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("2").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("3").getDocCount(), equalTo(1L)); bucket = category.getBucketByKey("4"); assertThat(bucket.getDocCount(), equalTo(1L)); property = bucket.getAggregations().get("property"); assertThat(property.getDocCount(), equalTo(3L)); propertyId = property.getAggregations().get("property_id"); assertThat(propertyId.getBuckets().size(), equalTo(3)); assertThat(propertyId.getBucketByKey("1").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("2").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("3").getDocCount(), equalTo(1L)); } public void testFilterAggInsideNestedAgg() throws Exception { assertAcked(prepareCreate("classes") .addMapping("class", jsonBuilder().startObject().startObject("class").startObject("properties") .startObject("name").field("type", "text").endObject() .startObject("methods") .field("type", "nested") .startObject("properties") .startObject("name").field("type", "text").endObject() .startObject("return_type").field("type", "keyword").endObject() .startObject("parameters") .field("type", "nested") .startObject("properties") .startObject("name").field("type", "text").endObject() .startObject("type").field("type", "keyword").endObject() .endObject() .endObject() .endObject() .endObject().endObject().endObject().endObject())); client().prepareIndex("classes", "class", "1").setSource(jsonBuilder().startObject() .field("name", "QueryBuilder") .startArray("methods") .startObject() .field("name", "toQuery") .field("return_type", "Query") .startArray("parameters") .startObject() .field("name", "context") .field("type", "QueryShardContext") .endObject() .endArray() .endObject() .startObject() .field("name", "queryName") .field("return_type", "QueryBuilder") .startArray("parameters") .startObject() .field("name", "queryName") .field("type", "String") .endObject() .endArray() .endObject() .startObject() .field("name", "boost") .field("return_type", "QueryBuilder") .startArray("parameters") .startObject() .field("name", "boost") .field("type", "float") .endObject() .endArray() .endObject() .endArray() .endObject()).get(); client().prepareIndex("classes", "class", "2").setSource(jsonBuilder().startObject() .field("name", "Document") .startArray("methods") .startObject() .field("name", "add") .field("return_type", "void") .startArray("parameters") .startObject() .field("name", "field") .field("type", "IndexableField") .endObject() .endArray() .endObject() .startObject() .field("name", "removeField") .field("return_type", "void") .startArray("parameters") .startObject() .field("name", "name") .field("type", "String") .endObject() .endArray() .endObject() .startObject() .field("name", "removeFields") .field("return_type", "void") .startArray("parameters") .startObject() .field("name", "name") .field("type", "String") .endObject() .endArray() .endObject() .endArray() .endObject()).get(); refresh(); SearchResponse response = client().prepareSearch("classes").addAggregation(nested("to_method", "methods") .subAggregation(filter("num_string_params", nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None))) ).get(); Nested toMethods = response.getAggregations().get("to_method"); Filter numStringParams = toMethods.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(3L)); response = client().prepareSearch("classes").addAggregation(nested("to_method", "methods") .subAggregation(terms("return_type").field("methods.return_type").subAggregation( filter("num_string_params", nestedQuery("methods.parameters", termQuery("methods.parameters.type", "String"), ScoreMode.None)) ) )).get(); toMethods = response.getAggregations().get("to_method"); Terms terms = toMethods.getAggregations().get("return_type"); Bucket bucket = terms.getBucketByKey("void"); assertThat(bucket.getDocCount(), equalTo(3L)); numStringParams = bucket.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(2L)); bucket = terms.getBucketByKey("QueryBuilder"); assertThat(bucket.getDocCount(), equalTo(2L)); numStringParams = bucket.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(1L)); bucket = terms.getBucketByKey("Query"); assertThat(bucket.getDocCount(), equalTo(1L)); numStringParams = bucket.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(0L)); } }