Skip to content

Commit e653508

Browse files
committed
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 1650257 commit e653508

File tree

4 files changed

+117
-9
lines changed

4 files changed

+117
-9
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,24 @@ PUT /sales
3232
},
3333
"shop": {
3434
"type": "keyword"
35-
}
35+
},
36+
"nested": {
37+
"type": "nested",
38+
"properties": {
39+
"product": {
40+
"type": "keyword"
41+
},
42+
"timestamp": {
43+
"type": "date"
44+
},
45+
"price": {
46+
"type": "long"
47+
},
48+
"shop": {
49+
"type": "keyword"
50+
}
51+
}
52+
}
3653
}
3754
}
3855
}
@@ -289,7 +306,6 @@ GET /_search
289306
--------------------------------------------------
290307
// CONSOLE
291308

292-
293309
This will create composite buckets from the values created by two values source, a `date_histogram` and a `terms`.
294310
Each bucket is composed of two values, one for each value source defined in the aggregation.
295311
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: 71 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
index: test
@@ -348,3 +352,66 @@ setup:
348352
}
349353
}
350354
]
355+
356+
---
357+
"Composite aggregation with nested parent":
358+
- skip:
359+
version: " - 6.6.99"
360+
reason: the ability to set a nested parent aggregation was added in 6.7.
361+
362+
- do:
363+
search:
364+
index: test
365+
body:
366+
aggregations:
367+
1:
368+
nested:
369+
path: nested
370+
aggs:
371+
2:
372+
composite:
373+
sources: [
374+
"nested": {
375+
"terms": {
376+
"field": "nested.nested_long"
377+
}
378+
}
379+
]
380+
381+
- match: {hits.total: 6}
382+
- length: { aggregations.1.2.buckets: 4 }
383+
- match: { aggregations.1.2.buckets.0.key.nested: 0 }
384+
- match: { aggregations.1.2.buckets.0.doc_count: 1 }
385+
- match: { aggregations.1.2.buckets.1.key.nested: 10 }
386+
- match: { aggregations.1.2.buckets.1.doc_count: 2 }
387+
- match: { aggregations.1.2.buckets.2.key.nested: 20 }
388+
- match: { aggregations.1.2.buckets.2.doc_count: 2 }
389+
- match: { aggregations.1.2.buckets.3.key.nested: 1000 }
390+
- match: { aggregations.1.2.buckets.3.doc_count: 1 }
391+
392+
- do:
393+
search:
394+
index: test
395+
body:
396+
aggregations:
397+
1:
398+
nested:
399+
path: nested
400+
aggs:
401+
2:
402+
composite:
403+
after: { "nested": 10 }
404+
sources: [
405+
"nested": {
406+
"terms": {
407+
"field": "nested.nested_long"
408+
}
409+
}
410+
]
411+
412+
- match: {hits.total: 6}
413+
- length: { aggregations.1.2.buckets: 2 }
414+
- match: { aggregations.1.2.buckets.0.key.nested: 20 }
415+
- match: { aggregations.1.2.buckets.0.doc_count: 2 }
416+
- match: { aggregations.1.2.buckets.1.key.nested: 1000 }
417+
- match: { aggregations.1.2.buckets.1.doc_count: 1 }

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

Lines changed: 27 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;
@@ -144,11 +145,35 @@ public CompositeAggregationBuilder size(int size) {
144145
return this;
145146
}
146147

148+
/**
149+
* @return the number of composite buckets. Defaults to {@code 10}.
150+
*/
151+
public int size() {
152+
return size;
153+
}
154+
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+
147170
@Override
148171
protected AggregatorFactory<?> doBuild(SearchContext context, AggregatorFactory<?> parent,
149172
AggregatorFactories.Builder subfactoriesBuilder) throws IOException {
150-
if (parent != null) {
151-
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() + "]");
152177
}
153178
CompositeValuesSourceConfig[] configs = new CompositeValuesSourceConfig[sources.size()];
154179
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)