From a01836e86d20d2012b03bfe722a16a85bdc9d33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 21 Sep 2016 08:46:59 +0200 Subject: [PATCH] feat(adhoc filters): basic implementation for ad hoc filters for elasticsearch, #6038 --- public/app/features/templating/editor_ctrl.ts | 2 +- .../datasource/elasticsearch/datasource.js | 15 ++++++++++++-- .../datasource/elasticsearch/query_builder.js | 20 ++++++++++++++++++- .../specs/query_builder_specs.ts | 12 +++++++++++ .../plugins/datasource/influxdb/datasource.ts | 3 +-- public/test/specs/helpers.js | 1 + 6 files changed, 47 insertions(+), 6 deletions(-) diff --git a/public/app/features/templating/editor_ctrl.ts b/public/app/features/templating/editor_ctrl.ts index 81bf2d4d71c..3c0410e1316 100644 --- a/public/app/features/templating/editor_ctrl.ts +++ b/public/app/features/templating/editor_ctrl.ts @@ -84,7 +84,7 @@ export class VariableEditorCtrl { if ($scope.current.type === 'adhoc' && $scope.current.datasource !== null) { $scope.infoText = 'Adhoc filters are applied automatically to all queries that target this datasource'; datasourceSrv.get($scope.current.datasource).then(ds => { - if (!ds.supportAdhocFilters) { + if (!ds.getTagKeys) { $scope.infoText = 'This datasource does not support adhoc filters yet.'; } }); diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js index 8cfe7d6129a..0889c078082 100644 --- a/public/app/plugins/datasource/elasticsearch/datasource.js +++ b/public/app/plugins/datasource/elasticsearch/datasource.js @@ -177,11 +177,14 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes var target; var sentTargets = []; + // add global adhoc filters to timeFilter + var adhocFilters = templateSrv.getAdhocFilters(this.name); + for (var i = 0; i < options.targets.length; i++) { target = options.targets[i]; if (target.hide) {continue;} - var queryObj = this.queryBuilder.build(target); + var queryObj = this.queryBuilder.build(target, adhocFilters); var esQuery = angular.toJson(queryObj); var luceneQuery = target.query || '*'; luceneQuery = templateSrv.replace(luceneQuery, options.scopedVars, 'lucene'); @@ -247,7 +250,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes // Hide meta-fields and check field type if (key[0] !== '_' && (!query.type || - query.type && typeMap[subObj.type] === query.type)) { + query.type && typeMap[subObj.type] === query.type)) { fields[fieldName] = { text: fieldName, @@ -314,6 +317,14 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes return this.getTerms(query); } }; + + this.getTagKeys = function() { + return this.getFields({}); + }; + + this.getTagValues = function(options) { + return this.getTerms({field: options.key, query: '*'}); + }; } return { diff --git a/public/app/plugins/datasource/elasticsearch/query_builder.js b/public/app/plugins/datasource/elasticsearch/query_builder.js index 9c8217102aa..d256c6d1438 100644 --- a/public/app/plugins/datasource/elasticsearch/query_builder.js +++ b/public/app/plugins/datasource/elasticsearch/query_builder.js @@ -98,7 +98,23 @@ function (queryDef) { return query; }; - ElasticQueryBuilder.prototype.build = function(target) { + ElasticQueryBuilder.prototype.addAdhocFilters = function(query, adhocFilters) { + if (!adhocFilters) { + return; + } + + var i, filter, condition; + var must = query.query.filtered.filter.bool.must; + + for (i = 0; i < adhocFilters.length; i++) { + filter = adhocFilters[i]; + condition = {}; + condition[filter.key] = filter.value; + must.push({"term": condition}); + } + }; + + ElasticQueryBuilder.prototype.build = function(target, adhocFilters) { // make sure query has defaults; target.metrics = target.metrics || [{ type: 'count', id: '1' }]; target.dsType = 'elasticsearch'; @@ -125,6 +141,8 @@ function (queryDef) { } }; + this.addAdhocFilters(query, adhocFilters); + // handle document query if (target.bucketAggs.length === 0) { metric = target.metrics[0]; diff --git a/public/app/plugins/datasource/elasticsearch/specs/query_builder_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/query_builder_specs.ts index bbd5711fd1f..d9174e73969 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/query_builder_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/query_builder_specs.ts @@ -238,4 +238,16 @@ describe('ElasticQueryBuilder', function() { expect(firstLevel.aggs["2"].derivative.buckets_path).to.be("3"); }); + it('with adhoc filters', function() { + var query = builder.build({ + metrics: [{type: 'Count', id: '0'}], + timeField: '@timestamp', + bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '3'}], + }, [ + {key: 'key1', operator: '=', value: 'value1'} + ]); + + expect(query.query.filtered.filter.bool.must[1].term["key1"]).to.be("value1"); + }); + }); diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 4c707dd59d1..76a66d18e3c 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -9,6 +9,7 @@ import InfluxQuery from './influx_query'; import ResponseParser from './response_parser'; import InfluxQueryBuilder from './query_builder'; + export default class InfluxDatasource { type: string; urls: any; @@ -21,7 +22,6 @@ export default class InfluxDatasource { interval: any; supportAnnotations: boolean; supportMetrics: boolean; - supportAdhocFilters: boolean; responseParser: any; /** @ngInject */ @@ -40,7 +40,6 @@ export default class InfluxDatasource { this.interval = (instanceSettings.jsonData || {}).timeInterval; this.supportAnnotations = true; this.supportMetrics = true; - this.supportAdhocFilters = true; this.responseParser = new ResponseParser(); } diff --git a/public/test/specs/helpers.js b/public/test/specs/helpers.js index 0e5f20a853c..424b190b6dd 100644 --- a/public/test/specs/helpers.js +++ b/public/test/specs/helpers.js @@ -158,6 +158,7 @@ define([ return _.template(text, this.templateSettings)(this.data); }; this.init = function() {}; + this.getAdhocFilters = function() { return []; }; this.fillVariableValuesForUrl = function() {}; this.updateTemplateData = function() { }; this.variableExists = function() { return false; };