mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(elasticsearch): extended stats like std deviation now works, and sigma option as well, added unique count (cardinality as well, #1034
This commit is contained in:
parent
efc3def7f2
commit
3999a3caa2
@ -23,7 +23,7 @@
|
||||
"laxcomma": true,
|
||||
"sub": true,
|
||||
"unused": true,
|
||||
"maxdepth": 5,
|
||||
"maxdepth": 6,
|
||||
"maxlen": 140,
|
||||
|
||||
"globals": {
|
||||
@ -32,4 +32,4 @@
|
||||
"Chromath": false,
|
||||
"setImmediate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,15 +63,16 @@ function (angular, kbn) {
|
||||
restrict: 'E',
|
||||
link: function(scope, elem, attrs) {
|
||||
var text = $interpolate(attrs.text)(scope);
|
||||
var model = $interpolate(attrs.model)(scope);
|
||||
var ngchange = attrs.change ? (' ng-change="' + attrs.change + '"') : '';
|
||||
var tip = attrs.tip ? (' <tip>' + attrs.tip + '</tip>') : '';
|
||||
var label = '<label for="' + scope.$id + attrs.model + '" class="checkbox-label">' +
|
||||
var label = '<label for="' + scope.$id + model + '" class="checkbox-label">' +
|
||||
text + tip + '</label>';
|
||||
|
||||
var template = '<input class="cr1" id="' + scope.$id + attrs.model + '" type="checkbox" ' +
|
||||
' ng-model="' + attrs.model + '"' + ngchange +
|
||||
' ng-checked="' + attrs.model + '"></input>' +
|
||||
' <label for="' + scope.$id + attrs.model + '" class="cr1"></label>';
|
||||
var template = '<input class="cr1" id="' + scope.$id + model + '" type="checkbox" ' +
|
||||
' ng-model="' + model + '"' + ngchange +
|
||||
' ng-checked="' + model + '"></input>' +
|
||||
' <label for="' + scope.$id + model + '" class="cr1"></label>';
|
||||
|
||||
template = label + template;
|
||||
elem.replaceWith($compile(angular.element(template))(scope));
|
||||
|
@ -57,7 +57,7 @@
|
||||
<div class="editor-row">
|
||||
<div class="tight-form-section">
|
||||
<h5>Toggles</h5>
|
||||
<div class="tight-form">
|
||||
<div class="tight-form last">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Editable" model="dashboard.editable"></editor-checkbox>
|
||||
@ -65,7 +65,7 @@
|
||||
<li class="tight-form-item">
|
||||
<editor-checkbox text="Hide Controls (CTRL+H)" model="dashboard.hideControls"></editor-checkbox>
|
||||
</li>
|
||||
<li class="tight-form-item">
|
||||
<li class="tight-form-item last">
|
||||
<editor-checkbox text="Shared Crosshair (CTRL+O)" model="dashboard.sharedCrosshair"></editor-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -5,10 +5,11 @@ define([
|
||||
'kbn',
|
||||
'./queryBuilder',
|
||||
'./indexPattern',
|
||||
'./elasticResponse',
|
||||
'./queryCtrl',
|
||||
'./directives'
|
||||
],
|
||||
function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern) {
|
||||
function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticResponse) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
@ -174,8 +175,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern) {
|
||||
payload = payload.replace(/\$maxDataPoints/g, options.maxDataPoints);
|
||||
payload = templateSrv.replace(payload, options.scopedVars);
|
||||
|
||||
var processTimeSeries = _.bind(this._processTimeSeries, this, sentTargets);
|
||||
return this._post('/_msearch?search_type=count', payload).then(processTimeSeries);
|
||||
return this._post('/_msearch?search_type=count', payload).then(function(res) {
|
||||
return new ElasticResponse(sentTargets, res).getTimeSeries();
|
||||
});
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.translateTime = function(date) {
|
||||
@ -186,94 +188,6 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern) {
|
||||
return date.getTime();
|
||||
};
|
||||
|
||||
// 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, aggDef, esAgg;
|
||||
|
||||
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];
|
||||
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
|
||||
// if last agg collect series
|
||||
if (level === target.bucketAggs.length - 1) {
|
||||
for (y = 0; y < target.metrics.length; y++) {
|
||||
metric = target.metrics[y];
|
||||
seriesName = parentName;
|
||||
|
||||
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;
|
||||
}
|
||||
case 'extended_stats': {
|
||||
var stats = bucket[metric.id];
|
||||
|
||||
for (var statIndex in metric.stats) {
|
||||
var statName = metric.stats[statIndex];
|
||||
addMetricPoint(seriesName + ' ' + statName, stats[statName], bucket.key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
seriesName += ' ' + metric.field + ' ' + metric.type;
|
||||
value = bucket[metric.id].value;
|
||||
addMetricPoint(seriesName, value, bucket.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._processBuckets(bucket, target, series, level+1, parentName + ' ' + bucket.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype._processTimeSeries = function(targets, results) {
|
||||
var series = [];
|
||||
|
||||
for (var i = 0; i < results.responses.length; i++) {
|
||||
var response = results.responses[i];
|
||||
if (response.error) {
|
||||
throw { message: response.error };
|
||||
}
|
||||
|
||||
var aggregations = response.aggregations;
|
||||
var target = targets[i];
|
||||
var querySeries = {};
|
||||
|
||||
this._processBuckets(aggregations, target, querySeries, 0, target.refId);
|
||||
|
||||
for (var prop in querySeries) {
|
||||
if (querySeries.hasOwnProperty(prop)) {
|
||||
series.push(querySeries[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { data: series };
|
||||
};
|
||||
|
||||
ElasticDatasource.prototype.metricFindQuery = function() {
|
||||
return this._get('/_mapping').then(function(res) {
|
||||
var fields = {};
|
||||
|
103
public/app/plugins/datasource/elasticsearch/elasticResponse.js
Normal file
103
public/app/plugins/datasource/elasticsearch/elasticResponse.js
Normal file
@ -0,0 +1,103 @@
|
||||
define([
|
||||
],
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
function ElasticResponse(targets, response) {
|
||||
this.targets = targets;
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
// This is quite complex
|
||||
// neeed to recurise down the nested buckets to build series
|
||||
ElasticResponse.prototype.processBuckets = function(aggs, target, series, level, parentName) {
|
||||
var seriesName, value, metric, i, y, bucket, aggDef, esAgg;
|
||||
|
||||
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];
|
||||
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
|
||||
// if last agg collect series
|
||||
if (level === target.bucketAggs.length - 1) {
|
||||
for (y = 0; y < target.metrics.length; y++) {
|
||||
metric = target.metrics[y];
|
||||
seriesName = parentName;
|
||||
|
||||
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;
|
||||
}
|
||||
case 'extended_stats': {
|
||||
var stats = bucket[metric.id];
|
||||
stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper;
|
||||
stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower;
|
||||
|
||||
for (var statName in metric.meta) {
|
||||
if (metric.meta[statName]) {
|
||||
addMetricPoint(seriesName + ' ' + statName, stats[statName], bucket.key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
seriesName += ' ' + metric.field + ' ' + metric.type;
|
||||
value = bucket[metric.id].value;
|
||||
addMetricPoint(seriesName, value, bucket.key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.processBuckets(bucket, target, series, level+1, parentName + ' ' + bucket.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElasticResponse.prototype.getTimeSeries = function() {
|
||||
var series = [];
|
||||
|
||||
for (var i = 0; i < this.response.responses.length; i++) {
|
||||
var response = this.response.responses[i];
|
||||
if (response.error) {
|
||||
throw { message: response.error };
|
||||
}
|
||||
|
||||
var aggregations = response.aggregations;
|
||||
var target = this.targets[i];
|
||||
var querySeries = {};
|
||||
|
||||
this.processBuckets(aggregations, target, querySeries, 0, target.refId);
|
||||
|
||||
for (var prop in querySeries) {
|
||||
if (querySeries.hasOwnProperty(prop)) {
|
||||
series.push(querySeries[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { data: series };
|
||||
};
|
||||
|
||||
return ElasticResponse;
|
||||
});
|
@ -12,6 +12,7 @@ function (angular, _, queryDef) {
|
||||
var metricAggs = $scope.target.metrics;
|
||||
|
||||
$scope.metricAggTypes = queryDef.metricAggTypes;
|
||||
$scope.extendedStats = queryDef.extendedStats;
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.agg = metricAggs[$scope.index];
|
||||
@ -40,8 +41,14 @@ function (angular, _, queryDef) {
|
||||
break;
|
||||
}
|
||||
case 'extended_stats': {
|
||||
$scope.agg.stats = $scope.agg.stats || ['std_deviation'];
|
||||
$scope.settingsLinkText = 'Stats: ' + $scope.agg.stats.join(',');
|
||||
var stats = _.reduce($scope.agg.meta, function(memo, val, key) {
|
||||
if (val) {
|
||||
var def = _.findWhere($scope.extendedStats, {value: key});
|
||||
memo.push(def.text);
|
||||
}
|
||||
return memo;
|
||||
}, []);
|
||||
$scope.settingsLinkText = 'Stats: ' + stats.join(', ');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -52,6 +59,9 @@ function (angular, _, queryDef) {
|
||||
|
||||
$scope.onTypeChange = function() {
|
||||
$scope.agg.settings = {};
|
||||
$scope.agg.meta = {};
|
||||
$scope.showOptions = false;
|
||||
|
||||
$scope.validateModel();
|
||||
$scope.onChange();
|
||||
};
|
||||
|
@ -27,7 +27,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="showOptions">
|
||||
<div style="tight-form-inner-box" ng-if="agg.type === 'terms'">
|
||||
<div class="tight-form-inner-box" ng-if="agg.type === 'terms'">
|
||||
<div class="tight-form">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 60px">
|
||||
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
|
||||
<div class="tight-form" ng-if="showOptions">
|
||||
<div style="margin: 20px 0 20px 148px;display: inline-block">
|
||||
<div class="tight-form-inner-box">
|
||||
<div class="tight-form last" ng-if="agg.type === 'percentiles'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item">
|
||||
@ -38,5 +38,29 @@
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div ng-if="agg.type === 'extended_stats'">
|
||||
<div class="tight-form" ng-repeat="stat in extendedStats">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px">
|
||||
{{stat.text}}
|
||||
</li>
|
||||
<li class="tight-form-item last">
|
||||
<editor-checkbox text="" model="agg.meta.{{stat.value}}" change="onChange()"></editor-checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tight-form last" ng-if="agg.type === 'extended_stats'">
|
||||
<ul class="tight-form-list">
|
||||
<li class="tight-form-item" style="width: 100px">
|
||||
Sigma
|
||||
</li>
|
||||
<li>
|
||||
<input type="number" class="input-mini tight-form-input last" placeholder="3" ng-model="agg.settings.sigma" ng-blur="onChange()"></input>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -105,7 +105,7 @@ function (angular) {
|
||||
|
||||
var metricAgg = {field: metric.field};
|
||||
for (var prop in metric.settings) {
|
||||
if (metric.settings.hasOwnProperty(prop)) {
|
||||
if (metric.settings.hasOwnProperty(prop) && metric.settings[prop] !== null) {
|
||||
metricAgg[prop] = metric.settings[prop];
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ function (_) {
|
||||
{text: "Min of", value: 'min' },
|
||||
{text: "Extended Stats", value: 'extended_stats' },
|
||||
{text: "Percentiles", value: 'percentiles' },
|
||||
{text: "Unique Count", value: "cardinality" }
|
||||
],
|
||||
|
||||
bucketAggTypes: [
|
||||
@ -41,6 +42,17 @@ function (_) {
|
||||
{text: "20", value: '20' },
|
||||
],
|
||||
|
||||
extendedStats: [
|
||||
{text: 'Avg', value: 'avg'},
|
||||
{text: 'Min', value: 'min'},
|
||||
{text: 'Max', value: 'max'},
|
||||
{text: 'Sum', value: 'sum'},
|
||||
{text: 'Count', value: 'count'},
|
||||
{text: 'Std Dev', value: 'std_deviation'},
|
||||
{text: 'Std Dev Upper', value: 'std_deviation_bounds_upper'},
|
||||
{text: 'Std Dev Lower', value: 'std_deviation_bounds_lower'},
|
||||
],
|
||||
|
||||
getOrderByOptions: function(target) {
|
||||
var self = this;
|
||||
var metricRefs = [];
|
||||
|
239
public/test/specs/elasticsearch-response-specs.js
Normal file
239
public/test/specs/elasticsearch-response-specs.js
Normal file
@ -0,0 +1,239 @@
|
||||
define([
|
||||
'plugins/datasource/elasticsearch/elasticResponse',
|
||||
], function(ElasticResponse) {
|
||||
'use strict';
|
||||
|
||||
describe('ElasticResponse', function() {
|
||||
var targets;
|
||||
var response;
|
||||
var result;
|
||||
|
||||
describe('simple query and count', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}],
|
||||
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '2'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"2": {
|
||||
buckets: [
|
||||
{
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
},
|
||||
{
|
||||
doc_count: 15,
|
||||
key: 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
it('should return 1 series', function() {
|
||||
expect(result.data.length).to.be(1);
|
||||
expect(result.data[0].datapoints.length).to.be(2);
|
||||
expect(result.data[0].datapoints[0][0]).to.be(10);
|
||||
expect(result.data[0].datapoints[0][1]).to.be(1000);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('simple query count & avg aggregation', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}, {type: 'avg', field: 'value', id: '2'}],
|
||||
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"3": {
|
||||
buckets: [
|
||||
{
|
||||
"2": {value: 88},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
},
|
||||
{
|
||||
"2": {value: 99},
|
||||
doc_count: 15,
|
||||
key: 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
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].datapoints[0][0]).to.be(10);
|
||||
expect(result.data[0].datapoints[0][1]).to.be(1000);
|
||||
|
||||
expect(result.data[1].target).to.be("A value avg");
|
||||
expect(result.data[1].datapoints[0][0]).to.be(88);
|
||||
expect(result.data[1].datapoints[1][0]).to.be(99);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('single group by query', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}],
|
||||
bucketAggs: [{type: 'terms', field: 'host', id: '2'}, {type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"2": {
|
||||
buckets: [
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{doc_count: 1, key: 1000},
|
||||
{doc_count: 3, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 4,
|
||||
key: 'server1',
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{doc_count: 2, key: 1000},
|
||||
{doc_count: 8, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 10,
|
||||
key: 'server2',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
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 server1 count');
|
||||
expect(result.data[1].target).to.be('A server2 count');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with percentiles ', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'percentiles', settings: {percents: [75, 90]}, id: '1'}],
|
||||
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}];
|
||||
response = {
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with extended_stats ', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'extended_stats', meta: {max: true, std_deviation_bounds_upper: true}, id: '1'}],
|
||||
bucketAggs: [{type: 'date_histogram', id: '3'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"3": {
|
||||
buckets: [
|
||||
{
|
||||
"1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
},
|
||||
{
|
||||
"1": {max: 7.2, min: 3.5, std_deviation_bounds: {upper: 4, lower: -1}},
|
||||
doc_count: 15,
|
||||
key: 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
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 max');
|
||||
expect(result.data[1].target).to.be('A std_deviation_bounds_upper');
|
||||
|
||||
expect(result.data[0].datapoints[0][0]).to.be(10.2);
|
||||
expect(result.data[0].datapoints[1][0]).to.be(7.2);
|
||||
|
||||
expect(result.data[1].datapoints[0][0]).to.be(3);
|
||||
expect(result.data[1].datapoints[1][0]).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -3,7 +3,6 @@ define([
|
||||
'moment',
|
||||
'angular',
|
||||
'plugins/datasource/elasticsearch/datasource',
|
||||
'aws-sdk',
|
||||
], function(helpers, moment, angular) {
|
||||
'use strict';
|
||||
|
||||
@ -18,7 +17,7 @@ define([
|
||||
});
|
||||
|
||||
describe('When testing datasource with index pattern', function() {
|
||||
beforeEach(function(){
|
||||
beforeEach(function() {
|
||||
ctx.ds = new ctx.service({
|
||||
url: 'http://es.com',
|
||||
index: '[asd-]YYYY.MM.DD',
|
||||
@ -70,180 +69,9 @@ define([
|
||||
var header = angular.fromJson(parts[0]);
|
||||
expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When processing es response', function() {
|
||||
|
||||
describe('simple query and count', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = ctx.ds._processTimeSeries([{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}],
|
||||
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '2'}],
|
||||
}], {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"2": {
|
||||
buckets: [
|
||||
{
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
},
|
||||
{
|
||||
doc_count: 15,
|
||||
key: 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 1 series', function() {
|
||||
expect(result.data.length).to.be(1);
|
||||
expect(result.data[0].datapoints.length).to.be(2);
|
||||
expect(result.data[0].datapoints[0][0]).to.be(10);
|
||||
expect(result.data[0].datapoints[0][1]).to.be(1000);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('simple query count & avg aggregation', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = ctx.ds._processTimeSeries([{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}, {type: 'avg', field: 'value', id: '2'}],
|
||||
bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}], {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"3": {
|
||||
buckets: [
|
||||
{
|
||||
"2": {value: 88},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
},
|
||||
{
|
||||
"2": {value: 99},
|
||||
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].datapoints[0][0]).to.be(10);
|
||||
expect(result.data[0].datapoints[0][1]).to.be(1000);
|
||||
|
||||
expect(result.data[1].target).to.be("A value avg");
|
||||
expect(result.data[1].datapoints[0][0]).to.be(88);
|
||||
expect(result.data[1].datapoints[1][0]).to.be(99);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('single group by query', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
result = ctx.ds._processTimeSeries([{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}],
|
||||
bucketAggs: [{type: 'terms', field: 'host', id: '2'}, {type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}], {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"2": {
|
||||
buckets: [
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{doc_count: 1, key: 1000},
|
||||
{doc_count: 3, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 4,
|
||||
key: 'server1',
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{doc_count: 2, key: 1000},
|
||||
{doc_count: 8, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 10,
|
||||
key: 'server2',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
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 server1 count');
|
||||
expect(result.data[1].target).to.be('A server2 count');
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -156,6 +156,7 @@ require([
|
||||
'specs/elasticsearch-querybuilder-specs',
|
||||
'specs/elasticsearch-queryctrl-specs',
|
||||
'specs/elasticsearch-indexPattern-specs',
|
||||
'specs/elasticsearch-response-specs',
|
||||
];
|
||||
|
||||
var pluginSpecs = (config.plugins.specs || []).map(function (spec) {
|
||||
|
Loading…
Reference in New Issue
Block a user