Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

+ Add nested and child aggregation in visualization #1

Open
wants to merge 3 commits into
base: 5.5.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion src/ui/public/agg_response/tabify/tabify.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
24 changes: 24 additions & 0 deletions src/ui/public/agg_types/buckets/_bucket_agg_type.js
Original file line number Diff line number Diff line change
@@ -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: [ { input: '' } ],
write: _.noop
},
{
name: 'nested',
default: 0,
write: _.noop
},
{
name: 'aggNestedAndChild',
editor: nestedAndChildTemplate
}
);

BucketAggType.Super.call(this, config);

if (_.isFunction(config.getKey)) {
Expand Down
67 changes: 67 additions & 0 deletions src/ui/public/agg_types/controls/nested_and_child.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<div class="vis-option-item">
<label>
<input type="checkbox"
name="reversedNested"
ng-model="agg.params.reversedNested">
Reversed nested
</label>
</div>

<div class="form-group">
<div ng-repeat="child in agg.params.child">
<div class="vis-editor-agg-header">
<label>
Child {{$index + 1}}
<span ng-if="child.label">- {{ child.label }}</span>
</label>

<div class="kuiButtonGroup kuiButtonGroup--united">
<button
type="button"
ng-click="agg.params.child.splice($index, 1)"
class="kuiButton kuiButton--danger kuiButton--small">
<i class="fa fa-times"></i>
</button>
</div>
</div>

<div class="form-group">
<input
ng-model="child.input"
type="text"
class="form-control"
name="child{{$index}}">
</div>

</div>
</div>

<input ng-model="agg.params.child.length" name="childLength" required type="number" class="ng-hide" />
<div class="hintbox" ng-show="aggForm.childLength.$invalid">
<p>
<i class="fa fa-danger text-danger"></i>
<strong>Required:</strong> You must specify at least one child.
</p>
</div>

<button
click-focus="'child'+(agg.params.child.length-1)"
ng-click="agg.params.child.push({input:''})"
class="kuiButton kuiButton--primary kuiButton--fullWidth"
>
Add Child
</button>

<div class="vis-editor-agg-form-row">
<div class="form-group">
<label>Nested</label>
<input
name="nested"
ng-model="agg.params.nested"
class="form-control"
type="number"
min="0"
>
</div>
</div>

73 changes: 71 additions & 2 deletions src/ui/public/vis/agg_configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -119,19 +121,86 @@ export function VisAggConfigsProvider(Private) {
dslLvlCursor = dslTopLvl;
} else {
const prevConfig = list[i - 1];
const prevDsl = dslLvlCursor[prevConfig.id];
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
// sub aggs
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.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) {
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: childInput
},
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;

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 = {});
Expand Down