diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/230_composite.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/230_composite.yml index 303eff623527b..cb612343dd2cb 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/230_composite.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.aggregation/230_composite.yml @@ -14,11 +14,6 @@ setup: type: long geo_point: type: geo_point - nested: - type: nested - properties: - nested_long: - type: long - do: indices.create: @@ -30,11 +25,6 @@ setup: type: date long: type: long - nested: - type: nested - properties: - nested_long: - type: long - do: indices.create: @@ -59,23 +49,118 @@ setup: semester: type: keyword + - do: + indices.create: + index: nonesting + body: + mappings: + properties: + kw: + type: keyword + num: + type: integer + + - do: + index: + index: nonesting + id: 1 + body: { "kw": "one", "num": 1 } + + - do: + index: + index: nonesting + id: 2 + body: { "kw": "two", "num": 2 } + + - do: + index: + index: nonesting + id: 3 + body: { "kw": "three", "num": 3 } + - do: index: index: verynested id: 1 - body: { "department": "compsci", "staff": 12, "courses": [ { "name": "Object Oriented Programming", "credits": 3, "sessions": [ { "semester": "spr2021", "students": 37 }, { "semester": "fall2020", "students": 45} ] }, { "name": "Theory of Computation", "credits": 4, "sessions": [ { "semester": "spr2021", "students": 19 }, { "semester": "fall2020", "students": 14 } ] } ] } + body: { + "department": "compsci", + "staff": 12, + "courses": [ + { + "name": "Object Oriented Programming", + "credits": 3, + "sessions": [ + { + "semester": "spr2021", + "students": 37 + }, + { + "semester": "fall2020", + "students": 45 + } + ] + }, + { + "name": "Theory of Computation", + "credits": 4, + "sessions": [ + { + "semester": "spr2021", + "students": 19 + }, + { + "semester": "fall2020", + "students": 14 + } + ] + } + ] + } - do: index: index: verynested id: 2 - body: { "department": "math", "staff": 20, "courses": [ { "name": "Precalculus", "credits": 1, "sessions": [ { "semester": "spr2021", "students": 100 }, { "semester": "fall2020", "students": 134 } ] }, { "name": "Linear Algebra", "credits": 3, "sessions": [ { "semester": "spr2021", "students": 29 }, { "semester": "fall2020", "students": 23 } ] } ] } + body: { + "department": "math", + "staff": 20, + "courses": [ + { + "name": "Precalculus", + "credits": 1, + "sessions": [ + { + "semester": "spr2021", + "students": 100 + }, + { + "semester": "fall2020", + "students": 134 + } + ] + }, + { + "name": "Linear Algebra", + "credits": 3, + "sessions": [ + { + "semester": "spr2021", + "students": 29 + }, + { + "semester": "fall2020", + "students": 23 + } + ] + } + ] + } - do: index: index: test id: 1 - body: { "keyword": "foo", "long": [10, 20], "geo_point": "37.2343,-115.8067", "nested": [{"nested_long": 10}, {"nested_long": 20}] } + body: { "keyword": "foo", "long": [10, 20], "geo_point": "37.2343,-115.8067"} - do: index: @@ -87,13 +172,13 @@ setup: index: index: test id: 3 - body: { "keyword": "bar", "long": [100, 0], "geo_point": "90.0,0.0", "nested": [{"nested_long": 10}, {"nested_long": 0}] } + body: { "keyword": "bar", "long": [100, 0], "geo_point": "90.0,0.0"} - do: index: index: test id: 4 - body: { "keyword": "bar", "long": [1000, 0], "geo_point": "41.12,-71.34", "nested": [{"nested_long": 1000}, {"nested_long": 20}] } + body: { "keyword": "bar", "long": [1000, 0], "geo_point": "41.12,-71.34"} - do: index: @@ -115,7 +200,7 @@ setup: - do: indices.refresh: - index: [test, other, verynested] + index: [test, other, verynested, nonesting] --- "Simple Composite aggregation": @@ -286,7 +371,7 @@ setup: terms: field: long aggs: - nested: + invalid_child: composite: sources: [ { @@ -514,71 +599,6 @@ setup: } ] ---- -"Composite aggregation with nested parent": - - skip: - version: " - 6.99.99" - reason: the ability to set a nested parent aggregation was added in 7.0. - - - do: - search: - rest_total_hits_as_int: true - index: test - body: - aggregations: - 1: - nested: - path: nested - aggs: - 2: - composite: - sources: [ - "nested": { - "terms": { - "field": "nested.nested_long" - } - } - ] - - - match: {hits.total: 6} - - length: { aggregations.1.2.buckets: 4 } - - match: { aggregations.1.2.buckets.0.key.nested: 0 } - - match: { aggregations.1.2.buckets.0.doc_count: 1 } - - match: { aggregations.1.2.buckets.1.key.nested: 10 } - - match: { aggregations.1.2.buckets.1.doc_count: 2 } - - match: { aggregations.1.2.buckets.2.key.nested: 20 } - - match: { aggregations.1.2.buckets.2.doc_count: 2 } - - match: { aggregations.1.2.buckets.3.key.nested: 1000 } - - match: { aggregations.1.2.buckets.3.doc_count: 1 } - - - do: - search: - rest_total_hits_as_int: true - index: test - body: - aggregations: - 1: - nested: - path: nested - aggs: - 2: - composite: - after: { "nested": 10 } - sources: [ - "nested": { - "terms": { - "field": "nested.nested_long" - } - } - ] - - - match: {hits.total: 6} - - length: { aggregations.1.2.buckets: 2 } - - match: { aggregations.1.2.buckets.0.key.nested: 20 } - - match: { aggregations.1.2.buckets.0.doc_count: 2 } - - match: { aggregations.1.2.buckets.1.key.nested: 1000 } - - match: { aggregations.1.2.buckets.1.doc_count: 1 } - --- "Composite aggregation with unmapped field": - skip: @@ -1068,6 +1088,9 @@ setup: --- "Nested as parent": + - skip: + version: " - 6.99.99" + reason: Nested supporte added in 7.0 - do: search: rest_total_hits_as_int: true @@ -1078,7 +1101,7 @@ setup: "nested": { "path": "courses" }, "aggregations": { "names": { - "composite": { + "composite": { "sources": [ "kw": {"terms": {"field": "courses.name"}} ] @@ -1102,6 +1125,9 @@ setup: --- "Nested parent with compound key": + - skip: + version: " - 6.99.99" + reason: Nested supporte added in 7.0 - do: search: rest_total_hits_as_int: true @@ -1131,6 +1157,9 @@ setup: --- "Nested with a nested sub aggregation": + - skip: + version: " - 6.99.99" + reason: Nested supporte added in 7.0 - do: search: rest_total_hits_as_int: true @@ -1163,3 +1192,78 @@ setup: - match: { aggregations.courses.sessions.names.buckets.0.doc_count: 4} - match: { aggregations.courses.sessions.names.buckets.1.key.kw: "spr2021" } - match: { aggregations.courses.sessions.names.buckets.1.doc_count: 4} + +--- +"Nested then filter then nested then terms": + - skip: + version: " - 7.12.99" + reason: Filter support added in 7.13 + - do: + search: + rest_total_hits_as_int: true + index: verynested + body: + "aggregations": { + "courses": { + "nested": { "path": "courses" }, + "aggregations": { + "highpass_filter": { + "filter": { "range": {"courses.credits": { "gt": 1 }}}, + "aggregations": { + "sessions": { + "nested": { "path": "courses.sessions" }, + "aggregations": { + "names": { + "composite": { + "sources": [ + "kw": {"terms": { "field": "courses.sessions.semester" }} + ] + } + } + } + } + } + } + } + } + } + - match: {hits.total: 2} + - match: {aggregations.courses.doc_count: 4} + - match: {aggregations.courses.highpass_filter.doc_count: 3} + - match: {aggregations.courses.highpass_filter.sessions.doc_count: 6} + - length: { aggregations.courses.highpass_filter.sessions.names.buckets: 2 } + - match: { aggregations.courses.highpass_filter.sessions.names.buckets.0.key.kw: "fall2020" } + - match: { aggregations.courses.highpass_filter.sessions.names.buckets.0.doc_count: 3} + - match: { aggregations.courses.highpass_filter.sessions.names.buckets.1.key.kw: "spr2021" } + - match: { aggregations.courses.highpass_filter.sessions.names.buckets.1.doc_count: 3} + +--- +"Filter without nesting": + - skip: + version: " - 7.12.99" + reason: Filter support added in 7.13 + - do: + search: + rest_total_hits_as_int: true + index: nonesting + body: { + "aggs": { + "not_one": { + "filter": { "range": {"num": {"gt": 1}} }, + "aggs": { + "keez": { + "composite": { + "sources": [ + "key": {"terms": {"field": "kw"}} + ] + } + } + } + } + } + } + - match: {hits.total: 3} + - match: {aggregations.not_one.doc_count: 2} + - length: {aggregations.not_one.keez.buckets: 2} + - match: {aggregations.not_one.keez.buckets.0.key.key: "three"} + - match: {aggregations.not_one.keez.buckets.1.key.key: "two"} diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java index addcd5fb0aa17..d47bae6dfba46 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java @@ -17,6 +17,7 @@ import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactory; +import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregatorFactory; import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregatorFactory; import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry; @@ -160,11 +161,11 @@ public BucketCardinality bucketCardinality() { * this aggregator or the instance of the parent's factory that is incompatible with * the composite aggregation. */ - private AggregatorFactory checkParentIsNullOrNested(AggregatorFactory factory) { + private AggregatorFactory validateParentAggregations(AggregatorFactory factory) { if (factory == null) { return null; - } else if (factory instanceof NestedAggregatorFactory) { - return checkParentIsNullOrNested(factory.getParent()); + } else if (factory instanceof NestedAggregatorFactory || factory instanceof FilterAggregatorFactory) { + return validateParentAggregations(factory.getParent()); } else { return factory; } @@ -195,7 +196,7 @@ private static void validateSources(List> source @Override protected AggregatorFactory doBuild(AggregationContext context, AggregatorFactory parent, AggregatorFactories.Builder subfactoriesBuilder) throws IOException { - AggregatorFactory invalid = checkParentIsNullOrNested(parent); + AggregatorFactory invalid = validateParentAggregations(parent); if (invalid != null) { throw new IllegalArgumentException("[composite] aggregation cannot be used with a parent aggregation of" + " type: [" + invalid.getClass().getSimpleName() + "]"); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java index 635b1000deca2..53360186cb58b 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java @@ -382,7 +382,7 @@ protected LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucket Sort indexSortPrefix = buildIndexSortPrefix(ctx); int sortPrefixLen = computeSortPrefixLen(indexSortPrefix); - SortedDocsProducer sortedDocsProducer = sortPrefixLen == 0 ? + SortedDocsProducer sortedDocsProducer = (sortPrefixLen == 0 && parent == null) ? sources[0].createSortedDocsProducerOrNull(ctx.reader(), topLevelQuery()) : null; if (sortedDocsProducer != null) { // Visit documents sorted by the leading source of the composite definition and terminates