diff --git a/src/plugins/data/common/search/aggs/agg_config.test.ts b/src/plugins/data/common/search/aggs/agg_config.test.ts index bf6dfbd51b26a..6ea409e327d56 100644 --- a/src/plugins/data/common/search/aggs/agg_config.test.ts +++ b/src/plugins/data/common/search/aggs/agg_config.test.ts @@ -191,6 +191,85 @@ describe('AggConfig', () => { expect(dsl.aggs[medianConfig.id]).toHaveProperty('percentiles'); expect(dsl.aggs[medianConfig.id].percentiles).toBe(football); }); + + it('properly handles nested sibling pipeline aggregations', () => { + const customBucket = { + type: 'date_histogram', + params: { + field: '@timestamp', + interval: '1h', + }, + }; + const customMetric = { + type: 'avg_bucket', + params: { + customBucket: { + type: 'date_histogram', + params: { + field: '@timestamp', + interval: '30m', + }, + }, + customMetric: { + type: 'sum', + params: { + field: 'bytes', + }, + }, + }, + }; + const configStates = [ + { + type: 'avg_bucket', + params: { + customBucket, + customMetric, + }, + }, + ]; + const ac = new AggConfigs(indexPattern, configStates, { typesRegistry }, jest.fn()); + const dsl = ac.toDsl(); + + expect(dsl).toMatchInlineSnapshot(` + Object { + "1": Object { + "avg_bucket": Object { + "buckets_path": "1-bucket>1-metric", + }, + }, + "1-bucket": Object { + "aggs": Object { + "1-bucket": Object { + "aggs": Object { + "1-metric": Object { + "sum": Object { + "field": "bytes", + }, + }, + }, + "date_histogram": Object { + "field": "@timestamp", + "fixed_interval": "30m", + "min_doc_count": 1, + "time_zone": "dateFormat:tz", + }, + }, + "1-metric": Object { + "avg_bucket": Object { + "buckets_path": "1-bucket>1-metric", + }, + }, + }, + "date_histogram": Object { + "calendar_interval": "1h", + "field": "@timestamp", + "min_doc_count": 1, + "time_zone": "dateFormat:tz", + }, + }, + } + `); + }); }); describe('::ensureIds', () => { diff --git a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts index 752bacd7554e5..fb47e53362826 100644 --- a/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts +++ b/src/plugins/data/common/search/aggs/metrics/lib/sibling_pipeline_agg_writer.ts @@ -6,21 +6,25 @@ * Side Public License, v 1. */ +import { siblingPipelineType } from '../../../..'; import { IMetricAggConfig } from '../metric_agg_type'; import { METRIC_TYPES } from '../metric_agg_types'; export const siblingPipelineAggWriter = (agg: IMetricAggConfig, output: Record) => { - const customMetric = agg.getParam('customMetric'); - - if (!customMetric) return; - - const metricAgg = customMetric; + const metricAgg = agg.getParam('customMetric'); const bucketAgg = agg.getParam('customBucket'); + if (!metricAgg) return; // if a bucket is selected, we must add this agg as a sibling to it, and add a metric to that bucket (or select one of its) if (metricAgg.type.name !== METRIC_TYPES.COUNT) { bucketAgg.subAggs = (output.subAggs || []).concat(metricAgg); output.params.buckets_path = `${bucketAgg.id}>${metricAgg.id}`; + + // If the metric is another nested sibling pipeline agg, we need to include it as a sub-agg of this agg's bucket agg + if (metricAgg.type.subtype === siblingPipelineType) { + const subAgg = metricAgg.getParam('customBucket'); + if (subAgg) bucketAgg.subAggs.push(subAgg); + } } else { output.params.buckets_path = bucketAgg.id + '>_count'; }