Skip to content

Commit cb451ed

Browse files
authored
Allow nested fields in the composite aggregation (#37178)
This changes adds the support to handle `nested` fields in the `composite` aggregation. A `nested` aggregation can be used as parent of a `composite` aggregation in order to target `nested` fields in the `sources`. Closes #28611
1 parent 9e350d0 commit cb451ed

File tree

4 files changed

+112
-9
lines changed

4 files changed

+112
-9
lines changed

docs/reference/aggregations/bucket/composite-aggregation.asciidoc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,24 @@ PUT /sales
3131
},
3232
"shop": {
3333
"type": "keyword"
34-
}
34+
},
35+
"nested": {
36+
"type": "nested",
37+
"properties": {
38+
"product": {
39+
"type": "keyword"
40+
},
41+
"timestamp": {
42+
"type": "date"
43+
},
44+
"price": {
45+
"type": "long"
46+
},
47+
"shop": {
48+
"type": "keyword"
49+
}
50+
}
51+
}
3552
}
3653
}
3754
}
@@ -287,7 +304,6 @@ GET /_search
287304
--------------------------------------------------
288305
// CONSOLE
289306

290-
291307
This will create composite buckets from the values created by two values source, a `date_histogram` and a `terms`.
292308
Each bucket is composed of two values, one for each value source defined in the aggregation.
293309
Any type of combinations is allowed and the order in the array is preserved

rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@ setup:
1313
type: keyword
1414
long:
1515
type: long
16+
nested:
17+
type: nested
18+
properties:
19+
nested_long:
20+
type: long
1621

1722
- do:
1823
index:
1924
index: test
2025
type: doc
2126
id: 1
22-
body: { "keyword": "foo", "long": [10, 20] }
27+
body: { "keyword": "foo", "long": [10, 20], "nested": [{"nested_long": 10}, {"nested_long": 20}] }
2328

2429
- do:
2530
index:
@@ -33,14 +38,14 @@ setup:
3338
index: test
3439
type: doc
3540
id: 3
36-
body: { "keyword": "bar", "long": [100, 0] }
41+
body: { "keyword": "bar", "long": [100, 0], "nested": [{"nested_long": 10}, {"nested_long": 0}] }
3742

3843
- do:
3944
index:
4045
index: test
4146
type: doc
4247
id: 4
43-
body: { "keyword": "bar", "long": [1000, 0] }
48+
body: { "keyword": "bar", "long": [1000, 0], "nested": [{"nested_long": 1000}, {"nested_long": 20}] }
4449

4550
- do:
4651
index:
@@ -66,7 +71,6 @@ setup:
6671
version: " - 6.0.99"
6772
reason: this uses a new API that has been added in 6.1
6873

69-
7074
- do:
7175
search:
7276
rest_total_hits_as_int: true
@@ -357,3 +361,68 @@ setup:
357361
}
358362
}
359363
]
364+
365+
---
366+
"Composite aggregation with nested parent":
367+
- skip:
368+
version: " - 6.99.99"
369+
reason: the ability to set a nested parent aggregation was added in 7.0.
370+
371+
- do:
372+
search:
373+
rest_total_hits_as_int: true
374+
index: test
375+
body:
376+
aggregations:
377+
1:
378+
nested:
379+
path: nested
380+
aggs:
381+
2:
382+
composite:
383+
sources: [
384+
"nested": {
385+
"terms": {
386+
"field": "nested.nested_long"
387+
}
388+
}
389+
]
390+
391+
- match: {hits.total: 6}
392+
- length: { aggregations.1.2.buckets: 4 }
393+
- match: { aggregations.1.2.buckets.0.key.nested: 0 }
394+
- match: { aggregations.1.2.buckets.0.doc_count: 1 }
395+
- match: { aggregations.1.2.buckets.1.key.nested: 10 }
396+
- match: { aggregations.1.2.buckets.1.doc_count: 2 }
397+
- match: { aggregations.1.2.buckets.2.key.nested: 20 }
398+
- match: { aggregations.1.2.buckets.2.doc_count: 2 }
399+
- match: { aggregations.1.2.buckets.3.key.nested: 1000 }
400+
- match: { aggregations.1.2.buckets.3.doc_count: 1 }
401+
402+
- do:
403+
search:
404+
rest_total_hits_as_int: true
405+
index: test
406+
body:
407+
aggregations:
408+
1:
409+
nested:
410+
path: nested
411+
aggs:
412+
2:
413+
composite:
414+
after: { "nested": 10 }
415+
sources: [
416+
"nested": {
417+
"terms": {
418+
"field": "nested.nested_long"
419+
}
420+
}
421+
]
422+
423+
- match: {hits.total: 6}
424+
- length: { aggregations.1.2.buckets: 2 }
425+
- match: { aggregations.1.2.buckets.0.key.nested: 20 }
426+
- match: { aggregations.1.2.buckets.0.doc_count: 2 }
427+
- match: { aggregations.1.2.buckets.1.key.nested: 1000 }
428+
- match: { aggregations.1.2.buckets.1.doc_count: 1 }

server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregationBuilder.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.search.aggregations.AggregationBuilder;
3030
import org.elasticsearch.search.aggregations.AggregatorFactories;
3131
import org.elasticsearch.search.aggregations.AggregatorFactory;
32+
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregatorFactory;
3233
import org.elasticsearch.search.internal.SearchContext;
3334

3435
import java.io.IOException;
@@ -151,11 +152,28 @@ public int size() {
151152
return size;
152153
}
153154

155+
/**
156+
* Returns null if the provided factory and his parents are compatible with
157+
* this aggregator or the instance of the parent's factory that is incompatible with
158+
* the composite aggregation.
159+
*/
160+
private AggregatorFactory<?> checkParentIsNullOrNested(AggregatorFactory<?> factory) {
161+
if (factory == null) {
162+
return null;
163+
} else if (factory instanceof NestedAggregatorFactory) {
164+
return checkParentIsNullOrNested(factory.getParent());
165+
} else {
166+
return factory;
167+
}
168+
}
169+
154170
@Override
155171
protected AggregatorFactory<?> doBuild(SearchContext context, AggregatorFactory<?> parent,
156172
AggregatorFactories.Builder subfactoriesBuilder) throws IOException {
157-
if (parent != null) {
158-
throw new IllegalArgumentException("[composite] aggregation cannot be used with a parent aggregation");
173+
AggregatorFactory<?> invalid = checkParentIsNullOrNested(parent);
174+
if (invalid != null) {
175+
throw new IllegalArgumentException("[composite] aggregation cannot be used with a parent aggregation of" +
176+
" type: [" + invalid.getClass().getSimpleName() + "]");
159177
}
160178
CompositeValuesSourceConfig[] configs = new CompositeValuesSourceConfig[sources.size()];
161179
for (int i = 0; i < configs.length; i++) {

server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import java.util.List;
3333
import java.util.Map;
3434

35-
class NestedAggregatorFactory extends AggregatorFactory<NestedAggregatorFactory> {
35+
public class NestedAggregatorFactory extends AggregatorFactory<NestedAggregatorFactory> {
3636

3737
private final ObjectMapper parentObjectMapper;
3838
private final ObjectMapper childObjectMapper;

0 commit comments

Comments
 (0)