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

This commit is contained in:
Torkel Ödegaard
2015-12-09 16:46:29 +01:00
7 changed files with 208 additions and 7 deletions

View File

@@ -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;
}

View File

@@ -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() {

View File

@@ -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">

View File

@@ -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;
});

View File

@@ -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 = [];

View File

@@ -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);
});
});

View File

@@ -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);
});
});
});
});