diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index 674ff9b65ec..a539259f7a8 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.js +++ b/public/app/plugins/datasource/elasticsearch/datasource.js @@ -174,29 +174,41 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) { // This is quite complex // neeed to recurise down the nested buckets to build series - ElasticDatasource.prototype._processBuckets = function(buckets, groupByFields, series, level, parentName, parentTime) { + ElasticDatasource.prototype._processBuckets = function(buckets, target, series, level, parentName, parentTime) { var points = []; - var groupBy = groupByFields[level]; + var groupBy = target.groupByFields[level]; for (var i = 0; i < buckets.length; i++) { var bucket = buckets[i]; if (groupBy) { - var seriesName = level > 0 ? bucket.key : ''; + var seriesName = level > 0 ? parentName + ' ' + bucket.key : parentName; var time = parentTime || bucket.key; - this._processBuckets(bucket[groupBy.field].buckets, groupByFields, series, level+1, seriesName, time) + this._processBuckets(bucket[groupBy.field].buckets, target, series, level+1, seriesName, time) } else { - var seriesName = parentName; - if (level > 0) { - if (seriesName) { seriesName += " "; } - seriesName += bucket.key; - } else { - parentTime = bucket.key; + for (var y = 0; y < target.select.length; y++) { + var select = target.select[y]; + var seriesName = parentName; + var value; + + if (level > 0) { + seriesName += ' ' + bucket.key; + } else { + parentTime = bucket.key; + } + + if (select.field) { + seriesName += ' ' + select.field; + value = bucket[select.field].value; + } else { + seriesName += ' count'; + value = bucket.doc_count; + } + + var serie = series[seriesName] = series[seriesName] || {target: seriesName, datapoints: []}; + serie.datapoints.push([value, parentTime]); } - - var serie = series[seriesName] = series[seriesName] || {target: seriesName, datapoints: []}; - serie.datapoints.push([bucket.doc_count, parentTime]); } } }; @@ -215,7 +227,7 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) { var points = []; var querySeries = {} - this._processBuckets(buckets, target.groupByFields, querySeries, 0); + this._processBuckets(buckets, target, querySeries, 0, target.refId); _.each(querySeries, function(value) { series.push(value); diff --git a/public/app/plugins/datasource/elasticsearch/queryCtrl.js b/public/app/plugins/datasource/elasticsearch/queryCtrl.js index fd59220b7aa..c64ed9782c1 100644 --- a/public/app/plugins/datasource/elasticsearch/queryCtrl.js +++ b/public/app/plugins/datasource/elasticsearch/queryCtrl.js @@ -22,7 +22,7 @@ function (angular, _, ElasticQueryBuilder) { var target = $scope.target; target.function = target.function || 'mean'; target.timeField = target.timeField || '@timestamp'; - target.select = target.select || [{ agg: 'Count' }]; + target.select = target.select || [{ agg: 'count' }]; target.groupByFields = target.groupByFields || []; $scope.timeSegment = uiSegmentSrv.newSegment(target.timeField); @@ -31,8 +31,16 @@ function (angular, _, ElasticQueryBuilder) { return uiSegmentSrv.newSegment(group.field); }); + $scope.initSelectSegments(); + $scope.groupBySegments.push(uiSegmentSrv.newPlusButton()); + $scope.removeSelectSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove select --'}); + $scope.resetSelectSegment = uiSegmentSrv.newSegment({fake: true, value: '-- reset --'}); + $scope.removeGroupBySegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove group by --'}); + }; + + $scope.initSelectSegments = function() { $scope.selectSegments = []; - _.each(target.select, function(select) { + _.each($scope.target.select, function(select) { if ($scope.selectSegments.length > 0) { $scope.selectSegments.push(uiSegmentSrv.newCondition(" and ")); } @@ -44,10 +52,7 @@ function (angular, _, ElasticQueryBuilder) { } }); - $scope.groupBySegments.push(uiSegmentSrv.newPlusButton()); $scope.selectSegments.push(uiSegmentSrv.newPlusButton()); - $scope.removeSelectSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove select --'}); - $scope.removeGroupBySegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove group by --'}); }; $scope.getSelectSegments = function(segment, index) { @@ -55,12 +60,15 @@ function (angular, _, ElasticQueryBuilder) { var options = [ uiSegmentSrv.newSegment({value: 'count', type: 'agg'}), uiSegmentSrv.newSegment({value: 'min', type: 'agg', reqField: true}), - uiSegmentSrv.newSegment({value: 'count', type: 'agg', reqField: true}), + uiSegmentSrv.newSegment({value: 'max', type: 'agg', reqField: true}), uiSegmentSrv.newSegment({value: 'avg', type: 'agg', reqField: true}), ]; - if (index > 0) { + if (segment.type !== 'plus-button' && $scope.selectSegments.length > 3) { options.splice(0, 0, angular.copy($scope.removeSelectSegment)); } + if (index === 0 && $scope.selectSegments.length > 2) { + options.splice(0, 0, angular.copy($scope.resetSelectSegment)); + } return $q.when(options); } @@ -70,17 +78,28 @@ function (angular, _, ElasticQueryBuilder) { }; $scope.selectChanged = function(segment, index) { + // reset + if (segment.value === $scope.resetSelectSegment.value) { + $scope.target.select = [{ agg: 'count' }]; + $scope.initSelectSegments(); + $scope.get_data(); + return; + } + + // remove this select field if (segment.value === $scope.removeSelectSegment.value) { var nextSegment = $scope.selectSegments[index + 1]; var remove = 2; if (nextSegment && nextSegment.type === 'field') { remove += 1; } - $scope.selectSegments.splice(index-1, remove); + $scope.selectSegments.splice(Math.max(index-1, 0), remove); $scope.rebuildTargetSelects(); + $scope.get_data(); return; } + // add new if (segment.type === 'plus-button' && index > 0) { $scope.selectSegments.splice(index, 0, uiSegmentSrv.newCondition(' And ')); segment.type = 'agg'; @@ -102,6 +121,7 @@ function (angular, _, ElasticQueryBuilder) { } $scope.rebuildTargetSelects(); + $scope.get_data(); }; $scope.rebuildTargetSelects = function() { @@ -110,13 +130,14 @@ function (angular, _, ElasticQueryBuilder) { var segment = $scope.selectSegments[i]; var select = {agg: segment.value }; - if (segment.type === 'agg' && segment.reqField) { + if (segment.type === 'agg' && segment.value !== 'count') { select.field = $scope.selectSegments[i+1].value; i += 2; } else { i += 1; } + if (select.field === 'select field') { continue; } $scope.target.select.push(select); }; }; diff --git a/public/test/specs/elasticsearch-querybuilder-specs.js b/public/test/specs/elasticsearch-querybuilder-specs.js index 00a8cc56af8..550a485cb53 100644 --- a/public/test/specs/elasticsearch-querybuilder-specs.js +++ b/public/test/specs/elasticsearch-querybuilder-specs.js @@ -25,7 +25,7 @@ define([ groupByFields: [], }, 100, 1000); - expect(query.query.filtered.filter.bool.must[0].range["@timestamp"].gte).to.be(100); + var aggs = query.aggs.histogram; }); diff --git a/public/test/specs/elasticsearch-specs.js b/public/test/specs/elasticsearch-specs.js index e73e6616fd1..433c08e0f14 100644 --- a/public/test/specs/elasticsearch-specs.js +++ b/public/test/specs/elasticsearch-specs.js @@ -17,12 +17,13 @@ define([ describe('When processing es response', function() { - describe('simple query', function() { + describe('simple query and count', function() { var result; beforeEach(function() { result = ctx.ds._processTimeSeries([{ refId: 'A', + select: [{agg: 'count'}], groupByFields: [], }], { responses: [{ @@ -53,11 +54,58 @@ define([ }); + describe('simple query count & avg aggregation', function() { + var result; + + beforeEach(function() { + result = ctx.ds._processTimeSeries([{ + refId: 'A', + select: [{agg: 'count'}, {agg: 'avg', field: 'value'}], + groupByFields: [], + }], { + responses: [{ + aggregations: { + histogram: { + buckets: [ + { + value: {value: 88}, + doc_count: 10, + key: 1000 + }, + { + value: {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"); + 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', groupByFields: [{field: 'host' }]}], { + result = ctx.ds._processTimeSeries([{ + refId: 'A', + select: [{agg: 'count'}], + groupByFields: [{field: 'host' }] + }], { responses: [{ aggregations: { histogram: { @@ -92,8 +140,8 @@ define([ 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('server1'); - expect(result.data[1].target).to.be('server2'); + expect(result.data[0].target).to.be('A server1 count'); + expect(result.data[1].target).to.be('A server2 count'); }); }); @@ -101,7 +149,11 @@ define([ var result; beforeEach(function() { - result = ctx.ds._processTimeSeries([{refId: 'A', groupByFields: [{field: 'host'}, {field: 'site'}]}], { + result = ctx.ds._processTimeSeries([{ + refId: 'A', + select: [{agg: 'count'}], + groupByFields: [{field: 'host'}, {field: 'site'}] + }], { responses: [{ aggregations: { histogram: { @@ -170,10 +222,10 @@ define([ it('should return 2 series', function() { expect(result.data.length).to.be(4); expect(result.data[0].datapoints.length).to.be(2); - expect(result.data[0].target).to.be('server1 backend'); - expect(result.data[1].target).to.be('server1 frontend'); - expect(result.data[2].target).to.be('server2 backend'); - expect(result.data[3].target).to.be('server2 frontend'); + expect(result.data[0].target).to.be('A server1 backend count'); + expect(result.data[1].target).to.be('A server1 frontend count'); + expect(result.data[2].target).to.be('A server2 backend count'); + expect(result.data[3].target).to.be('A server2 frontend count'); }); });