diff --git a/public/app/core/table_model.ts b/public/app/core/table_model.ts index f3d0b81998f..6b02e906583 100644 --- a/public/app/core/table_model.ts +++ b/public/app/core/table_model.ts @@ -3,9 +3,11 @@ export default class TableModel { columns: any[]; rows: any[]; type: string; + columnMap: any; constructor() { this.columns = []; + this.columnMap = {}; this.rows = []; this.type = 'table'; } @@ -36,4 +38,11 @@ export default class TableModel { this.columns[options.col].desc = false; } } + + addColumn(col) { + if (!this.columnMap[col.text]) { + this.columns.push(col); + this.columnMap[col.text] = col; + } + } } diff --git a/public/app/plugins/datasource/elasticsearch/elastic_response.ts b/public/app/plugins/datasource/elasticsearch/elastic_response.ts index 04cfe20a9c1..a0416e8d91c 100644 --- a/public/app/plugins/datasource/elasticsearch/elastic_response.ts +++ b/public/app/plugins/datasource/elasticsearch/elastic_response.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import queryDef from "./query_def"; +import TableModel from 'app/core/table_model'; export function ElasticResponse(targets, response) { this.targets = targets; @@ -95,21 +96,35 @@ ElasticResponse.prototype.processMetrics = function(esAgg, target, seriesList, p } }; -ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, target, docs, props) { - var metric, y, i, bucket, metricName, doc; +ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, target, table, props) { + // add columns + if (table.columns.length === 0) { + for (let propKey of _.keys(props)) { + table.addColumn({text: propKey}); + } + table.addColumn({text: aggDef.field}); + } - for (i = 0; i < esAgg.buckets.length; i++) { - bucket = esAgg.buckets[i]; - doc = _.defaults({}, props); - doc[aggDef.field] = bucket.key; + // helper func to add values to value array + let addMetricValue = (values, metricName, value) => { + table.addColumn({text: metricName}); + values.push(value); + }; - for (y = 0; y < target.metrics.length; y++) { - metric = target.metrics[y]; + for (let bucket of esAgg.buckets) { + let values = []; + for (let propValues of _.values(props)) { + values.push(propValues); + } + + // add bucket key (value) + values.push(bucket.key); + + for (let metric of target.metrics) { switch (metric.type) { case "count": { - metricName = this._getMetricName(metric.type); - doc[metricName] = bucket.doc_count; + addMetricValue(values, this._getMetricName(metric.type), bucket.doc_count); break; } case 'extended_stats': { @@ -123,33 +138,32 @@ ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, targe stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper; stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower; - metricName = this._getMetricName(statName); - doc[metricName] = stats[statName]; + addMetricValue(values, this._getMetricName(statName), stats[statName]); } break; } default: { - metricName = this._getMetricName(metric.type); - var otherMetrics = _.filter(target.metrics, {type: metric.type}); + let metricName = this._getMetricName(metric.type); + let otherMetrics = _.filter(target.metrics, {type: metric.type}); // if more of the same metric type include field field name in property if (otherMetrics.length > 1) { metricName += ' ' + metric.field; } - doc[metricName] = bucket[metric.id].value; + addMetricValue(values, metricName, bucket[metric.id].value); break; } } } - docs.push(doc); + table.rows.push(values); } }; // This is quite complex // neeed to recurise down the nested buckets to build series -ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, docs, props, depth) { +ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, table, props, depth) { var bucket, aggDef, esAgg, aggId; var maxDepth = target.bucketAggs.length-1; @@ -165,7 +179,7 @@ ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, do if (aggDef.type === 'date_histogram') { this.processMetrics(esAgg, target, seriesList, props); } else { - this.processAggregationDocs(esAgg, aggDef, target, docs, props); + this.processAggregationDocs(esAgg, aggDef, target, table, props); } } else { for (var nameIndex in esAgg.buckets) { @@ -179,7 +193,7 @@ ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, do if (bucket.key_as_string) { props[aggDef.field] = bucket.key_as_string; } - this.processBuckets(bucket, target, seriesList, docs, props, depth+1); + this.processBuckets(bucket, target, seriesList, table, props, depth+1); } } } @@ -325,9 +339,9 @@ ElasticResponse.prototype.getTimeSeries = function() { var aggregations = response.aggregations; var target = this.targets[i]; var tmpSeriesList = []; - var docs = []; + var table = new TableModel(); - this.processBuckets(aggregations, target, tmpSeriesList, docs, {}, 0); + this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0); this.trimDatapoints(tmpSeriesList, target); this.nameSeries(tmpSeriesList, target); @@ -335,8 +349,8 @@ ElasticResponse.prototype.getTimeSeries = function() { seriesList.push(tmpSeriesList[y]); } - if (seriesList.length === 0 && docs.length > 0) { - seriesList.push({target: 'docs', type: 'docs', datapoints: docs}); + if (table.rows.length > 0) { + seriesList.push(table); } } } diff --git a/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts index 2e70cf18e9a..ff78a99e10e 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts @@ -387,10 +387,9 @@ describe('ElasticResponse', function() { result = new ElasticResponse(targets, response).getTimeSeries(); }); - it('should return docs with byte and count', function() { - expect(result.data[0].datapoints.length).to.be(3); - expect(result.data[0].datapoints[0].Count).to.be(1); - expect(result.data[0].datapoints[0].bytes).to.be(1000); + it('should return table with byte and count', function() { + expect(result.data[0].rows.length).to.be(3); + expect(result.data[0].columns).to.eql([{text: 'bytes'}, {text: 'Count'}]); }); }); @@ -530,14 +529,14 @@ describe('ElasticResponse', function() { it('should return table', function() { expect(result.data.length).to.be(1); - expect(result.data[0].type).to.be('docs'); - expect(result.data[0].datapoints.length).to.be(2); - expect(result.data[0].datapoints[0].host).to.be("server-1"); - expect(result.data[0].datapoints[0].Average).to.be(1000); - expect(result.data[0].datapoints[0].Count).to.be(369); + expect(result.data[0].type).to.be('table'); + expect(result.data[0].rows.length).to.be(2); + expect(result.data[0].rows[0][0]).to.be("server-1"); + expect(result.data[0].rows[0][1]).to.be(1000); + expect(result.data[0].rows[0][2]).to.be(369); - expect(result.data[0].datapoints[1].host).to.be("server-2"); - expect(result.data[0].datapoints[1].Average).to.be(2000); + expect(result.data[0].rows[1][0]).to.be("server-2"); + expect(result.data[0].rows[1][1]).to.be(2000); }); }); @@ -573,10 +572,9 @@ describe('ElasticResponse', function() { }); it('should include field in metric name', function() { - expect(result.data[0].type).to.be('docs'); - expect(result.data[0].datapoints[0].Average).to.be(undefined); - expect(result.data[0].datapoints[0]['Average test']).to.be(1000); - expect(result.data[0].datapoints[0]['Average test2']).to.be(3000); + expect(result.data[0].type).to.be('table'); + expect(result.data[0].rows[0][1]).to.be(1000); + expect(result.data[0].rows[0][2]).to.be(3000); }); });