Browse Source

HUE-7062 [dashboard] Convert arithmetic operators to their corresponding functions in the formula editor

Johan Ahlen 8 năm trước cách đây
mục cha
commit
2341c19

+ 52 - 15
desktop/core/src/desktop/static/desktop/js/autocomplete/jison/solrExpressionParser.jison

@@ -27,9 +27,9 @@
 [0-9]+(?:[,.][0-9]+)?                      { return 'NUMBER'; }
 
 '-'                                        { return '-'; }
-'*'                                        { return 'OPERATOR'; }
-'+'                                        { return 'OPERATOR'; }
-'/'                                        { return 'OPERATOR'; }
+'+'                                        { return '+'; }
+'*'                                        { return '*'; }
+'/'                                        { return '/'; }
 
 [a-z]+\s*\(                                {
                                              yy.lexer.unput('(');
@@ -52,7 +52,8 @@
 
 /lex
 
-%left '-' 'OPERATOR'
+%left '+' '-'
+%left '*' '/'
 
 %start SolrExpressionAutocomplete
 
@@ -61,7 +62,9 @@
 SolrExpressionAutocomplete
  : SolrExpression 'EOF'
    {
-     return {};
+     return {
+       parsedValue: $1
+     };
    }
  | SolrExpression_EDIT 'EOF'
    {
@@ -75,7 +78,7 @@ SolrExpressionAutocomplete
 
 SolrExpression
  : NonParenthesizedSolrExpression
- | '(' NonParenthesizedSolrExpression ')'
+ | '(' NonParenthesizedSolrExpression ')'  -> $1 + $2 + $3
  ;
 
 SolrExpression_EDIT
@@ -86,10 +89,12 @@ SolrExpression_EDIT
 NonParenthesizedSolrExpression
  : 'NUMBER'
  | 'IDENTIFIER'
- | 'FUNCTION' '(' ArgumentList ')'
- | SolrExpression 'OPERATOR' SolrExpression
- | SolrExpression '-' SolrExpression
- | '-' SolrExpression
+ | 'FUNCTION' '(' ArgumentList ')'    -> $1 + $2 + $3 + $4
+ | SolrExpression '+' SolrExpression  -> 'sum(' + $1 + ',' + $3 + ')'
+ | SolrExpression '-' SolrExpression  -> 'sub(' + $1 + ',' + $3 + ')'
+ | SolrExpression '*' SolrExpression  -> 'mul(' + $1 + ',' + $3 + ')'
+ | SolrExpression '/' SolrExpression  -> 'div(' + $1 + ',' + $3 + ')'
+ | '-' SolrExpression                 -> 'sub(0,' + $2 + ')'
  ;
 
 NonParenthesizedSolrExpression_EDIT
@@ -106,10 +111,10 @@ NonParenthesizedSolrExpression_EDIT
  ;
 
 NonParenthesizedSolrExpression_EDIT
- : SolrExpression 'OPERATOR' 'CURSOR'                                --> { suggestFunctions: true, suggestFields: true }
- | 'CURSOR' 'OPERATOR' SolrExpression                                --> { suggestFunctions: true, suggestFields: true }
- | SolrExpression_EDIT 'OPERATOR' SolrExpression                     --> $1
- | SolrExpression 'OPERATOR' SolrExpression_EDIT                     --> $3
+ : SolrExpression '+' 'CURSOR'                                       --> { suggestFunctions: true, suggestFields: true }
+ | 'CURSOR' '+' SolrExpression                                       --> { suggestFunctions: true, suggestFields: true }
+ | SolrExpression_EDIT '+' SolrExpression                            --> $1
+ | SolrExpression '+' SolrExpression_EDIT                            --> $3
  ;
 
 NonParenthesizedSolrExpression_EDIT
@@ -119,6 +124,20 @@ NonParenthesizedSolrExpression_EDIT
  | SolrExpression '-' SolrExpression_EDIT                            --> $3
  ;
 
+NonParenthesizedSolrExpression_EDIT
+ : SolrExpression '*' 'CURSOR'                                       --> { suggestFunctions: true, suggestFields: true }
+ | 'CURSOR' '*' SolrExpression                                       --> { suggestFunctions: true, suggestFields: true }
+ | SolrExpression_EDIT '*' SolrExpression                            --> $1
+ | SolrExpression '*' SolrExpression_EDIT                            --> $3
+ ;
+
+NonParenthesizedSolrExpression_EDIT
+ : SolrExpression '/' 'CURSOR'                                       --> { suggestFunctions: true, suggestFields: true }
+ | 'CURSOR' '/' SolrExpression                                       --> { suggestFunctions: true, suggestFields: true }
+ | SolrExpression_EDIT '/' SolrExpression                            --> $1
+ | SolrExpression '/' SolrExpression_EDIT                            --> $3
+ ;
+
 NonParenthesizedSolrExpression_EDIT
  : '-' 'CURSOR'                                                      --> { suggestFunctions: true, suggestFields: true }
  | '-' SolrExpression_EDIT                                           --> $2
@@ -179,7 +198,25 @@ parser.addFieldLocation = function (location, name) {
   parser.yy.locations.push({ type: 'field', name: name, location: adjustLocationForCursor(location) });
 }
 
-parser.parseSolrExpression = function (beforeCursor, afterCursor, debug) {
+parser.parseSolrExpression = function (expression, debug) {
+  parser.yy.cursorFound = false;
+  parser.yy.locations = [];
+  expression = expression.replace(/\r\n|\n\r/gm, '\n');
+
+  var result;
+  try {
+    result = parser.parse(expression);
+  } catch (err) {
+    if (debug) {
+      console.log(beforeCursor + '\u2020' + afterCursor);
+      console.log(err);
+      console.error(err.stack);
+    }
+  }
+  return result || false;
+}
+
+parser.autocompleteSolrExpression = function (beforeCursor, afterCursor, debug) {
   parser.yy.cursorFound = false;
   parser.yy.locations = [];
 

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/autocomplete/solrExpressionParser.js


+ 111 - 48
desktop/core/src/desktop/static/desktop/spec/autocomplete/solrExpressionParserSpec.js

@@ -16,67 +16,130 @@
 (function () {
   describe('solrExpressionParser.js', function () {
 
-    var testParser = function (beforeCursor, afterCursor, expectedResult) {
-      var result = solrExpressionParser.parseSolrExpression(beforeCursor, afterCursor, true);
-      if (!expectedResult.locations) {
-        delete result.locations;
-      }
-      expect(result).toEqual(expectedResult);
-    };
-
-    it('should suggest aggregate functions for "|"', function () {
-      testParser('', '', {
-        suggestAggregateFunctions: true
+    describe('autocomplete', function () {
+      var testAutocomplete = function (beforeCursor, afterCursor, expectedResult) {
+        var result = solrExpressionParser.autocompleteSolrExpression(beforeCursor, afterCursor, true);
+        if (!expectedResult.locations) {
+          delete result.locations;
+        }
+        expect(result).toEqual(expectedResult);
+      };
+
+      it('should suggest aggregate functions for "|"', function () {
+        testAutocomplete('', '', {
+          suggestAggregateFunctions: true
+        });
       });
-    });
 
-    it('should suggest functions and fields for "min(|"', function () {
-      testParser('min(', '', {
-        suggestFunctions: true,
-        suggestFields: true,
-        locations: [
-          { type: 'function', name: 'min', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 4 }}
-        ]
+      it('should suggest functions and fields for "min(|"', function () {
+        testAutocomplete('min(', '', {
+          suggestFunctions: true,
+          suggestFields: true,
+          locations: [
+            {type: 'function', name: 'min', location: {first_line: 1, last_line: 1, first_column: 1, last_column: 4}}
+          ]
+        });
       });
-    });
 
-    it('should suggest functions and fields for "min(boo + |"', function () {
-      testParser('min(boo + ', '', {
-        suggestFunctions: true,
-        suggestFields: true
+      it('should suggest functions and fields for "min(boo + |"', function () {
+        testAutocomplete('min(boo + ', '', {
+          suggestFunctions: true,
+          suggestFields: true
+        });
       });
-    });
 
-    it('should suggest functions and fields for "min(boo + | + baa)"', function () {
-      testParser('min(boo + ', ' + baa)', {
-        suggestFunctions: true,
-        suggestFields: true
+      it('should suggest functions and fields for "min(boo + | + baa)"', function () {
+        testAutocomplete('min(boo + ', ' + baa)', {
+          suggestFunctions: true,
+          suggestFields: true
+        });
       });
-    });
 
-    it('should suggest functions and fields for "min(1- max(|"', function () {
-      testParser('min(1- max(', '', {
-        suggestFunctions: true,
-        suggestFields: true
+      it('should suggest functions and fields for "min(1- max(|"', function () {
+        testAutocomplete('min(1- max(', '', {
+          suggestFunctions: true,
+          suggestFields: true
+        });
       });
-    });
 
-    it('should suggest operators for "min(boo + 1) - 4 + mul(10, baa)|"', function () {
-      testParser('min(boo + 1) - 4 + mul(10, baa)', '', {
-        suggestOperators: true,
-        locations: [
-          { type: 'function', name: 'min', location: { first_line: 1, last_line: 1, first_column: 1, last_column: 4 } },
-          { type: 'field', name: 'boo', location: { first_line: 1, last_line: 1, first_column: 5, last_column: 8 } },
-          { type: 'function', name: 'mul', location: { first_line: 1, last_line: 1, first_column: 20, last_column: 23 } },
-          { type: 'field', name: 'baa', location: { first_line: 1, last_line: 1, first_column: 28, last_column: 31 } }
-        ]
+      it('should suggest operators for "min(boo + 1) - 4 + mul(10, baa)|"', function () {
+        testAutocomplete('min(boo + 1) - 4 + mul(10, baa)', '', {
+          suggestOperators: true,
+          locations: [
+            {type: 'function', name: 'min', location: {first_line: 1, last_line: 1, first_column: 1, last_column: 4}},
+            {type: 'field', name: 'boo', location: {first_line: 1, last_line: 1, first_column: 5, last_column: 8}},
+            {type: 'function', name: 'mul', location: {first_line: 1, last_line: 1, first_column: 20, last_column: 23}},
+            {type: 'field', name: 'baa', location: {first_line: 1, last_line: 1, first_column: 28, last_column: 31}}
+          ]
+        });
       });
-    });
 
-    it('should suggest operators for "min(boo |"', function () {
-      testParser('min(boo ', '', {
-        suggestOperators: true
+      it('should suggest operators for "min(boo |"', function () {
+        testAutocomplete('min(boo ', '', {
+          suggestOperators: true
+        });
       });
     });
+
+    describe('parse', function () {
+      var testParse = function (expression, expectedResult) {
+        var result = solrExpressionParser.parseSolrExpression(expression);
+        expect(result).toBeTruthy();
+        expect(result).toEqual(expectedResult);
+      };
+
+      it('should parse "min(boo)"', function () {
+        testParse('min(boo)', {
+          parsedValue: 'min(boo)'
+        });
+      });
+
+      it('should fail parsing "min(boo"', function () {
+        var result = solrExpressionParser.parseSolrExpression('min(boo');
+        expect(result).toBeFalsy();
+      });
+
+      it('should convert + to sum for "min(boo + 1)"', function () {
+        testParse('min(boo + 1)', {
+          parsedValue: 'min(sum(boo,1))'
+        });
+      });
+
+      it('should convert - to sub for "10 - min(boo + 1)"', function () {
+        testParse('10 - min(boo + 1)', {
+          parsedValue: 'sub(10,min(sum(boo,1)))'
+        });
+      });
+
+      it('should convert - to sub for "-min(boo)"', function () {
+        testParse('-min(boo)', {
+          parsedValue: 'sub(0,min(boo))'
+        });
+      });
+
+      it('should convert / to div for "min(boo)/10"', function () {
+        testParse('min(boo)/10', {
+          parsedValue: 'div(min(boo),10)'
+        });
+      });
+
+      it('should convert * to mul for "1*2*3*4"', function () {
+        testParse('1*2*3*4', {
+          parsedValue: 'mul(mul(mul(1,2),3),4)'
+        });
+      });
+
+      it('should handle precedence properly  "1*2+3*4"', function () {
+        testParse('1*2+3*4', {
+          parsedValue: 'sum(mul(1,2),mul(3,4))'
+        });
+      });
+
+      it('should handle precedence properly with parentheses "1*(2+3)*4"', function () {
+        testParse('1*(2+3)*4', {
+          parsedValue: 'mul(mul(1,(sum(2,3))),4)'
+        });
+      });
+    })
   });
 })();

+ 26 - 1
desktop/core/src/desktop/templates/ko_components/ko_simple_ace_editor.mako

@@ -212,6 +212,13 @@ from desktop.views import _ko
             draggable: 'ms()',
             description: 'Returns milliseconds of difference between its arguments. Dates are relative to the Unix or POSIX time epoch, midnight, January 1, 1970 UTC. Arguments may be the name of an indexed TrieDateField, or date math based on a constant date or NOW.\n\n- ms(): Equivalent to ms(NOW), number of milliseconds since the epoch.\n- ms(a): Returns the number of milliseconds since the epoch that the argument represents.\n- ms(a,b) : Returns the number of milliseconds that b occurs before a (that is, a - b)'
           },
+          mul: {
+            returnTypes: ['T'],
+            arguments: [[{type: 'T', multiple: true}]],
+            signature: 'mul(x, y, ...)',
+            draggable: 'mul()',
+            description: 'Returns the product of multiple values or functions, which are specified in a comma-separated list. Same as the product function.'
+          },
           norm: {
             returnTypes: ['T'],
             arguments: [[{type: 'T'}]],
@@ -549,10 +556,14 @@ from desktop.views import _ko
 
         SolrFormulaAutocompleter.prototype.autocomplete = function () {
           var self = this;
-          var parseResult = solrExpressionParser.parseSolrExpression(self.editor.getTextBeforeCursor(), self.editor.getTextAfterCursor());
+          var parseResult = solrExpressionParser.autocompleteSolrExpression(self.editor.getTextBeforeCursor(), self.editor.getTextAfterCursor());
           self.suggestions.update(parseResult);
         };
 
+        SolrFormulaAutocompleter.prototype.parse = function (value) {
+          return solrExpressionParser.parseSolrExpression(value);
+        };
+
         return SolrFormulaAutocompleter;
       })();
 
@@ -564,6 +575,7 @@ from desktop.views import _ko
         var $element = $(element);
         var self = this;
         self.value = params.value;
+        self.parsedValue = params.parsedValue;
         self.ace = ko.observable();
         self.disposeFunctions = [];
 
@@ -665,8 +677,21 @@ from desktop.views import _ko
           });
         }
 
+        var parseThrottle = -1;
         var inputListener = editor.on('input', function () {
           self.value(editor.getValue());
+          if (self.parsedValue && self.autocompleter && self.autocompleter.parse) {
+            window.clearTimeout(parseThrottle);
+            parseThrottle = window.setTimeout(function () {
+              var parseResult = self.autocompleter.parse(editor.getValue());
+              if (parseResult) {
+                self.parsedValue(parseResult.parsedValue)
+              } else {
+                // TODO: What to do when we can't parse?
+                self.parsedValue(editor.getValue());
+              }
+            }, 200);
+          }
         });
 
         self.disposeFunctions.push(function () {

+ 1 - 2
desktop/libs/dashboard/src/dashboard/templates/common_search.mako

@@ -2007,8 +2007,7 @@ ${ dashboard.layout_skeleton(suffix='search') }
       <div class="clearfix"></div>
       <br/>
 
-      <div data-bind="component: { name: 'hue-simple-ace-editor', params: { value: formula, autocomplete: { type: 'solrFormula', support: { fields: $root.collection.template.fieldsAttributes } }, singleLine: true } }, visible: $parent.field() == 'formula'"></div>
-      <input data-bind="value: plain_formula" type="hidden"></input>
+      <div data-bind="component: { name: 'hue-simple-ace-editor', params: { value: plain_formula, parsedValue: formula, autocomplete: { type: 'solrFormula', support: { fields: $root.collection.template.fieldsAttributes } }, singleLine: true } }, visible: $parent.field() == 'formula'"></div>
 
       <!-- ko if: $data.function() != 'field' && $parents[1].widgetType() != 'hit-widget' -->
         <div class="facet-field-cnt">

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác