Torkel Ödegaard 9 лет назад
Родитель
Сommit
20b7c2f2e2

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

@@ -29,6 +29,7 @@ function (angular, _, queryDef) {
     $scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion);
     $scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion);
     $scope.extendedStats = queryDef.extendedStats;
     $scope.extendedStats = queryDef.extendedStats;
     $scope.pipelineAggOptions = [];
     $scope.pipelineAggOptions = [];
+    $scope.modelSettingsValues = {};
 
 
     $scope.init = function() {
     $scope.init = function() {
       $scope.agg = metricAggs[$scope.index];
       $scope.agg = metricAggs[$scope.index];
@@ -95,6 +96,12 @@ function (angular, _, queryDef) {
           $scope.settingsLinkText = 'Stats: ' + stats.join(', ');
           $scope.settingsLinkText = 'Stats: ' + stats.join(', ');
           break;
           break;
         }
         }
+        case 'moving_avg': {
+          $scope.movingAvgModelTypes = queryDef.movingAvgModelOptions;
+          $scope.modelSettings = queryDef.getMovingAvgSettings($scope.agg.settings.model, true);
+          $scope.updateMovingAvgModelSettings();
+          break;
+        }
         case 'raw_document': {
         case 'raw_document': {
           $scope.target.metrics = [$scope.agg];
           $scope.target.metrics = [$scope.agg];
           $scope.target.bucketAggs = [];
           $scope.target.bucketAggs = [];
@@ -127,6 +134,25 @@ function (angular, _, queryDef) {
       $scope.onChange();
       $scope.onChange();
     };
     };
 
 
+    $scope.updateMovingAvgModelSettings = function () {
+      var modelSettingsKeys = [];
+      var modelSettings = queryDef.getMovingAvgSettings($scope.agg.settings.model, false);
+      for (var i=0; i < modelSettings.length; i++) {
+        modelSettingsKeys.push(modelSettings[i].value);
+      }
+
+      for (var key in $scope.agg.settings.settings) {
+        if (($scope.agg.settings.settings[key] === null) || (modelSettingsKeys.indexOf(key) === -1)) {
+          delete $scope.agg.settings.settings[key];
+        }
+      }
+    };
+
+    $scope.onChangeClearInternal = function() {
+      delete $scope.agg.settings.minimize;
+      $scope.onChange();
+    };
+
     $scope.onTypeChange = function() {
     $scope.onTypeChange = function() {
       $scope.agg.settings = {};
       $scope.agg.settings = {};
       $scope.agg.meta = {};
       $scope.agg.meta = {};

+ 43 - 33
public/app/plugins/datasource/elasticsearch/partials/metric_agg.html

@@ -37,52 +37,62 @@
 </div>
 </div>
 
 
 <div class="gf-form-group" ng-if="showOptions">
 <div class="gf-form-group" ng-if="showOptions">
-
 	<div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'">
 	<div class="gf-form offset-width-7" ng-if="agg.type === 'derivative'">
 		<label class="gf-form-label width-10">Unit</label>
 		<label class="gf-form-label width-10">Unit</label>
 		<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
 		<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.unit" ng-blur="onChangeInternal()" spellcheck='false'>
 	</div>
 	</div>
 
 
-	<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
-		<label class="gf-form-label width-10">Window</label>
-		<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
-	</div>
+  <div ng-if="agg.type === 'moving_avg'">
+    <div class="gf-form offset-width-7">
+      <label class="gf-form-label width-10">Model</label>
+      <metric-segment-model property="agg.settings.model" options="movingAvgModelTypes" on-change="onChangeClearInternal()" custom="false" css-class="width-12"></metric-segment-model>
+    </div>
 
 
-	<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
-		<label class="gf-form-label width-10">Model</label>
-		<input type="text" class="gf-form-input max-width-12" ng-change="onChangeInternal()" ng-model="agg.settings.model" blur="onChange()" spellcheck='false'>
-	</div>
+    <div class="gf-form offset-width-7">
+      <label class="gf-form-label width-10">Window</label>
+      <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.window" ng-blur="onChangeInternal()" spellcheck='false'>
+    </div>
 
 
-	<div class="gf-form offset-width-7" ng-if="agg.type === 'moving_avg'">
-		<label class="gf-form-label width-10">Predict</label>
-		<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.predict" ng-blur="onChangeInternal()" spellcheck='false'>
-	</div>
+    <div class="gf-form offset-width-7">
+      <label class="gf-form-label width-10">Predict</label>
+      <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.predict" ng-blur="onChangeInternal()" spellcheck='false'>
+    </div>
 
 
-	<div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
-		<label class="gf-form-label width-10">Percentiles</label>
-		<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
-	</div>
 
 
-	<div class="gf-form offset-width-7" ng-if="agg.type === 'cardinality'">
-		<label class="gf-form-label width-10">Precision threshold</label>
-		<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision_threshold" ng-blur="onChange()"></input>
-	</div>
+    <div class="gf-form offset-width-7" ng-repeat="setting in modelSettings">
+      <label class="gf-form-label width-10">{{setting.text}}</label>
+      <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.settings[setting.value]" ng-blur="onChangeInternal()" spellcheck='false'>
+    </div>
 
 
-	<div ng-if="agg.type === 'extended_stats'">
-		<gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch>
+    <gf-form-switch ng-if="agg.settings.model == 'holt_winters'" class="gf-form offset-width-7" label="Pad" label-class="width-10" checked="agg.settings.settings.pad" on-change="onChangeInternal()"></gf-form-switch>
+    <gf-form-switch ng-if="agg.settings.model.match('ewma|holt_winters|holt') !== null" class="gf-form offset-width-7" label="Minimize" label-class="width-10" checked="agg.settings.minimize" on-change="onChangeInternal()"></gf-form-switch>
+  </div>
 
 
-		<div class="gf-form offset-width-7">
-			<label class="gf-form-label width-10">Sigma</label>
-			<input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
-		</div>
-	</div>
+  <div class="gf-form offset-width-7" ng-if="agg.type === 'percentiles'">
+    <label class="gf-form-label width-10">Percentiles</label>
+    <input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
+  </div>
 
 
-	<div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript">
-		<label class="gf-form-label width-10">Script</label>
-		<input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
-	</div>
+  <div class="gf-form offset-width-7" ng-if="agg.type === 'cardinality'">
+    <label class="gf-form-label width-10">Precision threshold</label>
+    <input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.precision_threshold" ng-blur="onChange()"></input>
+  </div>
+
+  <div ng-if="agg.type === 'extended_stats'">
+    <gf-form-switch ng-repeat="stat in extendedStats" class="gf-form offset-width-7" label="{{stat.text}}" label-class="width-10" checked="agg.meta[stat.value]" on-change="onChangeInternal()"></gf-form-switch>
+
+    <div class="gf-form offset-width-7">
+      <label class="gf-form-label width-10">Sigma</label>
+      <input type="number" class="gf-form-input max-width-12" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
+    </div>
+  </div>
+
+  <div class="gf-form offset-width-7" ng-if="aggDef.supportsInlineScript">
+    <label class="gf-form-label width-10">Script</label>
+    <input type="text" class="gf-form-input max-width-12" empty-to-null ng-model="agg.inlineScript" ng-blur="onChangeInternal()" spellcheck='false' placeholder="_value * 1">
+  </div>
 
 
-	<div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing">
+  <div class="gf-form offset-width-7" ng-if="aggDef.supportsMissing">
 		<label class="gf-form-label width-10">
 		<label class="gf-form-label width-10">
 			Missing
 			Missing
 			<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>
 			<tip>The missing parameter defines how documents that are missing a value should be treated. By default they will be ignored but it is also possible to treat them as if they had a value</tip>

+ 41 - 1
public/app/plugins/datasource/elasticsearch/query_def.js

@@ -69,17 +69,44 @@ function (_) {
       {text: '1d', value: '1d'},
       {text: '1d', value: '1d'},
     ],
     ],
 
 
+    movingAvgModelOptions: [
+      {text: 'Simple', value: 'simple'},
+      {text: 'Linear', value: 'linear'},
+      {text: 'Exponentially Weighted', value: 'ewma'},
+      {text: 'Holt Linear', value: 'holt'},
+      {text: 'Holt Winters', value: 'holt_winters'},
+    ],
+
     pipelineOptions: {
     pipelineOptions: {
       'moving_avg' : [
       'moving_avg' : [
         {text: 'window', default: 5},
         {text: 'window', default: 5},
         {text: 'model', default: 'simple'},
         {text: 'model', default: 'simple'},
-        {text: 'predict', default: undefined}
+        {text: 'predict', default: undefined},
+        {text: 'minimize', default: false},
       ],
       ],
       'derivative': [
       'derivative': [
         {text: 'unit', default: undefined},
         {text: 'unit', default: undefined},
       ]
       ]
     },
     },
 
 
+    movingAvgModelSettings: {
+      'simple' : [],
+      'linear' : [],
+      'ewma' : [
+        {text: "Alpha", value: "alpha", default: undefined}],
+      'holt' : [
+        {text: "Alpha", value: "alpha",  default: undefined},
+        {text: "Beta", value: "beta",  default: undefined},
+       ],
+      'holt_winters' : [
+        {text: "Alpha", value: "alpha", default: undefined},
+        {text: "Beta", value: "beta", default: undefined},
+        {text: "Gamma", value: "gamma", default: undefined},
+        {text: "Period", value: "period", default: undefined},
+        {text: "Pad", value: "pad", default: undefined, isCheckbox: true},
+       ],
+    },
+
     getMetricAggTypes: function(esVersion) {
     getMetricAggTypes: function(esVersion) {
       return _.filter(this.metricAggTypes, function(f) {
       return _.filter(this.metricAggTypes, function(f) {
         if (f.minVersion) {
         if (f.minVersion) {
@@ -119,6 +146,19 @@ function (_) {
       return result;
       return result;
     },
     },
 
 
+    getMovingAvgSettings: function(model, filtered) {
+      var filteredResult = [];
+      if (filtered) {
+        _.each(this.movingAvgModelSettings[model], function(setting) {
+          if (!(setting.isCheckbox)) {
+            filteredResult.push(setting);
+          }
+        });
+        return filteredResult;
+      }
+      return this.movingAvgModelSettings[model];
+    },
+
     getOrderByOptions: function(target) {
     getOrderByOptions: function(target) {
       var self = this;
       var self = this;
       var metricRefs = [];
       var metricRefs = [];