From 1106d918ba7cea30b174d92298978445079d2814 Mon Sep 17 00:00:00 2001 From: Michael Cheung Date: Thu, 31 Aug 2017 20:26:18 +0800 Subject: [PATCH 1/3] + Add nested and child aggregation in visualization --- .../agg_types/buckets/_bucket_agg_type.js | 24 ++++++++ .../agg_types/controls/nested_and_child.html | 33 +++++++++++ src/ui/public/vis/agg_configs.js | 57 ++++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 src/ui/public/agg_types/controls/nested_and_child.html diff --git a/src/ui/public/agg_types/buckets/_bucket_agg_type.js b/src/ui/public/agg_types/buckets/_bucket_agg_type.js index 44762ec86d876..4c354fa42ae94 100644 --- a/src/ui/public/agg_types/buckets/_bucket_agg_type.js +++ b/src/ui/public/agg_types/buckets/_bucket_agg_type.js @@ -1,11 +1,35 @@ import _ from 'lodash'; import { AggTypesAggTypeProvider } from 'ui/agg_types/agg_type'; +import nestedAndChildTemplate from 'ui/agg_types/controls/nested_and_child.html'; export function AggTypesBucketsBucketAggTypeProvider(Private) { const AggType = Private(AggTypesAggTypeProvider); _.class(BucketAggType).inherits(AggType); function BucketAggType(config) { + // always append reversed nested, child, nested + config.params.push( + { + name: 'reversedNested', + default: false, + write: _.noop + }, + { + name: 'child', + default: '', + write: _.noop + }, + { + name: 'nested', + default: 0, + write: _.noop + }, + { + name: 'aggNestedAndChild', + editor: nestedAndChildTemplate + } + ); + BucketAggType.Super.call(this, config); if (_.isFunction(config.getKey)) { diff --git a/src/ui/public/agg_types/controls/nested_and_child.html b/src/ui/public/agg_types/controls/nested_and_child.html new file mode 100644 index 0000000000000..241917245bbea --- /dev/null +++ b/src/ui/public/agg_types/controls/nested_and_child.html @@ -0,0 +1,33 @@ +
+ +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ diff --git a/src/ui/public/vis/agg_configs.js b/src/ui/public/vis/agg_configs.js index dac84c36ae142..0de63b559af1f 100644 --- a/src/ui/public/vis/agg_configs.js +++ b/src/ui/public/vis/agg_configs.js @@ -94,6 +94,8 @@ export function VisAggConfigsProvider(Private) { const dslTopLvl = {}; let dslLvlCursor; let nestedMetrics; + const subAggPrefix = 'agg_'; + let prevSubAggCnt; // count of additional aggregation if (this.vis.isHierarchical()) { // collect all metrics, and filter out the ones that we won't be copying @@ -119,7 +121,8 @@ export function VisAggConfigsProvider(Private) { dslLvlCursor = dslTopLvl; } else { const prevConfig = list[i - 1]; - const prevDsl = dslLvlCursor[prevConfig.id]; + const prevConfigKey = subAggPrefix.repeat(prevSubAggCnt) + prevConfig.id; // previous config key: prefix * n + id + const prevDsl = dslLvlCursor[prevConfigKey]; // advance the cursor and nest under the previous agg, or // put it on the same level if the previous agg doesn't accept @@ -127,11 +130,61 @@ export function VisAggConfigsProvider(Private) { dslLvlCursor = prevDsl.aggs || dslLvlCursor; } - const dsl = dslLvlCursor[config.id] = config.toDsl(); + const dsl = config.toDsl(); let subAggs; parseParentAggs(dslLvlCursor, dsl); + let newConfigId = config.id; + let newDsl = dsl; // define new dsl for additional aggregation + let aggObj = {}; // define aggObj to store orginal dsl + prevSubAggCnt = 0; + if (config.params.reversedNested) { + aggObj[newConfigId] = newDsl; + newDsl = { + reverse_nested: {}, + aggs: aggObj + }; + newConfigId = subAggPrefix + newConfigId; + prevSubAggCnt++; + } + + if (config.params.child) { + const childArr = config.params.child.split(',').map((term) => term.trim()); + const childCnt = childArr.length; + for (let i = 0; i < childCnt; ++i) { + aggObj = {}; + aggObj[newConfigId] = newDsl; + newDsl = { + children: { + type: childArr[childCnt - i - 1] + }, + aggs: aggObj + }; + newConfigId = subAggPrefix + newConfigId; + prevSubAggCnt++; + } + } + + if (config.params.nested) { + const nestedArr = config.params.field.name.split('.'); + const nestedLvl = (nestedArr.length - 1 < config.params.nested) ? nestedArr.length - 1 : config.params.nested; + for (let i = 0; i < nestedLvl; ++i) { + aggObj = {}; + aggObj[newConfigId] = newDsl; + newDsl = { + nested: { + path: nestedArr[i] + }, + aggs: aggObj + }; + newConfigId = subAggPrefix + newConfigId; + prevSubAggCnt++; + } + } + + dslLvlCursor[newConfigId] = newDsl; + if (config.schema.group === 'buckets' && i < list.length - 1) { // buckets that are not the last item in the list accept sub-aggs subAggs = dsl.aggs || (dsl.aggs = {}); From 69890bfa7d1fa688c4ba87c76cfabb1ef161ae50 Mon Sep 17 00:00:00 2001 From: Michael Cheung Date: Fri, 1 Sep 2017 17:35:27 +0800 Subject: [PATCH 2/3] + Add child UI support --- .../agg_types/buckets/_bucket_agg_type.js | 2 +- .../agg_types/controls/nested_and_child.html | 50 ++++++++++++++++--- src/ui/public/vis/agg_configs.js | 8 ++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/ui/public/agg_types/buckets/_bucket_agg_type.js b/src/ui/public/agg_types/buckets/_bucket_agg_type.js index 4c354fa42ae94..00f572854c799 100644 --- a/src/ui/public/agg_types/buckets/_bucket_agg_type.js +++ b/src/ui/public/agg_types/buckets/_bucket_agg_type.js @@ -16,7 +16,7 @@ export function AggTypesBucketsBucketAggTypeProvider(Private) { }, { name: 'child', - default: '', + default: [ { input: '' } ], write: _.noop }, { diff --git a/src/ui/public/agg_types/controls/nested_and_child.html b/src/ui/public/agg_types/controls/nested_and_child.html index 241917245bbea..1dab046d5a454 100644 --- a/src/ui/public/agg_types/controls/nested_and_child.html +++ b/src/ui/public/agg_types/controls/nested_and_child.html @@ -7,17 +7,51 @@ -
-
- - +
+
+
+ + +
+ +
+
+ +
+ +
+
+ +
+

+ + Required: You must specify at least one child. +

+
+ + +
diff --git a/src/ui/public/vis/agg_configs.js b/src/ui/public/vis/agg_configs.js index 0de63b559af1f..7ad6d5c9ece4e 100644 --- a/src/ui/public/vis/agg_configs.js +++ b/src/ui/public/vis/agg_configs.js @@ -150,14 +150,18 @@ export function VisAggConfigsProvider(Private) { } if (config.params.child) { - const childArr = config.params.child.split(',').map((term) => term.trim()); + const childArr = config.params.child; const childCnt = childArr.length; for (let i = 0; i < childCnt; ++i) { + const childInput = childArr[childCnt - i - 1].input; + if (!childInput) { + break; + } aggObj = {}; aggObj[newConfigId] = newDsl; newDsl = { children: { - type: childArr[childCnt - i - 1] + type: childInput }, aggs: aggObj }; From 0285990545d15ba654c1c6160b6babef2efd309b Mon Sep 17 00:00:00 2001 From: Michael Cheung Date: Wed, 20 Sep 2017 21:42:18 +0800 Subject: [PATCH 3/3] ! Fix reversed nested and nested aggregation's order ! Fix tabify visualize result --- src/ui/public/agg_response/tabify/tabify.js | 28 +++++++++- src/ui/public/vis/agg_configs.js | 62 ++++++++++++--------- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/ui/public/agg_response/tabify/tabify.js b/src/ui/public/agg_response/tabify/tabify.js index d2d907a422e7c..f146a89da8068 100644 --- a/src/ui/public/agg_response/tabify/tabify.js +++ b/src/ui/public/agg_response/tabify/tabify.js @@ -30,10 +30,36 @@ export function AggResponseTabifyProvider(Private, Notifier) { */ function collectBucket(write, bucket, key) { const agg = write.aggStack.shift(); + const subAggPrefix = 'agg_'; switch (agg.schema.group) { case 'buckets': - const buckets = new Buckets(bucket[agg.id]); + let buckets = new Buckets(bucket[agg.id]); + + // traverse the agg object + const bucketKeys = Object.keys(bucket); + const bucketLength = bucketKeys.length; + const subAggRegex = new RegExp('(' + subAggPrefix + ')*' + agg.id, 'g'); + let subAggCnt = 0; + for (let i = 0; i < bucketLength; ++i) { + if (bucketKeys[i].match(subAggRegex)) { + const subAggPrefixRegex = new RegExp(subAggPrefix, 'g'); + // get number of sub agg + subAggCnt = (bucketKeys[i].match(subAggPrefixRegex) || []).length; + break; + } + } + let aggBucket = bucket; + while (subAggCnt > 0) { + const prefix = subAggPrefix.repeat(subAggCnt) + agg.id; + aggBucket = aggBucket[prefix]; + subAggCnt--; + } + // get the deepest agg result + if (aggBucket !== undefined) { + buckets = new Buckets(aggBucket[agg.id]); + } + if (buckets.length) { const splitting = write.canSplit && agg.schema.name === 'split'; if (splitting) { diff --git a/src/ui/public/vis/agg_configs.js b/src/ui/public/vis/agg_configs.js index 7ad6d5c9ece4e..dd89fce9835de 100644 --- a/src/ui/public/vis/agg_configs.js +++ b/src/ui/public/vis/agg_configs.js @@ -121,8 +121,19 @@ export function VisAggConfigsProvider(Private) { dslLvlCursor = dslTopLvl; } else { const prevConfig = list[i - 1]; - const prevConfigKey = subAggPrefix.repeat(prevSubAggCnt) + prevConfig.id; // previous config key: prefix * n + id - const prevDsl = dslLvlCursor[prevConfigKey]; + let prevConfigKey; + let prevDsl; + if (prevSubAggCnt > 0) { + prevDsl = dslLvlCursor; + for (let i = prevSubAggCnt; i > 0; --i) { + prevConfigKey = subAggPrefix.repeat(i) + prevConfig.id; // previous config key: prefix * n + id + prevDsl = prevDsl[prevConfigKey].aggs; + } + prevDsl = prevDsl[prevConfig.id]; + } + else{ + prevDsl = dslLvlCursor[prevConfig.id]; + } // advance the cursor and nest under the previous agg, or // put it on the same level if the previous agg doesn't accept @@ -139,14 +150,21 @@ export function VisAggConfigsProvider(Private) { let newDsl = dsl; // define new dsl for additional aggregation let aggObj = {}; // define aggObj to store orginal dsl prevSubAggCnt = 0; - if (config.params.reversedNested) { - aggObj[newConfigId] = newDsl; - newDsl = { - reverse_nested: {}, - aggs: aggObj - }; - newConfigId = subAggPrefix + newConfigId; - prevSubAggCnt++; + if (config.params.nested) { + const nestedArr = config.params.field.name.split('.'); + const nestedLvl = (nestedArr.length - 1 < config.params.nested) ? nestedArr.length - 1 : config.params.nested; + for (let i = 0; i < nestedLvl; ++i) { + aggObj = {}; + aggObj[newConfigId] = newDsl; + newDsl = { + nested: { + path: nestedArr[i] + }, + aggs: aggObj + }; + newConfigId = subAggPrefix + newConfigId; + prevSubAggCnt++; + } } if (config.params.child) { @@ -170,21 +188,15 @@ export function VisAggConfigsProvider(Private) { } } - if (config.params.nested) { - const nestedArr = config.params.field.name.split('.'); - const nestedLvl = (nestedArr.length - 1 < config.params.nested) ? nestedArr.length - 1 : config.params.nested; - for (let i = 0; i < nestedLvl; ++i) { - aggObj = {}; - aggObj[newConfigId] = newDsl; - newDsl = { - nested: { - path: nestedArr[i] - }, - aggs: aggObj - }; - newConfigId = subAggPrefix + newConfigId; - prevSubAggCnt++; - } + if (config.params.reversedNested) { + aggObj = {}; + aggObj[newConfigId] = newDsl; + newDsl = { + reverse_nested: {}, + aggs: aggObj + }; + newConfigId = subAggPrefix + newConfigId; + prevSubAggCnt++; } dslLvlCursor[newConfigId] = newDsl;