diff --git a/public/app/plugins/datasource/elasticsearch/bucketAgg.js b/public/app/plugins/datasource/elasticsearch/bucketAgg.js
index a8e56aa07f3..d96e9ad8090 100644
--- a/public/app/plugins/datasource/elasticsearch/bucketAgg.js
+++ b/public/app/plugins/datasource/elasticsearch/bucketAgg.js
@@ -35,6 +35,20 @@ function (angular, _, queryDef) {
$scope.agg.settings = {};
$scope.showOptions = false;
+ switch($scope.agg.type) {
+ case 'date_histogram':
+ case 'terms': {
+ delete $scope.agg.query;
+ $scope.agg.type = 'select field';
+ break;
+ }
+ case 'filters': {
+ delete $scope.agg.field;
+ $scope.agg.query = '*';
+ break;
+ }
+ }
+
$scope.validateModel();
$scope.onChange();
};
@@ -65,6 +79,9 @@ function (angular, _, queryDef) {
break;
}
+ case 'filters': {
+ break;
+ }
case 'date_histogram': {
settings.interval = settings.interval || 'auto';
$scope.agg.field = $scope.target.timeField;
diff --git a/public/app/plugins/datasource/elasticsearch/elasticResponse.js b/public/app/plugins/datasource/elasticsearch/elasticResponse.js
index 878b1ce9468..5914e024c44 100644
--- a/public/app/plugins/datasource/elasticsearch/elasticResponse.js
+++ b/public/app/plugins/datasource/elasticsearch/elasticResponse.js
@@ -10,23 +10,8 @@ function (_, queryDef) {
this.response = response;
}
- // This is quite complex
- // neeed to recurise down the nested buckets to build series
- ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, level, props) {
- var value, metric, i, y, bucket, aggDef, esAgg, newSeries;
-
- aggDef = target.bucketAggs[level];
- esAgg = aggs[aggDef.id];
-
- if (level < target.bucketAggs.length - 1) {
- for (i = 0; i < esAgg.buckets.length; i++) {
- bucket = esAgg.buckets[i];
- props = _.clone(props);
- props[aggDef.field] = bucket.key;
- this.processBuckets(bucket, target, seriesList, level+1, props);
- }
- return;
- }
+ ElasticResponse.prototype.processMetrics = function(esAgg, target, seriesList, props) {
+ var metric, y, i, newSeries, bucket, value;
for (y = 0; y < target.metrics.length; y++) {
metric = target.metrics[y];
@@ -101,6 +86,35 @@ function (_, queryDef) {
}
};
+ // This is quite complex
+ // neeed to recurise down the nested buckets to build series
+ ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, props) {
+ var bucket, aggDef, esAgg, aggId;
+
+ for (aggId in aggs) {
+ aggDef = _.findWhere(target.bucketAggs, {id: aggId});
+ esAgg = aggs[aggId];
+ if (!aggDef) {
+ continue;
+ }
+
+ if (aggDef.type === 'date_histogram') {
+ this.processMetrics(esAgg, target, seriesList, props);
+ } else {
+ for (var nameIndex in esAgg.buckets) {
+ bucket = esAgg.buckets[nameIndex];
+ props = _.clone(props);
+ if (bucket.key) {
+ props[aggDef.field] = bucket.key;
+ } else {
+ props["filter"] = nameIndex;
+ }
+ this.processBuckets(bucket, target, seriesList, props);
+ }
+ }
+ }
+ };
+
ElasticResponse.prototype._getMetricName = function(metric) {
var metricDef = _.findWhere(queryDef.metricAggTypes, {value: metric});
if (!metricDef) {
@@ -172,7 +186,7 @@ function (_, queryDef) {
var target = this.targets[i];
var tmpSeriesList = [];
- this.processBuckets(aggregations, target, tmpSeriesList, 0, {});
+ this.processBuckets(aggregations, target, tmpSeriesList, {});
this.nameSeries(tmpSeriesList, target);
for (var y = 0; y < tmpSeriesList.length; y++) {
diff --git a/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html b/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html
index e87872fd7bf..2d7349f295a 100644
--- a/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html
+++ b/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html
@@ -7,9 +7,12 @@
-
+
+
+
+
{{settingsLinkText}}
diff --git a/public/app/plugins/datasource/elasticsearch/queryBuilder.js b/public/app/plugins/datasource/elasticsearch/queryBuilder.js
index 961f6cad96e..f1d1998e51d 100644
--- a/public/app/plugins/datasource/elasticsearch/queryBuilder.js
+++ b/public/app/plugins/datasource/elasticsearch/queryBuilder.js
@@ -54,12 +54,34 @@ function (angular) {
}
};
+ ElasticQueryBuilder.prototype.getFiltersAgg = function(target) {
+ var filterObj = {};
+
+ for (var i = 0; i < target.bucketAggs.length; i++) {
+ var aggDef = target.bucketAggs[i];
+ if (aggDef.type !== 'filters') {
+ continue;
+ }
+
+ filterObj[aggDef.query] = {
+ query: {
+ query_string: {
+ query: aggDef.query,
+ analyze_wildcard: true
+ }
+ }
+ };
+ }
+
+ return filterObj;
+ };
+
ElasticQueryBuilder.prototype.build = function(target) {
if (target.rawQuery) {
return angular.fromJson(target.rawQuery);
}
- var i, nestedAggs, metric;
+ var i, nestedAggs, metric, filtersHandled;
var query = {
"size": 0,
"query": {
@@ -95,6 +117,15 @@ function (angular) {
};
break;
}
+ case 'filters': {
+ // skip filters if we already processed them
+ if (filtersHandled) {
+ continue;
+ }
+ esAgg["filters"] = {filters: this.getFiltersAgg(target)};
+ filtersHandled = true;
+ break;
+ }
case 'terms': {
this.buildTermsAgg(aggDef, esAgg, target);
break;
diff --git a/public/app/plugins/datasource/elasticsearch/queryDef.js b/public/app/plugins/datasource/elasticsearch/queryDef.js
index 8770ea25fba..9c6089810ff 100644
--- a/public/app/plugins/datasource/elasticsearch/queryDef.js
+++ b/public/app/plugins/datasource/elasticsearch/queryDef.js
@@ -18,6 +18,7 @@ function (_) {
bucketAggTypes: [
{text: "Terms", value: 'terms' },
+ {text: "Filters", value: 'filters' },
{text: "Date Histogram", value: 'date_histogram' },
],
diff --git a/public/less/tightform.less b/public/less/tightform.less
index 39c788803a6..494497653ed 100644
--- a/public/less/tightform.less
+++ b/public/less/tightform.less
@@ -214,6 +214,10 @@ select.tight-form-input {
.tight-form-item-xlarge { width: 150px; }
.tight-form-item-xxlarge { width: 200px; }
+.tight-form-input.tight-form-item-xxlarge {
+ width: 215px;
+}
+
.tight-form-inner-box {
margin: 20px 0 20px 148px;
display: inline-block;
diff --git a/public/test/specs/elasticsearch-querybuilder-specs.js b/public/test/specs/elasticsearch-querybuilder-specs.js
index 0e1b354f60a..f80b29f4021 100644
--- a/public/test/specs/elasticsearch-querybuilder-specs.js
+++ b/public/test/specs/elasticsearch-querybuilder-specs.js
@@ -95,6 +95,22 @@ define([
expect(firstLevel.aggs["1"].percentiles.percents).to.eql([1,2,3,4]);
});
+ it('with filters aggs', function() {
+ var query = builder.build({
+ metrics: [{type: 'count', id: '1'}],
+ timeField: '@timestamp',
+ bucketAggs: [
+ {type: 'filters', query: '@metric:cpu', id: '2'},
+ {type: 'filters', query: '@metric:logins.count', id: '3' },
+ {type: 'date_histogram', field: '@timestamp', id: '4'}
+ ],
+ });
+
+ expect(query.aggs["2"].filters.filters["@metric:cpu"].query.query_string.query).to.be("@metric:cpu");
+ expect(query.aggs["2"].filters.filters["@metric:logins.count"].query.query_string.query).to.be("@metric:logins.count");
+ expect(query.aggs["2"].aggs["4"].date_histogram.field).to.be("@timestamp");
+ });
+
});
});
diff --git a/public/test/specs/elasticsearch-response-specs.js b/public/test/specs/elasticsearch-response-specs.js
index 41073d65f16..4001327aa5e 100644
--- a/public/test/specs/elasticsearch-response-specs.js
+++ b/public/test/specs/elasticsearch-response-specs.js
@@ -351,5 +351,56 @@ define([
});
});
+ describe('with two filters agg', function() {
+ var result;
+
+ beforeEach(function() {
+ targets = [{
+ refId: 'A',
+ metrics: [{type: 'count', id: '1'}],
+ bucketAggs: [
+ {type: 'filters', query: '@metric:cpu', id: '2'},
+ {type: 'filters', query: '@metric:logins.count', id: '5'},
+ {type: 'date_histogram', field: '@timestamp', id: '3'}
+ ],
+ }];
+ response = {
+ responses: [{
+ aggregations: {
+ "2": {
+ buckets: {
+ "@metric:cpu": {
+ "3": {
+ buckets: [
+ {doc_count: 1, key: 1000},
+ {doc_count: 3, key: 2000}
+ ]
+ },
+ },
+ "@metric:logins.count": {
+ "3": {
+ buckets: [
+ {doc_count: 2, key: 1000},
+ {doc_count: 8, 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('@metric:cpu');
+ expect(result.data[1].target).to.be('@metric:logins.count');
+ });
+ });
+
});
});