Repository: alibaba/GraphScope (language: python)

Issue:
[BUG] `Not` Operator Precedence Is Inconsistent with Implementation Layer
**Describe the bug**

for query `g.V().has('name', notContaining('mar'))`, compiler will generate the following filtering condition
```
NOT(POSIX REGEX CASE SENSITIVE(DEFAULT.name, _UTF-8'.*mar.*'))
```
In Calcite, `NOT` precedence is lower than `POSIX REGEX CASE SENSITIVE`, so the brace of `POSIX REGEX CASE SENSITIVE(DEFAULT.name, _UTF-8'.*mar.*')` is unnecessary. However, in implementation layer (i.e. c++), the `NOT` is implemented by `!`, which precedence is higher than `POSIX REGEX CASE SENSITIVE`, the execution order is changed, thus leads the execution errors.



Discussion / hints:

Fix patch:
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java
index 910460583658..297af3f0f1cc 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/RexToProtoConverter.java
@@ -293,8 +293,19 @@ private OuterExpression.Expression visitBinaryOperator(RexCall call) {
     }
 
     private boolean needBrace(SqlOperator operator, RexNode operand) {
-        return operand instanceof RexCall
-                && ((RexCall) operand).getOperator().getLeftPrec() <= operator.getLeftPrec();
+        switch (operator.getKind()) {
+            case NOT:
+                // the operator precedence of `not` in sql is low, which is the same as `and` or
+                // `or`.
+                // However, the `not` operator is implemented by `!` in c++ or in rust, which
+                // precedence is high.
+                // To solve the inconsistency, we always add brace for operand of `not` operator.
+                return true;
+            default:
+                return operand instanceof RexCall
+                        && ((RexCall) operand).getOperator().getLeftPrec()
+                                <= operator.getLeftPrec();
+        }
     }
 
     @Override


Test patch:
diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/antlr4x/GraphBuilderTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/antlr4x/GraphBuilderTest.java
index 46639514e044..f1071ea5d09c 100644
--- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/antlr4x/GraphBuilderTest.java
+++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/gremlin/antlr4x/GraphBuilderTest.java
@@ -19,6 +19,7 @@
 import com.alibaba.graphscope.common.ir.Utils;
 import com.alibaba.graphscope.common.ir.runtime.proto.RexToProtoConverter;
 import com.alibaba.graphscope.common.ir.tools.GraphBuilder;
+import com.alibaba.graphscope.common.ir.tools.GraphStdOperatorTable;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
 import com.alibaba.graphscope.common.ir.tools.config.SourceConfig;
 import com.alibaba.graphscope.common.ir.type.GraphProperty;
@@ -272,6 +273,24 @@ public void g_V_has_name_not_containing_test() {
                 node.explain().trim());
     }
 
+    // @.name not containing 'mar'
+    @Test
+    public void name_not_containing_expr_test() throws Exception {
+        GraphBuilder builder = Utils.mockGraphBuilder();
+        RexNode expr =
+                builder.source(new SourceConfig(GraphOpt.Source.VERTEX))
+                        .not(
+                                builder.call(
+                                        GraphStdOperatorTable.POSIX_REGEX_CASE_SENSITIVE,
+                                        builder.variable(null, "name"),
+                                        builder.literal(".*mar.*")));
+        RexToProtoConverter converter = new RexToProtoConverter(true, false, Utils.rexBuilder);
+        OuterExpression.Expression exprProto = expr.accept(converter);
+        Assert.assertEquals(
+                FileUtils.readJsonFromResource("proto/name_not_containing_expr.json"),
+                JsonFormat.printer().print(exprProto));
+    }
+
     @Test
     public void g_V_has_name_startingWith_test() {
         RelNode node = eval("g.V().has('name', startingWith('mar'))");
diff --git a/interactive_engine/compiler/src/test/resources/proto/name_not_containing_expr.json b/interactive_engine/compiler/src/test/resources/proto/name_not_containing_expr.json
new file mode 100644
index 000000000000..7b5ea627e997
--- /dev/null
+++ b/interactive_engine/compiler/src/test/resources/proto/name_not_containing_expr.json
@@ -0,0 +1,38 @@
+{
+  "operators": [{
+    "logical": "NOT",
+    "nodeType": {
+      "dataType": "BOOLEAN"
+    }
+  }, {
+    "brace": "LEFT_BRACE"
+  }, {
+    "var": {
+      "property": {
+        "key": {
+          "name": "name"
+        }
+      },
+      "nodeType": {
+        "dataType": "STRING"
+      }
+    },
+    "nodeType": {
+      "dataType": "STRING"
+    }
+  }, {
+    "logical": "REGEX",
+    "nodeType": {
+      "dataType": "BOOLEAN"
+    }
+  }, {
+    "const": {
+      "str": ".*mar.*"
+    },
+    "nodeType": {
+      "dataType": "STRING"
+    }
+  }, {
+    "brace": "RIGHT_BRACE"
+  }]
+}