feat(elasticsearch): worked on percentiles metric aggregator in editor and in elasticsearch response processing

This commit is contained in:
Torkel Ödegaard 2015-09-05 18:31:42 +02:00
parent 3e9aca3ed4
commit f942ec952e
9 changed files with 158 additions and 32 deletions

View File

@ -33,24 +33,26 @@ function (angular, _, queryDef) {
};
$scope.validateModel = function() {
$scope.index = _.indexOf(bucketAggs, $scope.agg);
$scope.isFirst = $scope.index === 0;
$scope.isLast = $scope.index === bucketAggs.length - 1;
$scope.aggOptionsString = "";
$scope.settingsLinkText = "";
if ($scope.agg.type === "terms") {
$scope.agg.order = $scope.agg.order || "desc";
$scope.agg.size = $scope.agg.size || "0";
$scope.agg.orderBy = $scope.agg.orderBy || "_count";
$scope.agg.orderBy = $scope.agg.orderBy || "_term";
if ($scope.agg.size === '0') {
$scope.aggOptionsString = "";
$scope.settingsLinkText = "";
} else {
$scope.aggOptionsString = queryDef.describeOrder($scope.agg.order) + ' ' + $scope.agg.size + ', '
$scope.settingsLinkText = queryDef.describeOrder($scope.agg.order) + ' ' + $scope.agg.size + ', '
}
$scope.aggOptionsString += 'Order by: ' + queryDef.describeOrderBy($scope.agg.orderBy, $scope.target);
$scope.settingsLinkText += 'Order by: ' + queryDef.describeOrderBy($scope.agg.orderBy, $scope.target);
if ($scope.agg.size === '0') {
$scope.aggOptionsString += ' (' + $scope.agg.order + ')';
$scope.settingsLinkText += ' (' + $scope.agg.order + ')';
}
}

View File

@ -175,10 +175,18 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
// This is quite complex
// neeed to recurise down the nested buckets to build series
ElasticDatasource.prototype._processBuckets = function(aggs, target, series, level, parentName) {
var seriesName, value, metric, i, y, bucket, childBucket, aggDef, esAgg;
var seriesName, value, metric, i, y, z, bucket, childBucket, aggDef, esAgg;
var buckets;
var dataFound = 0;
function addMetricPoint(seriesName, value, time) {
var current = series[seriesName];
if (!current) {
current = series[seriesName] = {target: seriesName, datapoints: []};
}
current.datapoints.push([value, time]);
}
aggDef = target.bucketAggs[level];
esAgg = aggs[aggDef.id];
@ -191,16 +199,27 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
metric = target.metrics[y];
seriesName = parentName;
if (metric.type === 'count') {
seriesName += ' count';
value = bucket.doc_count;
} else {
seriesName += ' ' + metric.field + ' ' + metric.type;
value = bucket[metric.id].value;
switch(metric.type) {
case 'count': {
seriesName += ' count';
value = bucket.doc_count;
addMetricPoint(seriesName, value, bucket.key);
break;
}
case 'percentiles': {
var values = bucket[metric.id].values;
for (var prop in values) {
addMetricPoint(seriesName + ' ' + prop, values[prop], bucket.key)
}
break;
}
default: {
seriesName += ' ' + metric.field + ' ' + metric.type;
value = bucket[metric.id].value;
addMetricPoint(seriesName, value, bucket.key);
break;
}
}
var serie = series[seriesName] = series[seriesName] || {target: seriesName, datapoints: []};
serie.datapoints.push([value, bucket.key]);
}
}
else {

View File

@ -8,27 +8,43 @@ function (angular, _, queryDef) {
var module = angular.module('grafana.directives');
module.controller('ElasticMetricAggCtrl', function($scope, uiSegmentSrv, $q) {
module.controller('ElasticMetricAggCtrl', function($scope, uiSegmentSrv, $q, $rootScope) {
var metricAggs = $scope.target.metrics;
$scope.metricAggTypes = queryDef.metricAggTypes;
$scope.init = function() {
$scope.agg = metricAggs[$scope.index];
$scope.validateModel();
}
$rootScope.onAppEvent('elastic-query-updated', function() {
$scope.index = _.indexOf(metricAggs, $scope.agg);
$scope.isFirst = $scope.index === 0;
$scope.isSingle = metricAggs.length === 1;
$scope.validateModel();
});
$scope.validateModel = function() {
if (!$scope.agg.field) {
$scope.agg.field = 'select field';
}
}
$scope.$watchCollection("target.metrics", function() {
$scope.isFirst = $scope.index === 0;
$scope.isLast = $scope.index === metricAggs.length - 1;
$scope.isSingle = metricAggs.length === 1;
});
if ($scope.agg.type === 'percentiles') {
$scope.agg.settings.percents = $scope.agg.settings.percents || [25,50,75,95,99];
$scope.settingsLinkText = 'values: ' + $scope.agg.settings.percents.join(',');
}
}
$scope.toggleOptions = function() {
$scope.showOptions = !$scope.showOptions;
}
};
$scope.onTypeChange = function() {
$scope.agg.settings = {};
$scope.onChange();
};
$scope.addMetricAgg = function() {
var addIndex = metricAggs.length;

View File

@ -8,8 +8,8 @@
<metric-segment-model property="agg.type" options="bucketAggTypes" on-change="onChangeInternal()"></metric-segment-model>
<metric-segment-model property="agg.field" get-options="getFields()" on-change="onChangeInternal()"></metric-segment>
</li>
<li class="tight-form-item tight-form-align" ng-if="aggOptionsString">
<a ng-click="toggleOptions()">{{aggOptionsString}}</a>
<li class="tight-form-item tight-form-align" ng-if="settingsLinkText">
<a ng-click="toggleOptions()">{{settingsLinkText}}</a>
</li>
</ul>

View File

@ -4,13 +4,13 @@
Metric
</li>
<li>
<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onChange()"></metric-segment-model>
<metric-segment-model property="agg.type" options="metricAggTypes" on-change="onTypeChange()"></metric-segment-model>
</li>
<li ng-if="agg.type !== 'count'">
<metric-segment-model property="agg.field" get-options="getFields()" on-change="onChange()"></metric-segment>
</li>
<li class="tight-form-item tight-form-align" ng-if="aggOptionsString">
<a ng-click="toggleOptions()">{{aggOptionsString}}</a>
<li class="tight-form-item tight-form-align" ng-if="settingsLinkText">
<a ng-click="toggleOptions()">{{settingsLinkText}}</a>
</li>
</ul>
@ -25,4 +25,18 @@
<div class="clearfix"></div>
</div>
<div class="tight-form" ng-if="showOptions">
<div style="margin: 20px 0 20px 148px;display: inline-block">
<div class="tight-form last" ng-if="agg.type === 'percentiles'">
<ul class="tight-form-list">
<li class="tight-form-item">
Percentiles
</li>
<li>
<input type="text" class="input-xlarge tight-form-input last" ng-model="agg.settings.percents" array-join ng-blur="onChange()"></input>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div>
</div>

View File

@ -48,7 +48,7 @@ function (angular) {
esAgg["date_histogram"] = {
"interval": target.interval || "$interval",
"field": aggDef.field,
"min_doc_count": 0,
"min_doc_count": 1,
"extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
};
break;
@ -92,8 +92,15 @@ function (angular) {
continue;
}
var metricAgg = {field: metric.field};
for (var prop in metric.settings) {
if (metric.settings.hasOwnProperty(prop)) {
metricAgg[prop] = metric.settings[prop];
}
}
var aggField = {};
aggField[metric.type] = {field: metric.field};
aggField[metric.type] = metricAgg;
nestedAggs.aggs[metric.id] = aggField;
}

View File

@ -12,6 +12,7 @@ function (_) {
{text: "Max of", value: 'max' },
{text: "Min of", value: 'min' },
{text: "Standard Deviations", value: 'std_dev' },
{text: "Percentiles", value: 'percentiles' },
],
bucketAggTypes: [

View File

@ -65,6 +65,31 @@ define([
expect(secondLevel.aggs["5"].avg.field).to.be("@value");
});
it('with metric percentiles', function() {
var builder = new ElasticQueryBuilder();
var query = builder.build({
metrics: [
{
id: '1',
type: 'percentiles',
field: '@load_time',
settings: {
percents: [1,2,3,4]
}
}
],
bucketAggs: [
{type: 'date_histogram', field: '@timestamp', id: '3'}
],
}, 100, 1000);
var firstLevel = query.aggs["3"];
expect(firstLevel.aggs["1"].percentiles.field).to.be("@load_time");
expect(firstLevel.aggs["1"].percentiles.percents).to.eql([1,2,3,4]);
});
});
});

View File

@ -145,6 +145,48 @@ define([
});
});
describe('with percentiles ', function() {
var result;
beforeEach(function() {
result = ctx.ds._processTimeSeries([{
refId: 'A',
metrics: [{type: 'percentiles', settings: {percents: [75, 90]}, id: '1'}],
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '3'}],
}], {
responses: [{
aggregations: {
"3": {
buckets: [
{
"1": {values: {"75": 3.3, "90": 5.5}},
doc_count: 10,
key: 1000
},
{
"1": {values: {"75": 2.3, "90": 4.5}},
doc_count: 15,
key: 2000
}
]
}
}
}]
});
});
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('A 75');
expect(result.data[1].target).to.be('A 90');
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);
});
});
});
});
});