Просмотр исходного кода

feat(elasticsearch): small progress on new elasticsearch metric query capability

Torkel Ödegaard 10 лет назад
Родитель
Сommit
ae7093e0bb

+ 3 - 11
public/app/plugins/datasource/cloudwatch/queryCtrl.js

@@ -19,17 +19,9 @@ function (angular, _) {
       $scope.target.region = $scope.target.region || $scope.datasource.getDefaultRegion();
       $scope.target.errors = validateTarget();
 
-      $scope.regionSegment = $scope.getSegmentForValue($scope.target.region, 'select region');
-      $scope.namespaceSegment = $scope.getSegmentForValue($scope.target.namespace, 'select namespace');
-      $scope.metricSegment = $scope.getSegmentForValue($scope.target.metricName, 'select metric');
-    };
-
-    $scope.getSegmentForValue = function(value, fallbackText) {
-      if (value) {
-        return uiSegmentSrv.newSegment(value);
-      } else {
-        return uiSegmentSrv.newSegment({value: fallbackText, fake: true});
-      }
+      $scope.regionSegment =  uiSegmentSrv.getSegmentForValue($scope.target.region, 'select region');
+      $scope.namespaceSegment = uiSegmentSrv.getSegmentForValue($scope.target.namespace, 'select namespace');
+      $scope.metricSegment = uiSegmentSrv.getSegmentForValue($scope.target.metricName, 'select metric');
     };
 
     $scope.getRegions = function() {

+ 30 - 2
public/app/plugins/datasource/elasticsearch/datasource.js

@@ -138,13 +138,21 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
       var queryBuilder = new ElasticQueryBuilder;
       var query = queryBuilder.build(options.targets);
       query = query.replace(/\$interval/g, options.interval);
-      query = query.replace(/\$rangeFrom/g, options.range.from);
-      query = query.replace(/\$rangeTo/g, options.range.to);
+      query = query.replace(/\$rangeFrom/g, this.translateTime(options.range.from));
+      query = query.replace(/\$rangeTo/g, this.translateTime(options.range.to));
       query = query.replace(/\$maxDataPoints/g, options.maxDataPoints);
       query = templateSrv.replace(query, options.scopedVars);
       return this._post('/_search?search_type=count', query).then(this._getTimeSeries);
     };
 
+    ElasticDatasource.prototype.translateTime = function(date) {
+      if (_.isString(date)) {
+        return date;
+      }
+
+      return date.toJSON();
+    };
+
     ElasticDatasource.prototype._getTimeSeries = function(results) {
       var _aggregation2timeSeries = function(aggregation) {
         var datapoints = aggregation.date_histogram.buckets.map(function(entry) {
@@ -169,6 +177,26 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
       return { data: data };
     };
 
+    ElasticDatasource.prototype.metricFindQuery = function(query) {
+      var region;
+      var namespace;
+      var metricName;
+
+      var transformSuggestData = function(suggestData) {
+        return _.map(suggestData, function(v) {
+          return { text: v };
+        });
+      };
+
+      var d = $q.defer();
+
+      var regionQuery = query.match(/^region\(\)/);
+      if (regionQuery) {
+        d.resolve(transformSuggestData(this.performSuggestRegion()));
+        return d.promise;
+      }
+    };
+
     return ElasticDatasource;
   });
 });

+ 7 - 34
public/app/plugins/datasource/elasticsearch/partials/query.editor.html

@@ -37,32 +37,12 @@
 		</li>
 	</ul>
 
-	<input type="text" class="tight-form-clear-input" style="width: 80%" ng-model="target.query" focus-me="target.rawQuery" spellcheck='false' ng-model-onblur ng-change="get_data()" ng-show="target.rawQuery"/>
-
-	<ul class="tight-form-list" role="menu" ng-hide="target.rawQuery">
-		<li class="tight-form-item query-keyword" style="width: 75px;">
-			Function
-		</li>
-		<li class="dropdown tight-form-item">
-			<a gf-dropdown="functionMenu" class="dropdown-toggle" data-toggle="dropdown">
-				{{target.function}}<span>(value)</span>
-			</a>
-		</li>
-	</ul>
-
-	<div class="clearfix"></div>
-</div>
-
-<div class="tight-form" ng-hide="target.rawQuery">
 	<ul class="tight-form-list">
-		<li class="tight-form-item">
-			<i class="fa fa-eye invisible"></i>
-		</li>
-		<li class="tight-form-item query-keyword" style="width: 75px;">
-			Key Field
+		<li class="tight-form-item query-keyword">
+			Timestamp field
 		</li>
 		<li>
-			<metric-segment segment="keyFieldSegment" on-value-changed="keyFieldChanged()"></metric-segment>
+			<metric-segment segment="timestampSegment" get-alt-segments="getFields()" on-value-changed="timestampChanged()"></metric-segment>
 		</li>
 	</ul>
 
@@ -71,14 +51,11 @@
 
 <div class="tight-form" ng-hide="target.rawQuery">
 	<ul class="tight-form-list">
-		<li class="tight-form-item">
-			<i class="fa fa-eye invisible"></i>
-		</li>
-		<li class="tight-form-item query-keyword" style="width: 75px;">
-			Value Field
+		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
+			Value field
 		</li>
 		<li>
-			<metric-segment segment="valueFieldSegment" on-value-changed="valueFieldChanged()"></metric-segment>
+			<metric-segment segment="valueFieldSegment" get-alt-segments="getFields()"  on-value-changed="valueFieldChanged()"></metric-segment>
 		</li>
 	</ul>
 
@@ -87,11 +64,7 @@
 
 <div class="tight-form" ng-hide="target.rawQuery">
 	<ul class="tight-form-list">
-		<li class="tight-form-item">
-			<i class="fa fa-eye invisible"></i>
-		</li>
-
-		<li class="tight-form-item query-keyword" style="width: 75px;">
+		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
 			Term
 		</li>
 

+ 35 - 32
public/app/plugins/datasource/elasticsearch/queryBuilder.js

@@ -3,52 +3,55 @@ define([
 function () {
   'use strict';
 
-  function ElasticQueryBuilder() {
-  }
+  function ElasticQueryBuilder() { }
 
   ElasticQueryBuilder.prototype.build = function(targets) {
     var query = {
       "aggs": {},
       "size": "$maxDataPoints"
     };
+
     var self = this;
-    targets.forEach(function(target) {
-      if (!target.hide) {
-        query["aggs"][target.termKey + "_" + target.termValue] = {
-          "filter": {
-            "and": [
-              self._buildRangeFilter(target),
-              self._buildTermFilter(target)
-            ]
-          },
-          "aggs": {
+    targets.forEach(function(target, index) {
+      if (target.hide) {
+        return;
+      }
+
+      var esQuery = {
+        "filter": {
+          "and": [
+            self._buildRangeFilter(target)
+          ]
+        },
+        "aggs": {
+          "date_histogram": {
             "date_histogram": {
-              "date_histogram": {
-                "interval": target.interval || "$interval",
-                "field": target.keyField,
-                "min_doc_count": 0,
-              },
-              "aggs": {
+              "interval": target.interval || "$interval",
+              "field": target.timestampField,
+              "min_doc_count": 0,
+            },
+            "aggs": {
+              "stats": {
                 "stats": {
-                  "stats": {
-                    "field": target.valueField
-                  }
+                  "field": target.valueField
                 }
               }
             }
           }
-        };
-        if (target.groupByField) {
-          query["aggs"][target.termKey + "_" + target.termValue]["aggs"] = {
-            "terms": {
-              "terms": {
-                "field": target.groupByField
-              },
-              "aggs": query["aggs"][target.termKey + "_" + target.termValue]["aggs"]
-            }
-          };
         }
+      };
+      if (target.groupByField) {
+        query["aggs"][target.termKey + "_" + target.termValue]["aggs"] = {
+          "terms": {
+            "terms": {
+              "field": target.groupByField
+            },
+            "aggs": query["aggs"][target.termKey + "_" + target.termValue]["aggs"]
+          }
+        };
       }
+
+      query["aggs"]['query:' + index] = esQuery;
     });
     query = JSON.stringify(query);
     return query;
@@ -56,7 +59,7 @@ function () {
 
   ElasticQueryBuilder.prototype._buildRangeFilter = function(target) {
     var filter = {"range":{}};
-    filter["range"][target.keyField] = {
+    filter["range"][target.timestampField] = {
       "gte": "$rangeFrom",
       "lte": "$rangeTo"
     };

+ 27 - 87
public/app/plugins/datasource/elasticsearch/queryCtrl.js

@@ -8,7 +8,7 @@ function (angular, _, ElasticQueryBuilder) {
 
   var module = angular.module('grafana.controllers');
 
-  module.controller('ElasticQueryCtrl', function($scope, $timeout, $sce, templateSrv, $q) {
+  module.controller('ElasticQueryCtrl', function($scope, $timeout, uiSegmentSrv, templateSrv, $q) {
 
     $scope.functionList = ['count', 'min', 'max', 'total', 'mean'];
 
@@ -17,63 +17,40 @@ function (angular, _, ElasticQueryBuilder) {
     });
 
     $scope.init = function() {
+      $scope.queryBuilder = new ElasticQueryBuilder(target);
+
       var target = $scope.target;
       target.function = target.function || 'mean';
+      target.timestampField = target.timestampField || '@timestamp';
+      target.valueField = target.valueField || 'value' ;
 
-      $scope.queryBuilder = new ElasticQueryBuilder(target);
+      $scope.timestampSegment = uiSegmentSrv.newSegment(target.timestampField);
+      $scope.valueFieldSegment = uiSegmentSrv.newSegment(target.valueField);
 
-      if (!target.keyField) {
-        target.keyField = '@timestamp';
-      }
-      $scope.keyFieldSegment = new MetricSegment({value: target.keyField});
-
-      if (!target.valueField) {
-        target.valueField = 'metric';
-      }
-      $scope.valueFieldSegment = new MetricSegment({value: target.valueField});
-
-      if (!target.termKey) {
-        target.termKey = 'service.raw';
-      }
-      $scope.termKeySegment = new MetricSegment({value: target.termKey});
-
-      if (!target.termValue) {
-        target.termValue = 'cpu-average/cpu-user';
-      }
-      $scope.termValueSegment = new MetricSegment({value: target.termValue});
-
-      if (!target.groupByField) {
-        target.groupByField = 'host.raw';
-      }
-      $scope.groupByFieldSegment = new MetricSegment({value: target.groupByField});
-
-      if (!target.measurement) {
-        $scope.measurementSegment = MetricSegment.newSelectMeasurement();
-      } else {
-        $scope.measurementSegment = new MetricSegment(target.measurement);
-      }
-
-      $scope.tagSegments = [];
-      _.each(target.tags, function(tag) {
-        if (tag.condition) {
-          $scope.tagSegments.push(MetricSegment.newCondition(tag.condition));
-        }
-        $scope.tagSegments.push(new MetricSegment({value: tag.key, type: 'key', cssClass: 'query-segment-key' }));
-        $scope.tagSegments.push(new MetricSegment.newOperator("="));
-        $scope.tagSegments.push(new MetricSegment({value: tag.value, type: 'value', cssClass: 'query-segment-value'}));
-      });
+      $scope.termKeySegment = uiSegmentSrv.getSegmentForValue(target.termKey, 'select term field');
+      $scope.termValueSegment = uiSegmentSrv.getSegmentForValue(target.termValue, 'select term value');
+      $scope.groupByFieldSegment = uiSegmentSrv.getSegmentForValue(target.groupByField, 'add group by');
+    };
 
-      $scope.fixTagSegments();
+    $scope.getFields = function() {
+      return $scope.datasource.metricFindQuery('fields()')
+        .then($scope.transformToSegments(true));
+    };
 
-      $scope.groupBySegments = [];
-      _.each(target.groupByTags, function(tag) {
-        $scope.groupBySegments.push(new MetricSegment(tag));
-      });
+    $scope.transformToSegments = function(addTemplateVars) {
+      return function(results) {
+        var segments = _.map(results, function(segment) {
+          return uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable });
+        });
 
-      $scope.groupBySegments.push(MetricSegment.newPlusButton());
+        if (addTemplateVars) {
+          _.each(templateSrv.variables, function(variable) {
+            segments.unshift(uiSegmentSrv.newSegment({ type: 'template', value: '$' + variable.name, expandable: true }));
+          });
+        }
 
-      $scope.removeTagFilterSegment = new MetricSegment({fake: true, value: '-- remove tag filter --'});
-      $scope.removeGroupBySegment = new MetricSegment({fake: true, value: '-- remove group by --'});
+        return segments;
+      };
     };
 
     $scope.valueFieldChanged = function() {
@@ -101,48 +78,11 @@ function (angular, _, ElasticQueryBuilder) {
       $scope.$parent.get_data();
     };
 
-    $scope.fixTagSegments = function() {
-      var count = $scope.tagSegments.length;
-      var lastSegment = $scope.tagSegments[Math.max(count-1, 0)];
-
-      if (!lastSegment || lastSegment.type !== 'plus-button') {
-        $scope.tagSegments.push(MetricSegment.newPlusButton());
-      }
-    };
-
-    $scope.groupByTagUpdated = function(segment, index) {
-      if (segment.value === $scope.removeGroupBySegment.value) {
-        $scope.target.groupByTags.splice(index, 1);
-        $scope.groupBySegments.splice(index, 1);
-        $scope.$parent.get_data();
-        return;
-      }
-
-      if (index === $scope.groupBySegments.length-1) {
-        $scope.groupBySegments.push(MetricSegment.newPlusButton());
-      }
-
-      segment.type = 'group-by-key';
-      segment.fake = false;
-
-      $scope.target.groupByTags[index] = segment.value;
-      $scope.$parent.get_data();
-    };
-
     $scope.changeFunction = function(func) {
       $scope.target.function = func;
       $scope.$parent.get_data();
     };
 
-    $scope.measurementChanged = function() {
-      $scope.target.measurement = $scope.measurementSegment.value;
-      $scope.$parent.get_data();
-    };
-
-    $scope.toggleQueryMode = function () {
-      $scope.target.rawQuery = !$scope.target.rawQuery;
-    };
-
     $scope.handleQueryError = function(err) {
       $scope.parserError = err.message || 'Failed to issue metric query';
       return [];

+ 8 - 0
public/app/services/uiSegmentSrv.js

@@ -32,6 +32,14 @@ function (angular, _) {
       this.html = options.html || $sce.trustAsHtml(templateSrv.highlightVariablesAsHtml(this.value));
     }
 
+    this.getSegmentForValue = function(value, fallbackText) {
+      if (value) {
+        return this.newSegment(value);
+      } else {
+        return this.newSegment({value: fallbackText, fake: true});
+      }
+    };
+
     this.newSelectMeasurement = function() {
       return new MetricSegment({value: 'select measurement', fake: true});
     };