Explorar el Código

feat(elasticsearch): lots of work on elasticsearch metrics queries, #1034

Torkel Ödegaard hace 10 años
padre
commit
d099d8950f

+ 54 - 29
public/app/plugins/datasource/elasticsearch/datasource.js

@@ -135,14 +135,33 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
     };
     };
 
 
     ElasticDatasource.prototype.query = function(options) {
     ElasticDatasource.prototype.query = function(options) {
-      var queryBuilder = new ElasticQueryBuilder;
-      var query = queryBuilder.build(options.targets);
-      query = query.replace(/\$interval/g, options.interval);
-      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);
+      var queryBuilder = new ElasticQueryBuilder();
+      var header = '{"index":"' + this.index + '","search_type":"count","ignore_unavailable":true}'
+      var payload = ""
+      var sentTargets = [];
+      var timeFrom = this.translateTime(options.range.from);
+      var timeTo = this.translateTime(options.range.to);
+
+      _.each(options.targets, function(target) {
+        if (target.hide) {
+          return;
+        }
+
+        var esQuery = queryBuilder.build(target, timeFrom, timeTo);
+        payload += header + '\n';
+        payload += esQuery + '\n';
+
+        sentTargets.push(target);
+      });
+
+      payload = payload.replace(/\$interval/g, options.interval);
+      payload = payload.replace(/\$rangeFrom/g, this.translateTime(options.range.from));
+      payload = payload.replace(/\$rangeTo/g, this.translateTime(options.range.to));
+      payload = payload.replace(/\$maxDataPoints/g, options.maxDataPoints);
+      payload = templateSrv.replace(payload, options.scopedVars);
+
+      var processTimeSeries = _.partial(this._processTimeSeries, sentTargets);
+      return this._post('/_msearch?search_type=count', payload).then(processTimeSeries);
     };
     };
 
 
     ElasticDatasource.prototype.translateTime = function(date) {
     ElasticDatasource.prototype.translateTime = function(date) {
@@ -150,31 +169,37 @@ function (angular, _, config, kbn, moment, ElasticQueryBuilder) {
         return date;
         return date;
       }
       }
 
 
-      return date.toJSON();
+      return date.getTime();
     };
     };
 
 
-    ElasticDatasource.prototype._getTimeSeries = function(results) {
-      var _aggregation2timeSeries = function(aggregation) {
-        var datapoints = aggregation.date_histogram.buckets.map(function(entry) {
-          return [entry.stats.avg, entry.key];
-        });
-        return { target: aggregation.key, datapoints: datapoints };
-      };
-      var data = [];
+    ElasticDatasource._aggToSeries = function(agg) {
+      var datapoints = agg.date_histogram.buckets.map(function(entry) {
+        return [entry.stats.avg, entry.key];
+      });
+      return { target: agg.key, datapoints: datapoints };
+    };
 
 
-      if (results && results.aggregations) {
-        for (var target in results.aggregations) {
-          if (!results.aggregations.hasOwnProperty(target)) {
-            continue;
-          }
-          if (results.aggregations[target].date_histogram && results.aggregations[target].date_histogram.buckets) {
-            data.push(_aggregation2timeSeries(results.aggregations[target]));
-          } else if (results.aggregations[target].terms && results.aggregations[target].terms.buckets) {
-            [].push.apply(data, results.aggregations[target].terms.buckets.map(_aggregation2timeSeries));
-          }
+
+    ElasticDatasource.prototype._processTimeSeries = function(targets, results) {
+      var series = [];
+
+      _.each(results.responses, function(response, index) {
+        var buckets = response.aggregations.date_histogram.buckets;
+        var target = targets[index];
+        var points = [];
+
+        for (var i = 0; i < buckets.length; i++) {
+          var bucket = buckets[i];
+          points[i] = [bucket.doc_count, bucket.key];
         }
         }
-      }
-      return { data: data };
+
+        series.push({target: 'name', datapoints: points})
+        console.log('Nr DataPoints: ' + points.length);
+      });
+
+      console.log(series);
+
+      return { data: series };
     };
     };
 
 
     ElasticDatasource.prototype.metricFindQuery = function(query) {
     ElasticDatasource.prototype.metricFindQuery = function(query) {

+ 17 - 32
public/app/plugins/datasource/elasticsearch/partials/query.editor.html

@@ -37,58 +37,44 @@
 		</li>
 		</li>
 	</ul>
 	</ul>
 
 
-	<ul class="tight-form-list">
-		<li class="tight-form-item query-keyword">
-			Timestamp field
+	<ul class="tight-form-list" ng-hide="target.rawQuery">
+		<li class="tight-form-item query-keyword" style="width: 75px">
+			Query
 		</li>
 		</li>
 		<li>
 		<li>
-			<metric-segment segment="timestampSegment" get-alt-segments="getFields()" on-value-changed="timestampChanged()"></metric-segment>
+			<input type="text" class="input-xlarge tight-form-input" ng-model="target.query" spellcheck='false' placeholder="Lucence query" ng-blur="get_data()">
 		</li>
 		</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 query-keyword tight-form-align" style="width: 75px;">
-			Value field
+		<li class="tight-form-item query-keyword">
+			Time field
 		</li>
 		</li>
 		<li>
 		<li>
-			<metric-segment segment="valueFieldSegment" get-alt-segments="getFields()"  on-value-changed="valueFieldChanged()"></metric-segment>
+			<metric-segment segment="timeSegment" get-alt-segments="getFields()" on-value-changed="timestampChanged()"></metric-segment>
 		</li>
 		</li>
 	</ul>
 	</ul>
 
 
 	<div class="clearfix"></div>
 	<div class="clearfix"></div>
+
+	<div style="padding: 10px" ng-if="target.rawQuery">
+		<textarea ng-model="target.rawJson" rows="8" spellcheck="false" style="width: 100%; box-sizing: border-box;"></textarea>
+	</div>
 </div>
 </div>
 
 
 <div class="tight-form" ng-hide="target.rawQuery">
 <div class="tight-form" ng-hide="target.rawQuery">
 	<ul class="tight-form-list">
 	<ul class="tight-form-list">
 		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
 		<li class="tight-form-item query-keyword tight-form-align" style="width: 75px;">
-			Term
-		</li>
-
-		<li>
-			<metric-segment segment="termKeySegment" on-value-changed="termKeySegmentChanged()"></metric-segment>
+			Select
 		</li>
 		</li>
-
-		<li>
-			<span class="tight-form-item">:</span>
-		</li>
-
-		<li>
-			<metric-segment segment="termValueSegment" on-value-changed="termValueSegmentChanged()"></metric-segment>
+		<li ng-repeat="segment in selectSegments">
+			<metric-segment segment="segment" get-alt-segments="getSelectSegments(segment, $index)"  on-value-changed="selectSegmentChanged()"></metric-segment>
 		</li>
 		</li>
 	</ul>
 	</ul>
+
 	<div class="clearfix"></div>
 	<div class="clearfix"></div>
 </div>
 </div>
 
 
 <div class="tight-form" ng-hide="target.rawQuery">
 <div class="tight-form" ng-hide="target.rawQuery">
 	<ul class="tight-form-list">
 	<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;">
 			Group By
 			Group By
 		</li>
 		</li>
 		<li>
 		<li>
@@ -98,5 +84,4 @@
 
 
 	<div class="clearfix"></div>
 	<div class="clearfix"></div>
 </div>
 </div>
-	</div>
-</div>
+

+ 42 - 40
public/app/plugins/datasource/elasticsearch/queryBuilder.js

@@ -5,54 +5,56 @@ function () {
 
 
   function ElasticQueryBuilder() { }
   function ElasticQueryBuilder() { }
 
 
-  ElasticQueryBuilder.prototype.build = function(targets) {
-    var query = {
-      "aggs": {},
-      "size": "$maxDataPoints"
-    };
-
-    var self = this;
-    targets.forEach(function(target, index) {
-      if (target.hide) {
-        return;
-      }
+  ElasticQueryBuilder.prototype.build = function(target, timeFrom, timeTo) {
+    if (target.rawQuery) {
+      return angular.fromJson(target.rawJson);
+    }
 
 
-      var esQuery = {
-        "filter": {
-          "and": [
-            self._buildRangeFilter(target)
-          ]
-        },
-        "aggs": {
-          "date_histogram": {
-            "date_histogram": {
-              "interval": target.interval || "$interval",
-              "field": target.timestampField,
-              "min_doc_count": 0,
-            },
-            "aggs": {
-              "stats": {
-                "stats": {
-                  "field": target.valueField
+    var query = {
+      "size": 0,
+      "query": {
+        "filtered": {
+          "query": {
+            "query_string": {
+              "analyze_wildcard": true,
+              "query": target.query || '*' ,
+            }
+          },
+          "filter": {
+            "bool": {
+              "must": [
+                {
+                  "range": {
+                    "@timestamp": {
+                      "gte": timeFrom,
+                      "lte": timeTo
+                    }
+                  }
                 }
                 }
-              }
+              ],
+              "must_not": [
+
+              ]
             }
             }
           }
           }
         }
         }
-      };
-      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 = {
+      "date_histogram": {
+        "date_histogram": {
+          "interval": target.interval || "$interval",
+          "field": target.timeField,
+          "min_doc_count": 0,
+          "extended_bounds": {
+            "min": timeFrom,
+            "max": timeTo
           }
           }
-        };
+        }
       }
       }
+    };
 
 
-      query["aggs"]['query:' + index] = esQuery;
-    });
     query = JSON.stringify(query);
     query = JSON.stringify(query);
     return query;
     return query;
   };
   };

+ 11 - 7
public/app/plugins/datasource/elasticsearch/queryCtrl.js

@@ -21,15 +21,15 @@ function (angular, _, ElasticQueryBuilder) {
 
 
       var target = $scope.target;
       var target = $scope.target;
       target.function = target.function || 'mean';
       target.function = target.function || 'mean';
-      target.timestampField = target.timestampField || '@timestamp';
-      target.valueField = target.valueField || 'value' ;
+      target.timeField = target.timeField || '@timestamp';
+      target.select = target.select || [{ agg: 'count' }];
 
 
-      $scope.timestampSegment = uiSegmentSrv.newSegment(target.timestampField);
-      $scope.valueFieldSegment = uiSegmentSrv.newSegment(target.valueField);
-
-      $scope.termKeySegment = uiSegmentSrv.getSegmentForValue(target.termKey, 'select term field');
-      $scope.termValueSegment = uiSegmentSrv.getSegmentForValue(target.termValue, 'select term value');
+      $scope.timeSegment = uiSegmentSrv.newSegment(target.timeField);
       $scope.groupByFieldSegment = uiSegmentSrv.getSegmentForValue(target.groupByField, 'add group by');
       $scope.groupByFieldSegment = uiSegmentSrv.getSegmentForValue(target.groupByField, 'add group by');
+
+      $scope.selectSegments = _.map(target.select, function(select) {
+        return uiSegmentSrv.newSegment(select.agg);
+      });
     };
     };
 
 
     $scope.getFields = function() {
     $scope.getFields = function() {
@@ -101,6 +101,10 @@ function (angular, _, ElasticQueryBuilder) {
       return segments;
       return segments;
     };
     };
 
 
+    $scope.toggleQueryMode = function () {
+      $scope.target.rawQuery = !$scope.target.rawQuery;
+    };
+
     $scope.init();
     $scope.init();
 
 
   });
   });