Browse Source

feat(elasticsearch): a lot of work to support aggregation queries without date_histogram, queries that return metric aggregations now work with the table panel (json data type), #3219

Torkel Ödegaard 10 years ago
parent
commit
4751e4b94e

+ 1 - 1
public/app/panels/table/controller.ts

@@ -27,7 +27,7 @@ export class TablePanelCtrl {
     var panelDefaults = {
       targets: [{}],
       transform: 'timeseries_to_rows',
-      pageSize: 50,
+      pageSize: null,
       showHeader: true,
       styles: [
         {

+ 1 - 1
public/app/panels/table/editor.html

@@ -44,7 +44,7 @@
 						Pagination (Page size)
 					</li>
 					<li>
-						<input type="number" class="input-small tight-form-input" placeholder="50"
+						<input type="number" class="input-small tight-form-input" placeholder="100"
 						empty-to-null ng-model="panel.pageSize" ng-change="render()" ng-model-onblur>
 					</li>
 					<li class="tight-form-item">

+ 2 - 1
public/app/panels/table/editor.ts

@@ -38,7 +38,7 @@ export function tablePanelEditor() {
 
       scope.updateColumnsMenu = function(data) {
         scope.columnsMenu = transformers[scope.panel.transform].getColumns(data);
-        scope.showColumnOptions = scope.columnsMenu.length > 0;
+        scope.showColumnOptions = true;
      };
 
       scope.$on('render', function(event, table, rawData) {
@@ -51,6 +51,7 @@ export function tablePanelEditor() {
       };
 
       scope.transformChanged = function() {
+        scope.panel.columns = [];
         scope.updateColumnsMenu();
         scope.render();
       };

+ 2 - 7
public/app/panels/table/module.ts

@@ -45,7 +45,8 @@ export function tablePanel() {
       function appendPaginationControls(footerElem) {
         footerElem.empty();
 
-        var pageCount = Math.ceil(data.rows.length / panel.pageSize);
+        var pageSize = panel.pageSize || 100;
+        var pageCount = Math.ceil(data.rows.length / pageSize);
         if (pageCount === 1) {
           return;
         }
@@ -55,18 +56,12 @@ export function tablePanel() {
 
         var paginationList = $('<ul></ul>');
 
-        // var prevLink = $('<li><a class="table-panel-page-link pointer">«</a></li>');
-        // paginationList.append(prevLink);
-
         for (var i = startPage; i < endPage; i++) {
           var activeClass = i === scope.pageIndex ? 'active' : '';
           var pageLinkElem = $('<li><a class="table-panel-page-link pointer ' + activeClass + '">' + (i+1) + '</a></li>');
           paginationList.append(pageLinkElem);
         }
 
-        // var nextLink = $('<li><a href="#">»</a></li>');
-        // paginationList.append(nextLink);
-
         footerElem.append(paginationList);
       }
 

+ 3 - 2
public/app/panels/table/renderer.ts

@@ -119,8 +119,9 @@ export class TableRenderer {
   }
 
   render(page) {
-    let startPos = page * this.panel.pageSize;
-    let endPos = Math.min(startPos + this.panel.pageSize, this.table.rows.length);
+    let pageSize = this.panel.pageSize || 100;
+    let startPos = page * pageSize;
+    let endPos = Math.min(startPos + pageSize, this.table.rows.length);
     var html = "";
 
     for (var y = startPos; y < endPos; y++) {

+ 1 - 1
public/app/panels/table/transformers.ts

@@ -145,7 +145,7 @@ transformers['json'] = {
     var names: any = {};
     for (var i = 0; i < data.length; i++) {
       var series = data[i];
-      if (series.type === 'docs') {
+      if (series.type !== 'docs') {
         continue;
       }
 

+ 61 - 5
public/app/plugins/datasource/elasticsearch/elastic_response.js

@@ -86,20 +86,71 @@ function (_, queryDef) {
     }
   };
 
+  ElasticResponse.prototype.processAggregationDocs = function(esAgg, aggDef, target, docs, props) {
+    var metric, y, i, bucket, metricName, doc;
+
+    for (i = 0; i < esAgg.buckets.length; i++) {
+      bucket = esAgg.buckets[i];
+      doc = _.defaults({}, props);
+      doc[aggDef.field] = bucket.key;
+
+      for (y = 0; y < target.metrics.length; y++) {
+        metric = target.metrics[y];
+
+        switch(metric.type) {
+          case "count": {
+            metricName = this._getMetricName(metric.type);
+            doc[metricName] = bucket.doc_count;
+            break;
+          }
+          case 'extended_stats': {
+            for (var statName in metric.meta) {
+              if (!metric.meta[statName]) {
+                continue;
+              }
+
+              var stats = bucket[metric.id];
+              // add stats that are in nested obj to top level obj
+              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];
+            }
+            break;
+          }
+          default:  {
+            metricName = this._getMetricName(metric.type);
+            doc[metricName] =bucket[metric.id].value;
+            break;
+          }
+        }
+      }
+
+      docs.push(doc);
+    }
+  };
+
   // This is quite complex
   // neeed to recurise down the nested buckets to build series
-  ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, props) {
+  ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, docs, props, depth) {
     var bucket, aggDef, esAgg, aggId;
+    var maxDepth = target.bucketAggs.length-1;
 
     for (aggId in aggs) {
       aggDef = _.findWhere(target.bucketAggs, {id: aggId});
       esAgg = aggs[aggId];
+
       if (!aggDef) {
         continue;
       }
 
-      if (aggDef.type === 'date_histogram') {
-        this.processMetrics(esAgg, target, seriesList, props);
+      if (depth === maxDepth) {
+        if (aggDef.type === 'date_histogram')  {
+          this.processMetrics(esAgg, target, seriesList, props);
+        } else {
+          this.processAggregationDocs(esAgg, aggDef, target, docs, props);
+        }
       } else {
         for (var nameIndex in esAgg.buckets) {
           bucket = esAgg.buckets[nameIndex];
@@ -109,7 +160,7 @@ function (_, queryDef) {
           } else {
             props["filter"] = nameIndex;
           }
-          this.processBuckets(bucket, target, seriesList, props);
+          this.processBuckets(bucket, target, seriesList, docs, props, depth+1);
         }
       }
     }
@@ -217,13 +268,18 @@ function (_, queryDef) {
         var aggregations = response.aggregations;
         var target = this.targets[i];
         var tmpSeriesList = [];
+        var docs = [];
 
-        this.processBuckets(aggregations, target, tmpSeriesList, {});
+        this.processBuckets(aggregations, target, tmpSeriesList, docs, {}, 0);
         this.nameSeries(tmpSeriesList, target);
 
         for (var y = 0; y < tmpSeriesList.length; y++) {
           seriesList.push(tmpSeriesList[y]);
         }
+
+        if (seriesList.length === 0 && docs.length > 0) {
+          seriesList.push({target: 'docs', type: 'docs', datapoints: docs});
+        }
       }
     }
 

+ 45 - 0
public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts

@@ -411,6 +411,51 @@ describe('ElasticResponse', function() {
     });
   });
 
+  describe('No group by time', function() {
+    beforeEach(function() {
+      targets = [{
+        refId: 'A',
+        metrics: [{type: 'avg', id: '1'}, {type: 'count' }],
+        bucketAggs: [{id: '2', type: 'terms', field: 'host'}],
+      }];
+
+      response =  {
+        responses: [{
+          aggregations: {
+            "2": {
+              buckets: [
+                {
+                  "1": { value: 1000},
+                  key: "server-1",
+                  doc_count: 369,
+                },
+                {
+                  "1": { value: 2000},
+                  key: "server-2",
+                  doc_count: 200,
+                },
+              ]
+            }
+          }
+        }]
+      };
+
+      result = new ElasticResponse(targets, response).getTimeSeries();
+    });
+
+    it.only('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].datapoints[1].host).to.be("server-2");
+      expect(result.data[0].datapoints[1].Average).to.be(2000);
+    });
+  });
+
   describe('Raw documents query', function() {
     beforeEach(function() {
       targets = [{ refId: 'A', metrics: [{type: 'raw_document', id: '1'}], bucketAggs: [] }];