[agg_response/tabify/response] expand response class support for splits

This commit is contained in:
Spencer Alger 2014-10-27 11:38:46 -07:00
parent bd068ef46b
commit 731377300b
2 changed files with 90 additions and 68 deletions

View file

@ -14,13 +14,12 @@ define(function (require) {
this.opts = opts || {};
this.rowBuffer = [];
this.splitIndex = {};
this.columns = getColumns(vis);
this.aggStack = _.pluck(this.columns, 'aggConfig');
this.canSplit = this.opts.canSplit !== false;
this.root = null;
this.stack = [];
this.root = new TableGroup();
this.stack = [this.root];
}
/**
@ -29,9 +28,11 @@ define(function (require) {
*
* @param {TableGroup} parent - the TableGroup that should contain this Table
*/
function Table() {
function Table(agg, key) {
this.rows = [];
this.columns = [];
this.columns = null; // written with the first row
this.aggConfig = agg;
this.key = key;
}
/**
@ -42,9 +43,9 @@ define(function (require) {
* @param {any} key - The key for the bucket that created this agg
* @param {TableGroup} [parent] - option TableGroup that owns this TableGroup
*/
function TableGroup(agg, key) {
function TableGroup(agg) {
this.tables = [];
this.aggConfig = agg;
this.key = key;
}
/**
@ -60,9 +61,9 @@ define(function (require) {
TabbedAggResponseWriter.prototype.table = function (group, parent, agg, key) {
var table;
if (group) {
table = new TableGroup(agg, key);
table = new TableGroup(agg);
} else {
table = new Table();
table = new Table(agg, key);
}
// if there hasn't been a root yet, this is it
@ -85,37 +86,46 @@ define(function (require) {
* walk into the remaining branches and end up writing some rows to the table.
*
* @param {aggConfig} agg - the aggConfig that created this split
* @param {any} key - the key for the bucket that is a result of the split
* @param {function} block - a function to execute in the context of the split table
* @param {Buckets} buckets - the buckets produces by the agg
* @param {function} block - a function to execute for each sub bucket
* @return {TableGroup} tableGroup - the table group created for the split
*/
TabbedAggResponseWriter.prototype.split = function (agg, key, block) {
var splitId = agg.id + ':' + key;
var tableGroup = this.splitIndex[splitId];
TabbedAggResponseWriter.prototype.split = function (agg, buckets, block) {
var self = this;
if (this.stack.length === 0) {
// we can't split nothing, we have to create a table group for our tableGroup
this.stack.unshift(this.table(true, void 0));
if (!self.canSplit) {
throw new Error('attempted to split when splitting is disabled');
}
if (!tableGroup) {
tableGroup = this.splitIndex[splitId] = this.table(true, this.stack[0], agg, key);
_.pull(this.columns, _.find(this.columns, function (col) {
return col.aggConfig === agg;
}));
}
var parent = self.stack[0];
var tableGroup = findTable(parent, agg) || self.table(true, self.stack[0], agg);
_.pull(self.columns, _.find(self.columns, function (col) {
return col.aggConfig === agg;
}));
this.stack.unshift(tableGroup);
if (_.isFunction(block)) block.call(tableGroup);
while (true) {
var prev = this.stack.shift();
if (prev === tableGroup) break;
if (!prev) throw new Error('TableGroup was removed from stack, unable to procede');
}
self.stack.unshift(tableGroup);
buckets.forEach(function (bucket, key) {
var table = findTable(tableGroup, agg, key);
if (!table) {
table = self.table(false, tableGroup, agg, key);
table.title = agg.makeLabel() + ': ' + key;
}
self.stack.unshift(table);
if (_.isFunction(block)) block.call(self, bucket, key);
self.stack.shift();
});
self.stack.shift();
return tableGroup;
};
function findTable(tableGroup, agg, key) {
return _.find(tableGroup.tables, function (table) {
return table.aggConfig === agg && table.key === key;
});
}
/**
* Push a value into the row, then run a block. Once the block is
* complete the value is pulled from the stack.
@ -126,7 +136,7 @@ define(function (require) {
*/
TabbedAggResponseWriter.prototype.cell = function (value, block) {
this.rowBuffer.push(value);
if (_.isFunction(block)) block.call(this, value);
if (_.isFunction(block)) block.call(this);
this.rowBuffer.pop(value);
return value;
@ -148,13 +158,12 @@ define(function (require) {
}
var table = this.stack[0];
if (!table || (table instanceof TableGroup)) {
if (!table || table === this.root) {
table = this.table(false, table);
Array.prototype.push.apply(table.columns, this.columns);
this.stack.unshift(table);
}
while (cells.length < table.columns.length) cells.push('');
while (cells.length < this.columns.length) cells.push('');
table.rows.push(cells);
};
@ -163,8 +172,25 @@ define(function (require) {
*
* @return {object} - the final table-tree
*/
TabbedAggResponseWriter.prototype.done = function () {
return this.root;
TabbedAggResponseWriter.prototype.response = function () {
var table = this.root.tables[0];
if (!table) return;
delete table.$parent;
var columns = this.columns;
// give the columns some metadata
columns.map(function (col) {
col.title = col.aggConfig.makeLabel();
});
// walk the tree and write the columns to each table
(function step(table) {
if (table.tables) table.tables.forEach(step);
else table.columns = columns.slice(0);
}(table));
return table;
};
return TabbedAggResponseWriter;

View file

@ -3,17 +3,18 @@ define(function (require) {
var _ = require('lodash');
var AggConfig = Private(require('components/vis/_agg_config'));
var TabbedAggResponseWriter = Private(require('components/agg_response/tabify/_response'));
var TabbedAggResponseWriter = Private(require('components/agg_response/tabify/_response_writer'));
var Buckets = require('components/agg_response/tabify/_buckets');
var notify = new Notifier({ location: 'agg_response/tabify'});
function tabifyAggResponse(vis, esResponse, respOpts) {
var resp = new TabbedAggResponseWriter(vis, respOpts);
var write = new TabbedAggResponseWriter(vis, respOpts);
var topLevelBucket = _.assign({}, esResponse.aggregations, {
doc_count: esResponse.hits.total
});
if (!resp.aggStack.length) {
if (!write.aggStack.length) {
var schema = vis.type.schemas.metrics[0];
if (!schema) {
throw new Error('Unable to tabify empty response without a metric schema of some sort');
@ -26,15 +27,15 @@ define(function (require) {
type: 'count',
schema: schema
});
resp.columns.push({
write.columns.push({
aggConfig: tempCountAgg
});
resp.aggStack.push(tempCountAgg);
write.aggStack.push(tempCountAgg);
}
collectBucket(resp, topLevelBucket);
collectBucket(write, topLevelBucket);
return resp.done();
return write.response();
}
/**
@ -46,46 +47,48 @@ define(function (require) {
* @param {undefined|string} key - the key where the bucket was found
* @returns {undefined}
*/
function collectBucket(resp, bucket, key) {
var agg = resp.aggStack.shift();
function collectBucket(write, bucket, key) {
var agg = write.aggStack.shift();
var aggResp = bucket[agg.id];
switch (agg.schema.group) {
case 'buckets':
var buckets = new Buckets(aggResp);
if (buckets.length) {
var splitting = resp.canSplit && agg.schema.name === 'split';
var splitting = write.canSplit && agg.schema.name === 'split';
buckets.forEach(function collectSubBucket(subBucket, subBucketKey) {
var key = bucketKey(subBucket, subBucketKey);
var recurse = function () {
collectBucket(resp, subBucket, key);
};
if (splitting) resp.split(agg, key, recurse);
else resp.cell(key, recurse);
});
if (splitting) {
write.split(agg, buckets, function (subBucket, key) {
collectBucket(write, subBucket, key);
});
} else {
buckets.forEach(function (subBucket, key) {
write.cell(key, function () {
collectBucket(write, subBucket, key);
});
});
}
} else {
// bucket didn't result in sub-buckets, we will try to
// write the row, but stop digging. This row.write will do nothing in
// specific scenarios known to the the Response
resp.row();
write.row();
}
break;
case 'metrics':
resp.cell(aggResp ? metricValue(aggResp) : bucketCount(bucket), function () {
if (!resp.aggStack.length) {
write.cell(aggResp ? metricValue(aggResp) : bucketCount(bucket), function () {
if (!write.aggStack.length) {
// row complete
resp.row();
write.row();
} else {
// process the next agg at this same level
collectBucket(resp, bucket, key);
collectBucket(write, bucket, key);
}
});
break;
}
resp.aggStack.unshift(agg);
write.aggStack.unshift(agg);
}
// read the metric value from a metric response
@ -98,13 +101,6 @@ define(function (require) {
return bucket.doc_count;
}
// read the key from an agg bucket, optionally use the key the bucket
// was found at
function bucketKey(bucket, key) {
if (key != null) return key;
return bucket.key;
}
return notify.timed('tabify agg response', tabifyAggResponse);
};
});