Explorar el Código

Merge branch 'moving_avg_es_support' of https://github.com/bergquist/grafana into bergquist-moving_avg_es_support

Torkel Ödegaard hace 10 años
padre
commit
d6311aefb7

+ 14 - 3
public/app/plugins/datasource/elasticsearch/elastic_response.js

@@ -76,8 +76,12 @@ function (_, queryDef) {
           newSeries = { datapoints: [], metric: metric.type, field: 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]);
+
+            value = bucket[metric.id];
+            if (value !== undefined) {
+              newSeries.datapoints.push([value.value, bucket.key]);
+            }
+
           }
           seriesList.push(newSeries);
           break;
@@ -193,7 +197,14 @@ function (_, queryDef) {
       });
     }
 
-    if (series.field) {
+    if (series.field && series.metric === 'moving_avg') {
+      var appliedAgg = _.findWhere(target.metrics, { id: series.field });
+      if (appliedAgg) {
+        metricName += ' ' + queryDef.describeMetric(appliedAgg);
+      } else {
+        metricName = 'Unset';
+      }
+    } else if (series.field) {
       metricName += ' ' + series.field;
     }
 

+ 18 - 0
public/app/plugins/datasource/elasticsearch/metric_agg.js

@@ -13,14 +13,21 @@ function (angular, _, queryDef) {
 
     $scope.metricAggTypes = queryDef.metricAggTypes;
     $scope.extendedStats = queryDef.extendedStats;
+    $scope.mavgOptions = [];
 
     $scope.init = function() {
       $scope.agg = metricAggs[$scope.index];
       $scope.validateModel();
+      $scope.updateMavgOptions();
+    };
+
+    $scope.updateMavgOptions = function() {
+      $scope.mavgOptions = queryDef.getMovingAverageOptions($scope.target);
     };
 
     $rootScope.onAppEvent('elastic-query-updated', function() {
       $scope.index = _.indexOf(metricAggs, $scope.agg);
+      $scope.updateMavgOptions();
       $scope.validateModel();
     }, $scope);
 
@@ -35,6 +42,12 @@ function (angular, _, queryDef) {
       }
 
       switch($scope.agg.type) {
+        case 'moving_avg': {
+          $scope.agg.pipelineAgg = $scope.agg.pipelineAgg || 'Metric to apply moving average';
+          $scope.settingsLinkText = 'Moving average options';
+          $scope.agg.field = $scope.agg.pipelineAgg;
+          break;
+        }
         case 'percentiles': {
           $scope.agg.settings.percents = $scope.agg.settings.percents || [25,50,75,95,99];
           $scope.settingsLinkText = 'values: ' + $scope.agg.settings.percents.join(',');
@@ -65,6 +78,11 @@ function (angular, _, queryDef) {
 
     $scope.toggleOptions = function() {
       $scope.showOptions = !$scope.showOptions;
+      $scope.updateMavgOptions();
+    };
+
+    $scope.onChangeInternal = function() {
+      $scope.onChange();
     };
 
     $scope.onTypeChange = function() {

+ 12 - 1
public/app/plugins/datasource/elasticsearch/partials/metricAgg.html

@@ -7,7 +7,7 @@
 			<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="tight-form-item-large"></metric-segment-model>
 		</li>
 		<li ng-if="aggDef.requiresField">
-			<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment>
+			<metric-segment-model property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="tight-form-item-xxlarge"></metric-segment-model>
 		</li>
 		<li class="tight-form-item last" ng-if="settingsLinkText">
 			<a ng-click="toggleOptions()">{{settingsLinkText}}</a>
@@ -27,6 +27,17 @@
 
 <div class="tight-form" ng-if="showOptions">
 	<div class="tight-form-inner-box">
+		<div class="tight-form last" ng-if="agg.type === 'moving_avg'">
+			<ul class="tight-form-list">
+				<li class="tight-form-item">
+					Based on
+				</li>
+				<li>
+					<metric-segment-model property="agg.pipelineAgg" options="mavgOptions" on-change="onChangeInternal()" css-class="last"></metric-segment-model>
+				</li>
+			</ul>
+			<div class="clearfix"></div>
+		</div>
 		<div class="tight-form last" ng-if="agg.type === 'percentiles'">
 			<ul class="tight-form-list">
 				<li class="tight-form-item">

+ 13 - 3
public/app/plugins/datasource/elasticsearch/query_builder.js

@@ -167,14 +167,25 @@ function () {
         continue;
       }
 
-      var metricAgg = {field: metric.field};
+      var aggField = {};
+      var metricAgg = null;
+
+      if (metric.type === 'moving_avg') {
+        if (metric.pipelineAgg && /^\d*$/.test(metric.pipelineAgg)) {
+          metricAgg = { buckets_path: metric.pipelineAgg };
+        } else {
+          continue;
+        }
+      } else {
+        metricAgg = {field: metric.field};
+      }
+
       for (var prop in metric.settings) {
         if (metric.settings.hasOwnProperty(prop) && metric.settings[prop] !== null) {
           metricAgg[prop] = metric.settings[prop];
         }
       }
 
-      var aggField = {};
       aggField[metric.type] = metricAgg;
       nestedAggs.aggs[metric.id] = aggField;
     }
@@ -217,5 +228,4 @@ function () {
   };
 
   return ElasticQueryBuilder;
-
 });

+ 23 - 0
public/app/plugins/datasource/elasticsearch/query_def.js

@@ -13,6 +13,7 @@ function (_) {
       {text: "Min",  value: 'min', requiresField: true},
       {text: "Extended Stats",  value: 'extended_stats', requiresField: true},
       {text: "Percentiles",  value: 'percentiles', requiresField: true},
+      {text: "Moving Average",  value: 'moving_avg', requiresField: false },
       {text: "Unique Count", value: "cardinality", requiresField: true},
       {text: "Raw Document", value: "raw_document", requiresField: false}
     ],
@@ -66,6 +67,28 @@ function (_) {
       {text: '1d', value: '1d'},
     ],
 
+    pipelineAggs: ['moving_avg'],
+
+    isPipelineAgg: function(metric) {
+      if (metric.type) {
+        return this.pipelineAggs.indexOf(metric.type) > -1;
+      }
+
+      return false;
+    },
+
+    getMovingAverageOptions: function(targets) {
+      var self = this;
+      var result = [];
+      _.each(targets.metrics, function(metric) {
+        if (metric.type !== 'moving_avg') {
+          result.push({text: self.describeMetric(metric), value: metric.id });
+        }
+      });
+
+      return result;
+    },
+
     getOrderByOptions: function(target) {
       var self = this;
       var metricRefs = [];

+ 60 - 0
public/app/plugins/datasource/elasticsearch/specs/query_builder_specs.ts

@@ -155,4 +155,64 @@ describe('ElasticQueryBuilder', function() {
     expect(query.size).to.be(500);
   });
 
+  it('with moving average', function() {
+    var query = builder.build({
+      metrics: [
+        {
+          id: '3',
+          type: 'sum',
+          field: '@value'
+        },
+        {
+          id: '2',
+          type: 'moving_avg',
+          field: '3',
+          pipelineAgg: '3'
+        }
+      ],
+      bucketAggs: [
+        {type: 'date_histogram', field: '@timestamp', id: '3'}
+      ],
+    });
+
+    var firstLevel = query.aggs["3"];
+
+    expect(firstLevel.aggs["2"]).not.to.be(undefined);
+    expect(firstLevel.aggs["2"].moving_avg).not.to.be(undefined);
+    expect(firstLevel.aggs["2"].moving_avg.buckets_path).to.be("3");
+  });
+
+  it('with broken moving average', function() {
+      var query = builder.build({
+          metrics: [
+              {
+                  id: '3',
+                  type: 'sum',
+                  field: '@value'
+              },
+              {
+                  id: '2',
+                  type: 'moving_avg',
+                  field: '3',
+                  pipelineAgg: '3'
+              },
+              {
+                  id: '4',
+                  type: 'moving_avg',
+                  field: '3',
+                  pipelineAgg: 'Metric to apply moving average'
+              }
+          ],
+          bucketAggs: [
+              { type: 'date_histogram', field: '@timestamp', id: '3' }
+          ],
+      });
+
+      var firstLevel = query.aggs["3"];
+
+      expect(firstLevel.aggs["2"]).not.to.be(undefined);
+      expect(firstLevel.aggs["2"].moving_avg).not.to.be(undefined);
+      expect(firstLevel.aggs["2"].moving_avg.buckets_path).to.be("3");
+      expect(firstLevel.aggs["4"]).to.be(undefined);
+  });
 });

+ 68 - 0
public/app/plugins/datasource/elasticsearch/specs/query_def_specs.ts

@@ -0,0 +1,68 @@
+///<amd-dependency path="../query_def" name="QueryDef" />
+///<amd-dependency path="test/specs/helpers" name="helpers" />
+
+import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
+
+declare var helpers: any;
+declare var QueryDef: any;
+
+describe('ElasticQueryDef', function() {
+
+  describe('getMovingAverageOptions', function() {
+    describe('with zero targets', function() {
+      var response = QueryDef.getMovingAverageOptions([]);
+
+      it('should return zero', function() {
+        expect(response.length).to.be(0);
+      });
+    });
+
+    describe('with count and sum targets', function() {
+      var targets = {
+        metrics: [
+          { type: 'count', field: '@value' },
+          { type: 'sum', field: '@value' }
+        ]
+      };
+
+      var response = QueryDef.getMovingAverageOptions(targets);
+
+      it('should return zero', function() {
+        expect(response.length).to.be(2);
+      });
+    });
+
+    describe('with count and moving average targets', function() {
+      var targets = {
+        metrics: [
+          { type: 'count', field: '@value' },
+          { type: 'moving_avg', field: '@value' }
+        ]
+      };
+
+      var response = QueryDef.getMovingAverageOptions(targets);
+
+      it('should return zero', function() {
+        expect(response.length).to.be(1);
+      });
+    });
+  });
+
+  describe('isPipelineMetric', function() {
+    describe('moving_avg', function() {
+      var result = QueryDef.isPipelineAgg({ type: 'moving_avg' });
+
+      it('is pipe line metric', function() {
+        expect(result).to.be(true);
+      });
+    });
+
+    describe('count', function() {
+      var result = QueryDef.isPipelineAgg({ type: 'count' });
+
+      it('is not pipe line metric', function() {
+        expect(result).to.be(false);
+      });
+    });
+  });
+});