Repository: alibaba/GraphScope (language: python)

Issue:
[BUG] Unexpected when dealing with filters in `match` in the GOpt-based compilation
**Describe the bug**

In GOpt-based compilation, the following query throws exceptions:
Query:
```
gremlin> g.V().match(__.as("a").out("KNOWS").as("b"), __.as("b").hasLabel("PERSON").has("gender", "male").out("KNOWS").as("c")).count()
filters =(_.gender, _UTF-8'male') not exist in the previous vertex filters null
Type ':help' or ':h' for help.
```
The detailed exception:
```
java.lang.IllegalArgumentException: filters =(_.gender, _UTF-8'male') not exist in the previous vertex filters null
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor$1.visitAndAddVertex(GraphIOProcessor.java:276)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor$1.visit(GraphIOProcessor.java:202)
        at org.apache.calcite.rel.AbstractRelNode.childrenAccept(AbstractRelNode.java:215)
        at org.apache.calcite.rel.RelVisitor.visit(RelVisitor.java:46)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor$1.visit(GraphIOProcessor.java:199)
        at org.apache.calcite.rel.AbstractRelNode.childrenAccept(AbstractRelNode.java:215)
        at org.apache.calcite.rel.RelVisitor.visit(RelVisitor.java:46)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor$1.visit(GraphIOProcessor.java:199)
        at org.apache.calcite.rel.RelVisitor.go(RelVisitor.java:63)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor.visit(GraphIOProcessor.java:390)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor$InputConvertor.visit(GraphIOProcessor.java:144)
        at com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch.accept(GraphLogicalMultiMatch.java:100)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor.processInput(GraphIOProcessor.java:98)
        at com.alibaba.graphscope.common.ir.planner.GraphIOProcessor.processInput(GraphIOProcessor.java:91)
        at com.alibaba.graphscope.common.ir.planner.GraphRelOptimizer$MatchOptimizer.visit(GraphRelOptimizer.java:135)
        at com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch.accept(GraphLogicalMultiMatch.java:100)
        at org.apache.calcite.rel.RelShuttleImpl.visitChild(RelShuttleImpl.java:57)
        at org.apache.calcite.rel.RelShuttleImpl.visitChildren(RelShuttleImpl.java:71)
        at com.alibaba.graphscope.common.ir.rel.GraphShuttle.visit(GraphShuttle.java:65)
        at com.alibaba.graphscope.common.ir.rel.GraphLogicalAggregate.accept(GraphLogicalAggregate.java:138)
        at com.alibaba.graphscope.common.ir.planner.GraphRelOptimizer.optimize(GraphRelOptimizer.java:107)
        at com.alibaba.graphscope.common.ir.tools.GraphPlanner$PlannerInstance.planLogical(GraphPlanner.java:118)
        at com.alibaba.graphscope.common.ir.tools.QueryCache$Key.<init>(QueryCache.java:62)
        at com.alibaba.graphscope.common.ir.tools.QueryCache.createKey(QueryCache.java:80)
        at com.alibaba.graphscope.gremlin.plugin.script.GremlinCalciteScriptEngineFactory$ScriptEngine.eval(GremlinCalciteScriptEngineFactory.java:72)
        at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)
        at org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.lambda$eval$0(GremlinExecutor.java:272)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:829)
```



Discussion / hints:

Fix patch:
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphIOProcessor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphIOProcessor.java
index 599f8004a4ef..3518c84a619c 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphIOProcessor.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphIOProcessor.java
@@ -48,7 +48,6 @@
 
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelVisitor;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -267,15 +266,20 @@ private PatternVertex visitAndAddVertex(
                                 vertexOrEdgeDetails.put(existVertex, new DataValue(alias, filters));
                             } else if (filters != null) {
                                 DataValue value = vertexOrEdgeDetails.get(existVertex);
-                                if (value.getFilter() == null
-                                        || !RelOptUtil.conjunctions(value.getFilter())
-                                                .containsAll(RelOptUtil.conjunctions(filters))) {
-                                    throw new IllegalArgumentException(
-                                            "filters "
-                                                    + filters
-                                                    + " not exist in the previous vertex filters "
-                                                    + value.getFilter());
-                                }
+                                // reset condition
+                                RexNode newCondition =
+                                        (value.getFilter() == null)
+                                                ? filters
+                                                : RexUtil.composeConjunction(
+                                                        builder.getRexBuilder(),
+                                                        ImmutableList.of(
+                                                                filters, value.getFilter()));
+                                vertexOrEdgeDetails.put(
+                                        existVertex,
+                                        new DataValue(
+                                                value.getAlias(),
+                                                newCondition,
+                                                value.getParentAlias()));
                             }
                             return existVertex;
                         }


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 3adfb8cea4a4..524b16c3e175 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
@@ -16,7 +16,10 @@
 
 package com.alibaba.graphscope.gremlin.antlr4x;
 
+import com.alibaba.graphscope.common.config.Configs;
 import com.alibaba.graphscope.common.ir.Utils;
+import com.alibaba.graphscope.common.ir.planner.GraphIOProcessor;
+import com.alibaba.graphscope.common.ir.planner.GraphRelOptimizer;
 import com.alibaba.graphscope.common.ir.planner.rules.ExpandGetVFusionRule;
 import com.alibaba.graphscope.common.ir.runtime.proto.RexToProtoConverter;
 import com.alibaba.graphscope.common.ir.tools.GraphBuilder;
@@ -24,11 +27,13 @@
 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;
+import com.alibaba.graphscope.common.store.IrMeta;
 import com.alibaba.graphscope.common.utils.FileUtils;
 import com.alibaba.graphscope.gaia.proto.OuterExpression;
 import com.alibaba.graphscope.gremlin.antlr4x.parser.GremlinAntlr4Parser;
 import com.alibaba.graphscope.gremlin.antlr4x.visitor.GraphBuilderVisitor;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.protobuf.util.JsonFormat;
 
 import org.antlr.v4.runtime.tree.ParseTree;
@@ -37,9 +42,32 @@
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
 import org.junit.Assert;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class GraphBuilderTest {
+    private static Configs configs;
+    private static IrMeta irMeta;
+    private static GraphRelOptimizer optimizer;
+
+    @BeforeClass
+    public static void beforeClass() {
+        configs =
+                new Configs(
+                        ImmutableMap.of(
+                                "graph.planner.is.on",
+                                "true",
+                                "graph.planner.opt",
+                                "CBO",
+                                "graph.planner.rules",
+                                "FilterIntoJoinRule, FilterMatchRule, ExtendIntersectRule,"
+                                        + " ExpandGetVFusionRule",
+                                "graph.planner.cbo.glogue.schema",
+                                "target/test-classes/statistics/modern_statistics.txt"));
+        optimizer = new GraphRelOptimizer(configs);
+        irMeta = Utils.mockSchemaMeta("schema/modern.json");
+    }
+
     public static RelNode eval(String query) {
         GraphBuilder builder = Utils.mockGraphBuilder();
         GraphBuilderVisitor visitor = new GraphBuilderVisitor(builder);
@@ -47,6 +75,12 @@ public static RelNode eval(String query) {
         return visitor.visit(parseTree).build();
     }
 
+    public static RelNode eval(String query, GraphBuilder builder) {
+        GraphBuilderVisitor visitor = new GraphBuilderVisitor(builder);
+        ParseTree parseTree = new GremlinAntlr4Parser().parse(query);
+        return visitor.visit(parseTree).build();
+    }
+
     @Test
     public void g_V_test() {
         RelNode node = eval("g.V()");
@@ -1596,6 +1630,31 @@ public void g_V_match_as_a_out_as_b_count_test() {
                 node.explain().trim());
     }
 
+    // the filter conditions `has('age', 10)` and `has("name", "male")` should be composed and fused
+    // into the person 'b'
+    @Test
+    public void g_V_match_as_a_out_as_b_hasLabel_has_out_as_c_count_test() {
+        GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta);
+        RelNode node =
+                eval(
+                        "g.V().match(__.as(\"a\").out(\"knows\").as(\"b\").has('age', 10),"
+                                + " __.as(\"b\").hasLabel(\"person\").has(\"name\","
+                                + " \"male\").out(\"knows\").as(\"c\")).count()",
+                        builder);
+        RelNode after = optimizer.optimize(node, new GraphIOProcessor(builder, irMeta));
+        Assert.assertEquals(
+                "GraphLogicalAggregate(keys=[{variables=[], aliases=[]}], values=[[{operands=[a, b,"
+                        + " c], aggFunction=COUNT, alias='$f0', distinct=false}]])\n"
+                        + "  GraphPhysicalExpand(tableConfig=[{isAll=false, tables=[knows]}],"
+                        + " alias=[a], startAlias=[b], opt=[IN], physicalOpt=[VERTEX])\n"
+                        + "    GraphPhysicalExpand(tableConfig=[{isAll=false, tables=[knows]}],"
+                        + " alias=[c], startAlias=[b], opt=[OUT], physicalOpt=[VERTEX])\n"
+                        + "      GraphLogicalSource(tableConfig=[{isAll=false, tables=[person]}],"
+                        + " alias=[b], fusedFilter=[[AND(=(_.name, _UTF-8'male'), =(_.age, 10))]],"
+                        + " opt=[VERTEX])",
+                after.explain().trim());
+    }
+
     // id is the primary key of label 'person', should be fused as 'uniqueKeyFilters'
     @Test
     public void g_V_has_label_person_id_test() {