Commit 4ed7612e by Chris Cowan Committed by GitHub

Elasticsearch: Add Moving Function Pipeline Aggregation (#28131)

* Elasticsearch: Add Moving Function Pipeline Aggregation

* Removing support for moving average in 7.x

* Fixing getMetricAggTypes to handle undefined esVersion

* Adding moving_fn to go code

* Update public/app/plugins/datasource/elasticsearch/metric_agg.ts

Co-authored-by: Giordano Ricci <grdnricci@gmail.com>

* Adding test for esversion 70

* Removing default value for script, adding placeholder instead

* Fixing formatting

* Adding code for handling missing or obsolete aggregations

Co-authored-by: Giordano Ricci <grdnricci@gmail.com>
parent af8589a4
...@@ -45,6 +45,7 @@ var metricAggType = map[string]string{ ...@@ -45,6 +45,7 @@ var metricAggType = map[string]string{
"percentiles": "Percentiles", "percentiles": "Percentiles",
"cardinality": "Unique Count", "cardinality": "Unique Count",
"moving_avg": "Moving Average", "moving_avg": "Moving Average",
"moving_fn": "Moving Function",
"cumulative_sum": "Cumulative Sum", "cumulative_sum": "Cumulative Sum",
"derivative": "Derivative", "derivative": "Derivative",
"bucket_script": "Bucket Script", "bucket_script": "Bucket Script",
...@@ -64,6 +65,7 @@ var extendedStats = map[string]string{ ...@@ -64,6 +65,7 @@ var extendedStats = map[string]string{
var pipelineAggType = map[string]string{ var pipelineAggType = map[string]string{
"moving_avg": "moving_avg", "moving_avg": "moving_avg",
"moving_fn": "moving_fn",
"cumulative_sum": "cumulative_sum", "cumulative_sum": "cumulative_sum",
"derivative": "derivative", "derivative": "derivative",
"bucket_script": "bucket_script", "bucket_script": "bucket_script",
......
...@@ -44,6 +44,7 @@ export class ElasticMetricAggCtrl { ...@@ -44,6 +44,7 @@ export class ElasticMetricAggCtrl {
$scope.settingsLinkText = ''; $scope.settingsLinkText = '';
$scope.variablesLinkText = ''; $scope.variablesLinkText = '';
$scope.aggDef = _.find($scope.metricAggTypes, { value: $scope.agg.type }); $scope.aggDef = _.find($scope.metricAggTypes, { value: $scope.agg.type });
$scope.isValidAgg = $scope.aggDef != null;
if (queryDef.isPipelineAgg($scope.agg.type)) { if (queryDef.isPipelineAgg($scope.agg.type)) {
if (queryDef.isPipelineAggWithMultipleBucketPaths($scope.agg.type)) { if (queryDef.isPipelineAggWithMultipleBucketPaths($scope.agg.type)) {
...@@ -105,6 +106,13 @@ export class ElasticMetricAggCtrl { ...@@ -105,6 +106,13 @@ export class ElasticMetricAggCtrl {
$scope.updateMovingAvgModelSettings(); $scope.updateMovingAvgModelSettings();
break; break;
} }
case 'moving_fn': {
const movingFunctionOptions = queryDef.getPipelineOptions($scope.agg);
_.each(movingFunctionOptions, opt => {
$scope.agg.settings[opt.text] = $scope.agg.settings[opt.text] || opt.default;
});
break;
}
case 'raw_document': case 'raw_document':
case 'raw_data': { case 'raw_data': {
$scope.agg.settings.size = $scope.agg.settings.size || 500; $scope.agg.settings.size = $scope.agg.settings.size || 500;
...@@ -115,7 +123,7 @@ export class ElasticMetricAggCtrl { ...@@ -115,7 +123,7 @@ export class ElasticMetricAggCtrl {
break; break;
} }
} }
if ($scope.aggDef.supportsInlineScript) { if ($scope.aggDef?.supportsInlineScript) {
// I know this stores the inline script twice // I know this stores the inline script twice
// but having it like this simplifes the query_builder // but having it like this simplifes the query_builder
const inlineScript = $scope.agg.inlineScript; const inlineScript = $scope.agg.inlineScript;
......
...@@ -10,12 +10,18 @@ ...@@ -10,12 +10,18 @@
</label> </label>
</div> </div>
<div class="gf-form"> <div class="gf-form" ng-if="isValidAgg">
<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="width-10"></metric-segment-model> <metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()" custom="false" css-class="width-10"></metric-segment-model>
<metric-segment-model ng-if="aggDef.requiresField" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment-model> <metric-segment-model ng-if="aggDef.requiresField" property="agg.field" get-options="getFieldsInternal()" on-change="onChange()" css-class="width-12"></metric-segment-model>
<metric-segment-model ng-if="aggDef.isPipelineAgg && !aggDef.supportsMultipleBucketPaths" property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="width-12"></metric-segment-model> <metric-segment-model ng-if="aggDef.isPipelineAgg && !aggDef.supportsMultipleBucketPaths" property="agg.pipelineAgg" options="pipelineAggOptions" on-change="onChangeInternal()" custom="false" css-class="width-12"></metric-segment-model>
</div> </div>
<div class="gf-form gf-form--grow" ng-if="!isValidAgg">
<label class="gf-form-label gf-form-label--grow">
<em>This aggregation is no longer supported by your version of Elasticsearch</em>
</label>
</div>
<div class="gf-form gf-form--grow" ng-if="aggDef.isPipelineAgg && aggDef.supportsMultipleBucketPaths"> <div class="gf-form gf-form--grow" ng-if="aggDef.isPipelineAgg && aggDef.supportsMultipleBucketPaths">
<label class="gf-form-label gf-form-label--grow"> <label class="gf-form-label gf-form-label--grow">
<a ng-click="toggleVariables()"> <a ng-click="toggleVariables()">
...@@ -26,7 +32,7 @@ ...@@ -26,7 +32,7 @@
</label> </label>
</div> </div>
<div class="gf-form gf-form--grow"> <div class="gf-form gf-form--grow" ng-if="isValidAgg">
<label class="gf-form-label gf-form-label--grow"> <label class="gf-form-label gf-form-label--grow">
<a ng-click="toggleOptions()" ng-if="settingsLinkText"> <a ng-click="toggleOptions()" ng-if="settingsLinkText">
<icon name="'angle-down'" ng-show="showOptions"></icon> <icon name="'angle-down'" ng-show="showOptions"></icon>
...@@ -71,6 +77,25 @@ ...@@ -71,6 +77,25 @@
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.format" ng-blur="onChangeInternal()" spellcheck='false'> <input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.format" ng-blur="onChangeInternal()" spellcheck='false'>
</div> </div>
<div ng-if="agg.type === 'moving_fn'">
<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">
<label class="gf-form-label width-10">Script</label>
<input type="text" class="gf-form-input max-width-12" ng-model="agg.settings.script" ng-blur="onChangeInternal()" spellcheck='false' placeholder="eg. MovingFunctions.unweightedAvg(values)">
</div>
<div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Shift</label>
<input type="number" class="gf-form-input max-width-12" ng-model="agg.settings.shift" ng-blur="onChangeInternal()" spellcheck='false'>
</div>
</div>
<div ng-if="agg.type === 'moving_avg'"> <div ng-if="agg.type === 'moving_avg'">
<div class="gf-form offset-width-7"> <div class="gf-form offset-width-7">
<label class="gf-form-label width-10">Model</label> <label class="gf-form-label width-10">Model</label>
......
...@@ -57,6 +57,14 @@ export const metricAggTypes = [ ...@@ -57,6 +57,14 @@ export const metricAggTypes = [
requiresField: false, requiresField: false,
isPipelineAgg: true, isPipelineAgg: true,
minVersion: 2, minVersion: 2,
maxVersion: 60,
},
{
text: 'Moving Function',
value: 'moving_fn',
requiresField: false,
isPipelineAgg: true,
minVersion: 70,
}, },
{ {
text: 'Derivative', text: 'Derivative',
...@@ -151,6 +159,7 @@ export const pipelineOptions: any = { ...@@ -151,6 +159,7 @@ export const pipelineOptions: any = {
{ text: 'predict', default: undefined }, { text: 'predict', default: undefined },
{ text: 'minimize', default: false }, { text: 'minimize', default: false },
], ],
moving_fn: [{ text: 'window', default: 5 }, { text: 'script' }],
derivative: [{ text: 'unit', default: undefined }], derivative: [{ text: 'unit', default: undefined }],
cumulative_sum: [{ text: 'format', default: undefined }], cumulative_sum: [{ text: 'format', default: undefined }],
bucket_script: [], bucket_script: [],
...@@ -175,8 +184,10 @@ export const movingAvgModelSettings: any = { ...@@ -175,8 +184,10 @@ export const movingAvgModelSettings: any = {
export function getMetricAggTypes(esVersion: any) { export function getMetricAggTypes(esVersion: any) {
return _.filter(metricAggTypes, f => { return _.filter(metricAggTypes, f => {
if (f.minVersion) { if (f.minVersion || f.maxVersion) {
return f.minVersion <= esVersion; const minVersion = f.minVersion || 0;
const maxVersion = f.maxVersion || esVersion;
return esVersion >= minVersion && esVersion <= maxVersion;
} else { } else {
return true; return true;
} }
......
...@@ -162,8 +162,21 @@ describe('ElasticQueryDef', () => { ...@@ -162,8 +162,21 @@ describe('ElasticQueryDef', () => {
}); });
describe('using esversion 5', () => { describe('using esversion 5', () => {
const metricAggTypes = queryDef.getMetricAggTypes(5);
test('should get pipeline aggs', () => { test('should get pipeline aggs', () => {
expect(queryDef.getMetricAggTypes(5).length).toBe(15); expect(metricAggTypes.length).toBe(15);
});
});
describe('using esversion 70', () => {
const metricAggTypes = queryDef.getMetricAggTypes(70);
test('should get pipeline aggs', () => {
expect(metricAggTypes.length).toBe(15);
});
test('should get pipeline aggs with moving function', () => {
expect(metricAggTypes.some(m => m.value === 'moving_fn')).toBeTruthy();
});
test('should get pipeline aggs without moving average', () => {
expect(metricAggTypes.some(m => m.value === 'moving_avg')).toBeFalsy();
}); });
}); });
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment