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

More work on new influxdb query editor, #1525

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

+ 1 - 1
public/app/plugins/datasource/influxdb/partials/query.editor.html

@@ -91,7 +91,7 @@
 					time($interval)
 				</li>
 				<li ng-repeat="segment in groupBySegments">
-					<metric-segment segment="segment" get-alt-segments="getTagsOrValues(segment, 0)" on-value-changed="groupByTagUpdated(segment, $index)"></metric-segment>
+					<metric-segment segment="segment" get-alt-segments="getGroupByTagSegments(segment, 0)" on-value-changed="groupByTagUpdated(segment, $index)"></metric-segment>
 				</li>
 				<li class="dropdown">
 					<a class="tight-form-item pointer" data-toggle="dropdown" bs-tooltip="'Insert missing values, important when stacking'" data-placement="right">

+ 11 - 5
public/app/plugins/datasource/influxdb/queryBuilder.js

@@ -1,5 +1,5 @@
 define([
-    'lodash'
+  'lodash'
 ],
 function (_) {
   'use strict';
@@ -32,12 +32,18 @@ function (_) {
     }
 
     query +=  aggregationFunc + '(value)';
-    query += ' FROM ' + measurement + ' WHERE $timeFilter';
-    query += _.map(target.tags, function(tag) {
-      return ' AND ' + tag.key + '=' + "'" + tag.value + "'";
-    }).join('');
+    query += ' FROM ' + measurement + ' WHERE ';
+    var conditions = _.map(target.tags, function(tag) {
+      return tag.key + '=' + "'" + tag.value + "' ";
+    });
+    conditions.push('$timeFilter');
+
+    query += conditions.join('AND ');
 
     query += ' GROUP BY time($interval)';
+    if  (target.groupByTags && target.groupByTags.length > 0) {
+      query += ', ' + target.groupByTags.join();
+    }
 
     if (target.fill) {
       query += ' fill(' + target.fill + ')';

+ 63 - 16
public/app/plugins/datasource/influxdb/queryCtrl.js

@@ -35,14 +35,12 @@ function (angular, _) {
         if (tag.condition) {
           $scope.tagSegments.push(MetricSegment.newCondition(tag.condition));
         }
-        $scope.tagSegments.push(new MetricSegment({value: tag.key, type: 'key' }));
-        $scope.tagSegments.push(new MetricSegment({fake: true, value: "="}));
-        $scope.tagSegments.push(new MetricSegment({value: tag.value, type: 'value'}));
+        $scope.tagSegments.push(new MetricSegment({value: tag.key, type: 'key', cssClass: 'query-segment-key' }));
+        $scope.tagSegments.push(new MetricSegment({fake: true, value: "=", cssClass: 'query-segment-operator'}));
+        $scope.tagSegments.push(new MetricSegment({value: tag.value, type: 'value', cssClass: 'query-segment-value'}));
       });
 
-      if ($scope.tagSegments.length % 3 === 0) {
-        $scope.tagSegments.push(MetricSegment.newPlusButton());
-      }
+      $scope.fixTagSegments();
 
       $scope.groupBySegments = [];
       _.each(target.groupByTags, function(tag) {
@@ -50,12 +48,37 @@ function (angular, _) {
       });
 
       $scope.groupBySegments.push(MetricSegment.newPlusButton());
+
+      $scope.removeTagFilterSegment = new MetricSegment({fake: true, value: 'remove tag filter'});
+      $scope.removeGroupBySegment = new MetricSegment({fake: true, value: 'remove group by'});
+    };
+
+    $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) {
@@ -65,7 +88,6 @@ function (angular, _) {
 
     $scope.measurementChanged = function() {
       $scope.target.measurement = $scope.measurementSegment.value;
-      console.log('measurement updated', $scope.target.measurement);
       $scope.$parent.get_data();
     };
 
@@ -126,8 +148,23 @@ function (angular, _) {
       .then($scope.transformToSegments)
       .then($scope.addTemplateVariableSegments)
       .then(function(results) {
-        if (queryType === 'TAG_KEYS' && segment.type !== 'plus-button') {
-          results.push(new MetricSegment({fake: true, value: 'remove tag filter'}));
+        if (queryType === 'TAG_KEYS' && segment.type === 'key') {
+          results.push(angular.copy($scope.removeTagFilterSegment));
+        }
+        return results;
+      })
+      .then(null, $scope.handleQueryError);
+    };
+
+    $scope.getGroupByTagSegments = function(segment) {
+      var query = 'SHOW TAG KEYS FROM "' + $scope.target.measurement + '"';
+
+      return $scope.datasource.metricFindQuery(query, 'TAG_KEYS')
+      .then($scope.transformToSegments)
+      .then($scope.addTemplateVariableSegments)
+      .then(function(results) {
+        if (segment.type !== 'plus-button') {
+          results.push(angular.copy($scope.removeGroupBySegment));
         }
         return results;
       })
@@ -137,13 +174,15 @@ function (angular, _) {
     $scope.tagSegmentUpdated = function(segment, index) {
       $scope.tagSegments[index] = segment;
 
-      if (segment.value === 'remove tag filter') {
+      if (segment.value === $scope.removeTagFilterSegment.value) {
         $scope.tagSegments.splice(index, 3);
         if ($scope.tagSegments.length === 0) {
           $scope.tagSegments.push(MetricSegment.newPlusButton());
-        } else {
-          $scope.tagSegments.splice(index-1, 1);
-          $scope.tagSegments.push(MetricSegment.newPlusButton());
+        } else if ($scope.tagSegments.length > 2) {
+          $scope.tagSegments.splice(Math.max(index-1, 0), 1);
+          if ($scope.tagSegments[$scope.tagSegments.length-1].type !== 'plus-button') {
+            $scope.tagSegments.push(MetricSegment.newPlusButton());
+          }
         }
       }
       else {
@@ -151,9 +190,10 @@ function (angular, _) {
           if (index > 2) {
             $scope.tagSegments.splice(index, 0, MetricSegment.newCondition('AND'));
           }
-          $scope.tagSegments.push(new MetricSegment({fake: true, value: '=', type: 'operator'}));
-          $scope.tagSegments.push(new MetricSegment({fake: true, value: 'select tag value', type: 'value' }));
+          $scope.tagSegments.push(MetricSegment.newFake('=', 'operator', 'query-segment-operator'));
+          $scope.tagSegments.push(MetricSegment.newFake('select tag value', 'value', 'query-segment-value'));
           segment.type = 'key';
+          segment.cssClass = 'query-segment-key';
         }
 
         if ((index+1) === $scope.tagSegments.length) {
@@ -165,10 +205,13 @@ function (angular, _) {
     };
 
     $scope.rebuildTargetTagConditions = function() {
-      var tags = [{}];
+      var tags = [];
       var tagIndex = 0;
       _.each($scope.tagSegments, function(segment2) {
         if (segment2.type === 'key') {
+          if (tags.length === 0) {
+            tags.push({});
+          }
           tags[tagIndex].key = segment2.value;
         }
         else if (segment2.type === 'value') {
@@ -211,6 +254,10 @@ function (angular, _) {
       return new MetricSegment({value: 'select measurement', fake: true});
     };
 
+    MetricSegment.newFake = function(text, type, cssClass) {
+      return new MetricSegment({value: text, fake: true, type: type, cssClass: cssClass});
+    };
+
     MetricSegment.newCondition = function(condition) {
       return new MetricSegment({value: condition, type: 'condition', cssClass: 'query-keyword' });
     };

+ 12 - 0
public/css/less/grafana.less

@@ -343,4 +343,16 @@
   color: @blue;
 }
 
+.query-segment-key {
+  border-right: none;
+  padding-right: 1px;
+}
+
+.query-segment-operator {
+  padding-right: 1px;
+  border-right: none;
+  color: @orange;
+}
+
+
 

+ 32 - 3
public/test/specs/influx09-querybuilder-specs.js

@@ -18,7 +18,7 @@ define([
 
     });
 
-    describe('series with tags only', function() {
+    describe('series with single tag only', function() {
       var builder = new InfluxQueryBuilder({
         measurement: 'cpu',
         tags: [{key: 'hostname', value: 'server1'}]
@@ -27,10 +27,39 @@ define([
       var query = builder.build();
 
       it('should generate correct query', function() {
-        expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE $timeFilter ' +
-          'AND hostname=\'server1\' GROUP BY time($interval) ORDER BY asc');
+        expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE hostname=\'server1\' AND $timeFilter'
+                            + ' GROUP BY time($interval) ORDER BY asc');
+      });
+
+    });
+
+    describe('series with multiple tags only', function() {
+      var builder = new InfluxQueryBuilder({
+        measurement: 'cpu',
+        tags: [{key: 'hostname', value: 'server1'}, {key: 'app', value: 'email', condition: "AND"}]
+      });
+
+      var query = builder.build();
+
+      it('should generate correct query', function() {
+        expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE hostname=\'server1\' AND app=\'email\' AND ' +
+                            '$timeFilter GROUP BY time($interval) ORDER BY asc');
+      });
+    });
+
+    describe('series with groupByTag', function() {
+      var builder = new InfluxQueryBuilder({
+        measurement: 'cpu',
+        tags: [],
+        groupByTags: ["host"]
       });
 
+      var query = builder.build();
+
+      it('should generate correct query', function() {
+        expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE $timeFilter ' +
+          'GROUP BY time($interval), host ORDER BY asc');
+      });
     });
 
   });

+ 84 - 2
public/test/specs/influxdbQueryCtrl-specs.js

@@ -101,13 +101,31 @@ define([
       });
     });
 
-    describe('when deleting is changed', function() {
+    describe('when deleting first tag filter after value is selected', function() {
+      beforeEach(function() {
+        ctx.scope.init();
+        ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0);
+        ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2);
+        ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 0);
+      });
+
+      it('should remove tags', function() {
+        expect(ctx.scope.target.tags.length).to.be(0);
+      });
+
+      it('should remove all segment after 2 and replace with plus button', function() {
+        expect(ctx.scope.tagSegments.length).to.be(1);
+        expect(ctx.scope.tagSegments[0].type).to.be('plus-button');
+      });
+    });
+
+    describe('when deleting second tag value before second tag value is complete', function() {
       beforeEach(function() {
         ctx.scope.init();
         ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0);
         ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2);
         ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3);
-        ctx.scope.tagSegmentUpdated({value: 'remove tag filter', type: 'key'}, 4);
+        ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4);
       });
 
       it('should remove all segment after 2 and replace with plus button', function() {
@@ -116,5 +134,69 @@ define([
       });
     });
 
+    describe('when deleting second tag value before second tag value is complete', function() {
+      beforeEach(function() {
+        ctx.scope.init();
+        ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0);
+        ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2);
+        ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3);
+        ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4);
+      });
+
+      it('should remove all segment after 2 and replace with plus button', function() {
+        expect(ctx.scope.tagSegments.length).to.be(4);
+        expect(ctx.scope.tagSegments[3].type).to.be('plus-button');
+      });
+    });
+
+    describe('when deleting second tag value after second tag filter is complete', function() {
+      beforeEach(function() {
+        ctx.scope.init();
+        ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0);
+        ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2);
+        ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3);
+        ctx.scope.tagSegmentUpdated({value: 'value', type: 'value'}, 6);
+        ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4);
+      });
+
+      it('should remove all segment after 2 and replace with plus button', function() {
+        expect(ctx.scope.tagSegments.length).to.be(4);
+        expect(ctx.scope.tagSegments[3].type).to.be('plus-button');
+      });
+    });
+
+    describe('when adding group by', function() {
+      beforeEach(function() {
+        ctx.scope.init();
+        ctx.scope.groupByTagUpdated({value: 'host', type: 'plus-button' }, 0);
+      });
+
+      it('should add group by', function() {
+        expect(ctx.scope.target.groupByTags.length).to.be(1);
+        expect(ctx.scope.target.groupByTags[0]).to.be('host');
+      });
+
+      it('should add another plus button segment', function() {
+        expect(ctx.scope.groupBySegments[1].type).to.be('plus-button');
+      });
+    });
+
+    describe('when removing group by', function() {
+      beforeEach(function() {
+        ctx.scope.init();
+        ctx.scope.groupByTagUpdated({value: 'host', type: 'plus-button' }, 0);
+        ctx.scope.groupByTagUpdated(ctx.scope.removeGroupBySegment, 0);
+      });
+
+      it('should add group by', function() {
+        expect(ctx.scope.target.groupByTags.length).to.be(0);
+      });
+
+      it('should remove segment', function() {
+        expect(ctx.scope.groupBySegments.length).to.be(1);
+        expect(ctx.scope.groupBySegments[0].type).to.be('plus-button');
+      });
+    });
+
   });
 });