소스 검색

use sqlPart for ui parts

Sven Klemm 7 년 전
부모
커밋
410449b5e7

+ 7 - 2
public/app/plugins/datasource/postgres/partials/query.editor.html

@@ -18,8 +18,13 @@
         <label class="gf-form-label query-keyword width-5">WHERE</label>
       </div>
 
-      <div class="gf-form" ng-repeat="segment in ctrl.whereSegments">
-        <metric-segment segment="segment" get-options="ctrl.getWhereSegments(segment, $index)" on-change="ctrl.whereSegmentUpdated(segment, $index)"></metric-segment>
+      <div class="gf-form" ng-repeat="part in ctrl.whereSegments">
+        <sql-part-editor class="gf-form-label sql-part" part="part" handle-event="ctrl.handleWherePartEvent(ctrl.whereSegments, part, $event, $index)">
+        </sql-part-editor>
+      </div>
+
+      <div class="gf-form">
+        <metric-segment segment="ctrl.whereAdd" get-options="ctrl.getWhereOptions()" on-change="ctrl.whereAddAction(part, $index)"></metric-segment>
       </div>
 
       <div class="gf-form gf-form--grow">

+ 72 - 128
public/app/plugins/datasource/postgres/query_ctrl.ts

@@ -1,8 +1,8 @@
-import angular from 'angular';
 import _ from 'lodash';
 import { PostgresQueryBuilder } from './query_builder';
 import { QueryCtrl } from 'app/plugins/sdk';
 import PostgresQuery from './postgres_query';
+import sqlPart from './query_part';
 
 export interface QueryMeta {
   sql: string;
@@ -30,11 +30,11 @@ export class PostgresQueryCtrl extends QueryCtrl {
   schemaSegment: any;
   tableSegment: any;
   whereSegments: any;
+  whereAdd: any;
   timeColumnSegment: any;
   metricColumnSegment: any;
   selectMenu: any;
   groupBySegment: any;
-  removeWhereFilterSegment: any;
 
   /** @ngInject **/
   constructor($scope, $injector, private templateSrv, private $q, private uiSegmentSrv) {
@@ -55,12 +55,12 @@ export class PostgresQueryCtrl extends QueryCtrl {
       }
     }
 
-    this.schemaSegment= uiSegmentSrv.newSegment(this.target.schema);
+    this.schemaSegment = uiSegmentSrv.newSegment(this.target.schema);
 
     if (!this.target.table) {
-      this.tableSegment = uiSegmentSrv.newSegment({value: 'select table',fake: true});
+      this.tableSegment = uiSegmentSrv.newSegment({ value: 'select table', fake: true });
     } else {
-      this.tableSegment= uiSegmentSrv.newSegment(this.target.table);
+      this.tableSegment = uiSegmentSrv.newSegment(this.target.table);
     }
 
     this.timeColumnSegment = uiSegmentSrv.newSegment(this.target.timeColumn);
@@ -68,23 +68,19 @@ export class PostgresQueryCtrl extends QueryCtrl {
 
     this.buildSelectMenu();
     this.buildWhereSegments();
+    this.whereAdd = this.uiSegmentSrv.newPlusButton();
     this.groupBySegment = this.uiSegmentSrv.newPlusButton();
 
-    this.removeWhereFilterSegment = uiSegmentSrv.newSegment({
-      fake: true,
-      value: '-- remove filter --',
-    });
     this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
     this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
-
   }
 
   buildSelectMenu() {
     this.selectMenu = [
-      {text: "aggregate", value: "aggregate"},
-      {text: "math", value: "math"},
-      {text: "alias", value: "alias"},
-      {text: "column", value: "column"},
+      { text: 'Aggregate', value: 'aggregate' },
+      { text: 'Math', value: 'math' },
+      { text: 'Alias', value: 'alias' },
+      { text: 'Column', value: 'column' },
     ];
   }
 
@@ -108,14 +104,14 @@ export class PostgresQueryCtrl extends QueryCtrl {
 
   getTimeColumnSegments() {
     return this.datasource
-      .metricFindQuery(this.queryBuilder.buildColumnQuery("time"))
+      .metricFindQuery(this.queryBuilder.buildColumnQuery('time'))
       .then(this.transformToSegments(true))
       .catch(this.handleQueryError.bind(this));
   }
 
   getMetricColumnSegments() {
     return this.datasource
-      .metricFindQuery(this.queryBuilder.buildColumnQuery("metric"))
+      .metricFindQuery(this.queryBuilder.buildColumnQuery('metric'))
       .then(this.transformToSegments(true))
       .catch(this.handleQueryError.bind(this));
   }
@@ -186,7 +182,7 @@ export class PostgresQueryCtrl extends QueryCtrl {
   }
 
   addSelectPart(selectParts, cat, subitem) {
-    if ("submenu" in cat) {
+    if ('submenu' in cat) {
       this.queryModel.addSelectPart(selectParts, subitem.value);
     } else {
       this.queryModel.addSelectPart(selectParts, cat.value);
@@ -198,18 +194,17 @@ export class PostgresQueryCtrl extends QueryCtrl {
     switch (evt.name) {
       case 'get-param-options': {
         switch (part.def.type) {
-          case "aggregate":
+          case 'aggregate':
             return this.datasource
               .metricFindQuery(this.queryBuilder.buildAggregateQuery())
               .then(this.transformToSegments(false))
               .catch(this.handleQueryError.bind(this));
-          case "column":
+          case 'column':
             return this.datasource
-              .metricFindQuery(this.queryBuilder.buildColumnQuery("value"))
+              .metricFindQuery(this.queryBuilder.buildColumnQuery('value'))
               .then(this.transformToSegments(true))
               .catch(this.handleQueryError.bind(this));
         }
-
       }
       case 'part-param-changed': {
         this.panelCtrl.refresh();
@@ -251,125 +246,75 @@ export class PostgresQueryCtrl extends QueryCtrl {
 
   buildWhereSegments() {
     this.whereSegments = [];
-    for (let constraint of this.target.where) {
-
-      if (constraint.condition) {
-        this.whereSegments.push(this.uiSegmentSrv.newCondition(constraint.condition));
-      }
-      this.whereSegments.push(this.uiSegmentSrv.newKey(constraint.key));
-      this.whereSegments.push(this.uiSegmentSrv.newOperator(constraint.operator));
-      this.whereSegments.push(this.uiSegmentSrv.newKeyValue(constraint.value));
-    }
-
-    var count = this.whereSegments.length;
-    var lastSegment = this.whereSegments[Math.max(count - 1, 0)];
-
-    if (!lastSegment || lastSegment.type !== 'plus-button') {
-      this.whereSegments.push(this.uiSegmentSrv.newPlusButton());
-    }
+    this.whereSegments.push(sqlPart.create({ type: 'expression', params: ['value', '=', 'value'] }));
+    //    for (let constraint of this.target.where) {
+    //
+    //      this.whereSegments.push(sqlPart.create({type: 'column',params: ['1']}));
+    //      if (constraint.condition) {
+    //        this.whereSegments.push(this.uiSegmentSrv.newCondition(constraint.condition));
+    //      }
+    //      this.whereSegments.push(this.uiSegmentSrv.newKey(constraint.key));
+    //      this.whereSegments.push(this.uiSegmentSrv.newOperator(constraint.operator));
+    //      this.whereSegments.push(this.uiSegmentSrv.newKeyValue(constraint.value));
+    //    }
   }
 
-  getWhereSegments(segment, index) {
-    var query, addTemplateVars;
-
-    if (segment.type === 'condition') {
-      return this.$q.when([this.uiSegmentSrv.newSegment('AND'), this.uiSegmentSrv.newSegment('OR')]);
-    }
-    if (segment.type === 'operator') {
-      var columnName = this.whereSegments[index - 1].value;
-      query = this.queryBuilder.buildDatatypeQuery(columnName);
-      return this.datasource.metricFindQuery(query)
-      .then(results => {
-        var datatype = results[0].text;
-        switch (datatype) {
-          case "text":
-          case "character":
-          case "character varying":
-            return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '~', '~*','!~','!~*','IN']));
+  handleWherePartEvent(whereParts, part, evt, index) {
+    switch (evt.name) {
+      case 'get-param-options': {
+        switch (evt.param.name) {
+          case 'left':
+            return this.datasource
+              .metricFindQuery(this.queryBuilder.buildColumnQuery())
+              .then(this.transformToSegments(false))
+              .catch(this.handleQueryError.bind(this));
+          case 'right':
+            return this.datasource
+              .metricFindQuery(this.queryBuilder.buildValueQuery(part.params[0]))
+              .then(this.transformToSegments(true))
+              .catch(this.handleQueryError.bind(this));
+          case 'op':
+            return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<', '<=', '>', '>=', 'IN']));
           default:
-            return this.$q.when(this.uiSegmentSrv.newOperators(['=', '!=', '<', '<=', '>', '>=']));
-        }
-      })
-      .catch(this.handleQueryError.bind(this));
-    }
-
-    if (segment.type === 'key' || segment.type === 'plus-button') {
-      query = this.queryBuilder.buildColumnQuery();
-
-      addTemplateVars = false;
-    } else if (segment.type === 'value') {
-      query = this.queryBuilder.buildValueQuery(this.whereSegments[index -2].value);
-      addTemplateVars = true;
-    }
-
-    return this.datasource
-      .metricFindQuery(query)
-      .then(this.transformToSegments(addTemplateVars))
-      .then(results => {
-        if (segment.type === 'key') {
-          results.splice(0, 0, angular.copy(this.removeWhereFilterSegment));
-        }
-        return results;
-      })
-      .catch(this.handleQueryError.bind(this));
-  }
-
-  whereSegmentUpdated(segment, index) {
-    this.whereSegments[index] = segment;
-
-    // handle remove where condition
-    if (segment.value === this.removeWhereFilterSegment.value) {
-      this.whereSegments.splice(index, 3);
-      if (this.whereSegments.length === 0) {
-        this.whereSegments.push(this.uiSegmentSrv.newPlusButton());
-      } else if (this.whereSegments.length > 2) {
-        this.whereSegments.splice(Math.max(index - 1, 0), 1);
-        if (this.whereSegments[this.whereSegments.length - 1].type !== 'plus-button') {
-          this.whereSegments.push(this.uiSegmentSrv.newPlusButton());
+            return Promise.resolve([]);
         }
       }
-    } else {
-      if (segment.type === 'plus-button') {
-        if (index > 2) {
-          this.whereSegments.splice(index, 0, this.uiSegmentSrv.newCondition('AND'));
-        }
-        this.whereSegments.push(this.uiSegmentSrv.newOperator('='));
-        this.whereSegments.push(this.uiSegmentSrv.newFake('select value', 'value', 'query-segment-value'));
-        segment.type = 'key';
-        segment.cssClass = 'query-segment-key';
+      case 'part-param-changed': {
+        this.panelCtrl.refresh();
+        break;
       }
-
-      if (index + 1 === this.whereSegments.length) {
-        this.whereSegments.push(this.uiSegmentSrv.newPlusButton());
+      case 'action': {
+        whereParts.splice(whereParts.indexOf(part), 1);
+        this.panelCtrl.refresh();
+        break;
+      }
+      case 'get-part-actions': {
+        return this.$q.when([{ text: 'Remove', value: 'remove-part' }]);
       }
     }
-
-    this.rebuildTargetWhereConditions();
   }
 
-  rebuildTargetWhereConditions() {
-    var where = [];
-    var tagIndex = 0;
+  getWhereOptions() {
+    var options = [];
+    options.push(this.uiSegmentSrv.newSegment({ type: 'function', value: '$__timeFilter' }));
+    options.push(this.uiSegmentSrv.newSegment({ type: 'function', value: '$__unixEpochFilter' }));
+    options.push(this.uiSegmentSrv.newSegment({ type: 'function', value: 'Expression' }));
+    return Promise.resolve(options);
+  }
 
-    _.each(this.whereSegments, (segment2, index) => {
-      if (segment2.type === 'key') {
-        if (where.length === 0) {
-          where.push({});
-        }
-        where[tagIndex].key = segment2.value;
-      } else if (segment2.type === 'value') {
-        where[tagIndex].value = segment2.value;
-      } else if (segment2.type === 'template') {
-        where[tagIndex].value = segment2.value;
-      } else if (segment2.type === 'condition') {
-        where.push({ condition: segment2.value });
-        tagIndex += 1;
-      } else if (segment2.type === 'operator') {
-        where[tagIndex].operator = segment2.value;
+  whereAddAction(part, index) {
+    switch (this.whereAdd.type) {
+      case 'macro': {
+        this.whereSegments.push(
+          sqlPart.create({ type: 'function', name: this.whereAdd.value, params: ['value', '=', 'value'] })
+        );
+      }
+      default: {
+        this.whereSegments.push(sqlPart.create({ type: 'expression', params: ['value', '=', 'value'] }));
       }
-    });
+    }
 
-    this.target.where = where;
+    this.whereAdd = this.uiSegmentSrv.newPlusButton();
     this.panelCtrl.refresh();
   }
 
@@ -406,5 +351,4 @@ export class PostgresQueryCtrl extends QueryCtrl {
     this.error = err.message || 'Failed to issue metric query';
     return [];
   }
-
 }

+ 26 - 3
public/app/plugins/datasource/postgres/query_part.ts

@@ -32,7 +32,7 @@ function replaceAggregationAddStrategy(selectParts, partModel) {
   // look for existing aggregation
   for (var i = 0; i < selectParts.length; i++) {
     var part = selectParts[i];
-    if (part.def.type === "aggregate") {
+    if (part.def.type === 'aggregate') {
       selectParts[i] = partModel;
       return;
     }
@@ -83,6 +83,15 @@ function addColumnStrategy(selectParts, partModel, query) {
   query.selectModels.push(parts);
 }
 
+function addExpressionStrategy(selectParts, partModel, query) {
+  // copy all parts
+  var parts = _.map(selectParts, function(part: any) {
+    return createPart({ type: part.def.type, params: _.clone(part.params) });
+  });
+
+  query.selectModels.push(parts);
+}
+
 register({
   type: 'column',
   style: 'label',
@@ -92,11 +101,25 @@ register({
   renderer: columnRenderer,
 });
 
+register({
+  type: 'expression',
+  style: 'expression',
+  label: 'Expr:',
+  addStrategy: addExpressionStrategy,
+  params: [
+    { name: 'left', type: 'string', dynamicLookup: true },
+    { name: 'op', type: 'string', dynamicLookup: true },
+    { name: 'right', type: 'string', dynamicLookup: true },
+  ],
+  defaultParams: ['value', '=', 'value'],
+  renderer: columnRenderer,
+});
+
 register({
   type: 'aggregate',
   style: 'label',
   addStrategy: replaceAggregationAddStrategy,
-  params: [{name: 'name', type: 'string', dynamicLookup: true}],
+  params: [{ name: 'name', type: 'string', dynamicLookup: true }],
   defaultParams: ['avg'],
   renderer: aggregateRenderer,
 });
@@ -136,7 +159,7 @@ register({
       options: ['none', 'NULL', '0'],
     },
   ],
-  defaultParams: ['$__interval','none'],
+  defaultParams: ['$__interval', 'none'],
   renderer: functionRenderer,
 });