Просмотр исходного кода

feat(elasticsearch): metric response handling and processsing now supports alias patterns, {{term field name}} and {{metric}} now works, #1034

Torkel Ödegaard 10 лет назад
Родитель
Сommit
572a80d1d1

+ 74 - 51
public/app/plugins/datasource/elasticsearch/elasticResponse.js

@@ -11,8 +11,8 @@ function (_) {
 
   // This is quite complex
   // neeed to recurise down the nested buckets to build series
-  ElasticResponse.prototype.processBuckets = function(aggs, target, series, level) {
-    var value, metric, i, y, bucket, aggDef, esAgg, nestedSeries;
+  ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, level, props) {
+    var value, metric, i, y, bucket, aggDef, esAgg, newSeries;
 
     aggDef = target.bucketAggs[level];
     esAgg = aggs[aggDef.id];
@@ -20,9 +20,9 @@ function (_) {
     if (level < target.bucketAggs.length - 1) {
       for (i = 0; i < esAgg.buckets.length; i++) {
         bucket = esAgg.buckets[i];
-        nestedSeries = {prop: {key: bucket.key, field: aggDef.field}, series: []};
-        series.push(nestedSeries);
-        this.processBuckets(bucket, target, nestedSeries.series, level+1);
+        props = _.clone(props);
+        props[aggDef.field] = bucket.key;
+        this.processBuckets(bucket, target, seriesList, level+1, props);
       }
       return;
     }
@@ -32,54 +32,86 @@ function (_) {
 
       switch(metric.type) {
         case 'count': {
-          var countSeries = { datapoints: [], metric: 'count'};
+          newSeries = { datapoints: [], metric: 'count', props: props};
           for (i = 0; i < esAgg.buckets.length; i++) {
             bucket = esAgg.buckets[i];
             value = bucket.doc_count;
-            countSeries.datapoints.push([value, bucket.key]);
+            newSeries.datapoints.push([value, bucket.key]);
           }
-          series.push(countSeries);
+          seriesList.push(newSeries);
           break;
         }
         case 'percentiles': {
-          // for (i = 0; i < esAgg.buckets.length; i++) {
-          //   bucket = esAgg.buckets[i];
-          //   var values = bucket[metric.id].values;
-          //   for (var prop in values) {
-          //     addMetricPoint(seriesName + ' ' + prop, values[prop], bucket.key);
-          //   }
-          // }
+          if (esAgg.buckets.length === 0) {
+            break;
+          }
+
+          var firstBucket = esAgg.buckets[0];
+          var percentiles = firstBucket[metric.id].values;
+
+          for (var percentileName in percentiles) {
+            newSeries = {datapoints: [], metric: 'p' + percentileName, props: props};
+
+            for (i = 0; i < esAgg.buckets.length; i++) {
+              bucket = esAgg.buckets[i];
+              var values = bucket[metric.id].values;
+              newSeries.datapoints.push([values[percentileName], bucket.key]);
+            }
+            seriesList.push(newSeries);
+          }
+
           break;
         }
         case 'extended_stats': {
-          // var stats = bucket[metric.id];
-          // stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper;
-          // stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower;
-          //
-          // for (var statName in metric.meta) {
-          //   if (metric.meta[statName]) {
-          //     addMetricPoint(seriesName + ' ' + statName, stats[statName], bucket.key);
-          //   }
-          // }
+          for (var statName in metric.meta) {
+            if (!metric.meta[statName]) {
+              continue;
+            }
+
+            newSeries = {datapoints: [], metric: statName, props: props};
+
+            for (i = 0; i < esAgg.buckets.length; i++) {
+              bucket = esAgg.buckets[i];
+              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;
+
+              newSeries.datapoints.push([stats[statName], bucket.key]);
+            }
+
+            seriesList.push(newSeries);
+          }
+
           break;
         }
         default: {
-          var newSeries = { datapoints: [], metric: metric.type + ' ' + metric.field };
+          newSeries = { datapoints: [], metric: metric.type + ' ' + metric.field, props: props};
           for (i = 0; i < esAgg.buckets.length; i++) {
             bucket = esAgg.buckets[i];
             value = bucket[metric.id].value;
             newSeries.datapoints.push([value, bucket.key]);
           }
-          series.push(newSeries);
+          seriesList.push(newSeries);
           break;
         }
       }
     }
   };
 
-  ElasticResponse.prototype._getSeriesName = function(props, metric, alias) {
-    if (alias) {
-      return alias;
+  ElasticResponse.prototype._getSeriesName = function(props, metric, target, metricTypeCount) {
+    if (target.alias) {
+      var regex = /\{\{([\s\S]+?)\}\}/g;
+
+      return target.alias.replace(regex, function(match, g1, g2) {
+        var group = g1 || g2;
+
+        if (props[group]) { return props[group]; }
+        if (group === 'metric') { return metric; }
+
+        return match;
+      });
     }
 
     var propKeys = _.keys(props);
@@ -92,31 +124,15 @@ function (_) {
       name += props[propName] + ' ';
     }
 
-    if (propKeys.length === 1) {
+    if (metricTypeCount === 1) {
       return name.trim();
     }
 
     return name.trim() + ' ' + metric;
   };
 
-  ElasticResponse.prototype._collectSeriesFromTree = function(seriesTree, props, seriesList, alias) {
-    console.log('props: ', props);
-
-    for (var i = 0; i < seriesTree.length; i++) {
-      var series = seriesTree[i];
-      if (series.datapoints) {
-        series.target = this._getSeriesName(props, series.metric, alias);
-        seriesList.push(series);
-      } else {
-        props = _.clone(props);
-        props[series.prop.field] = series.prop.key;
-        this._collectSeriesFromTree(series.series, props, seriesList);
-      }
-    }
-  };
-
   ElasticResponse.prototype.getTimeSeries = function() {
-    var series = [];
+    var seriesList = [];
 
     for (var i = 0; i < this.response.responses.length; i++) {
       var response = this.response.responses[i];
@@ -126,13 +142,20 @@ function (_) {
 
       var aggregations = response.aggregations;
       var target = this.targets[i];
-      var seriesTree = [];
+      var tmpSeriesList = [];
+
+      this.processBuckets(aggregations, target, tmpSeriesList, 0, {});
 
-      this.processBuckets(aggregations, target, seriesTree, 0, '');
-      this._collectSeriesFromTree(seriesTree, {}, series, '');
+      var metricTypeCount = _.uniq(_.pluck(tmpSeriesList, 'metric')).length;
+
+      for (var y = 0; y < tmpSeriesList.length; y++) {
+        var series= tmpSeriesList[y];
+        series.target = this._getSeriesName(series.props, series.metric, target, metricTypeCount);
+        seriesList.push(series);
+      }
     }
 
-    return { data: series };
+    return { data: seriesList };
   };
 
   return ElasticResponse;

+ 0 - 1
public/app/plugins/datasource/elasticsearch/metricAgg.js

@@ -27,7 +27,6 @@ function (angular, _, queryDef) {
     $scope.validateModel = function() {
       $scope.isFirst = $scope.index === 0;
       $scope.isSingle = metricAggs.length === 1;
-
       $scope.settingsLinkText = '';
 
       if (!$scope.agg.field) {

+ 6 - 2
public/app/plugins/datasource/elasticsearch/partials/query.editor.html

@@ -47,8 +47,13 @@
 		<li>
 			<input type="text" class="tight-form-input" style="width: 345px;" ng-model="target.query" spellcheck='false' placeholder="Lucence query" ng-blur="get_data()">
 		</li>
+		<li class="tight-form-item query-keyword">
+			Alias
+		</li>
+		<li>
+			<input type="text" class="tight-form-input" style="width: 245px;" ng-model="target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="get_data()">
+		</li>
 	</ul>
-
 	<div class="clearfix"></div>
 
 	<div style="padding: 10px" ng-if="target.rawQuery">
@@ -57,7 +62,6 @@
 </div>
 
 <div ng-hide="target.rawQuery">
-
 	<div ng-repeat="agg in target.metrics">
 		<elastic-metric-agg
 			target="target" index="$index"

+ 0 - 20
public/app/plugins/datasource/influxdb/influxSeries.js

@@ -105,25 +105,5 @@ function (_) {
     return list;
   };
 
-  p.createNameForSeries = function(seriesName, groupByColValue) {
-    var regex = /\$(\w+)/g;
-    var segments = seriesName.split('.');
-
-    return this.alias.replace(regex, function(match, group) {
-      if (group === 's') {
-        return seriesName;
-      }
-      else if (group === 'g') {
-        return groupByColValue;
-      }
-      var index = parseInt(group);
-      if (_.isNumber(index) && index < segments.length) {
-        return segments[index];
-      }
-      return match;
-    });
-
-  };
-
   return InfluxSeries;
 });

+ 83 - 23
public/test/specs/elasticsearch-response-specs.js

@@ -94,7 +94,7 @@ define([
 
     });
 
-    describe('single group by query', function() {
+    describe('single group by query one metric', function() {
       var result;
 
       beforeEach(function() {
@@ -145,7 +145,60 @@ define([
       });
     });
 
-    describe.skip('with percentiles ', function() {
+    describe('single group by query two metrics', function() {
+      var result;
+
+      beforeEach(function() {
+        targets = [{
+          refId: 'A',
+          metrics: [{type: 'count', id: '1'}, {type: 'avg', field: '@value', id: '4'}],
+          bucketAggs: [{type: 'terms', field: 'host', id: '2'}, {type: 'date_histogram', field: '@timestamp', id: '3'}],
+        }];
+        response =  {
+          responses: [{
+            aggregations: {
+              "2": {
+                buckets: [
+                  {
+                    "3": {
+                      buckets: [
+                        { "4": {value: 10}, doc_count: 1, key: 1000},
+                        { "4": {value: 12}, doc_count: 3, key: 2000}
+                      ]
+                    },
+                    doc_count: 4,
+                    key: 'server1',
+                  },
+                  {
+                    "3": {
+                      buckets: [
+                        { "4": {value: 20}, doc_count: 1, key: 1000},
+                        { "4": {value: 32}, doc_count: 3, key: 2000}
+                      ]
+                    },
+                    doc_count: 10,
+                    key: 'server2',
+                  },
+                ]
+              }
+            }
+          }]
+        };
+
+        result = new ElasticResponse(targets, response).getTimeSeries();
+      });
+
+      it('should return 2 series', function() {
+        expect(result.data.length).to.be(4);
+        expect(result.data[0].datapoints.length).to.be(2);
+        expect(result.data[0].target).to.be('server1 count');
+        expect(result.data[1].target).to.be('server1 avg @value');
+        expect(result.data[2].target).to.be('server2 count');
+        expect(result.data[3].target).to.be('server2 avg @value');
+      });
+    });
+
+    describe('with percentiles ', function() {
       var result;
 
       beforeEach(function() {
@@ -181,22 +234,22 @@ define([
       it('should return 2 series', function() {
         expect(result.data.length).to.be(2);
         expect(result.data[0].datapoints.length).to.be(2);
-        expect(result.data[0].target).to.be('75');
-        expect(result.data[1].target).to.be('90');
+        expect(result.data[0].target).to.be('p75');
+        expect(result.data[1].target).to.be('p90');
         expect(result.data[0].datapoints[0][0]).to.be(3.3);
         expect(result.data[0].datapoints[0][1]).to.be(1000);
         expect(result.data[1].datapoints[1][0]).to.be(4.5);
       });
     });
 
-    describe.skip('with extended_stats ', function() {
+    describe('with extended_stats', function() {
       var result;
 
       beforeEach(function() {
         targets = [{
           refId: 'A',
           metrics: [{type: 'extended_stats', meta: {max: true, std_deviation_bounds_upper: true},  id: '1'}],
-          bucketAggs: [{type: 'date_histogram', id: '3'}],
+          bucketAggs: [{type: 'terms', field: 'host', id: '3'}, {type: 'date_histogram', id: '4'}],
         }];
         response = {
           responses: [{
@@ -204,15 +257,25 @@ define([
               "3": {
                 buckets: [
                   {
-                    "1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
-                    doc_count: 10,
-                    key: 1000
+                    key: 'server1',
+                    "4": {
+                      buckets: [{
+                        "1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
+                        doc_count: 10,
+                        key: 1000
+                      }]
+                    }
                   },
                   {
-                    "1": {max: 7.2, min: 3.5, std_deviation_bounds: {upper: 4, lower: -1}},
-                    doc_count: 15,
-                    key: 2000
-                  }
+                    key: 'server2',
+                    "4": {
+                      buckets: [{
+                        "1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
+                        doc_count: 10,
+                        key: 1000
+                      }]
+                    }
+                  },
                 ]
               }
             }
@@ -222,28 +285,25 @@ define([
         result = new ElasticResponse(targets, response).getTimeSeries();
       });
 
-      it('should return 2 series', function() {
-        expect(result.data.length).to.be(2);
-        expect(result.data[0].datapoints.length).to.be(2);
-        expect(result.data[0].target).to.be('max');
-        expect(result.data[1].target).to.be('std_deviation_bounds_upper');
+      it('should return 4 series', function() {
+        expect(result.data.length).to.be(4);
+        expect(result.data[0].datapoints.length).to.be(1);
+        expect(result.data[0].target).to.be('server1 max');
+        expect(result.data[1].target).to.be('server1 std_deviation_bounds_upper');
 
         expect(result.data[0].datapoints[0][0]).to.be(10.2);
-        expect(result.data[0].datapoints[1][0]).to.be(7.2);
-
         expect(result.data[1].datapoints[0][0]).to.be(3);
-        expect(result.data[1].datapoints[1][0]).to.be(4);
       });
     });
 
-    describe.skip('single group by with alias pattern', function() {
+    describe('single group by with alias pattern', function() {
       var result;
 
       beforeEach(function() {
         targets = [{
           refId: 'A',
           metrics: [{type: 'count', id: '1'}],
-          alias: '[[_@host]] $_metric and!',
+          alias: '{{@host}} {{metric}} and!',
           bucketAggs: [
             {type: 'terms', field: '@host', id: '2'},
             {type: 'date_histogram', field: '@timestamp', id: '3'}