Repository: alibaba/GraphScope (language: python)

Issue:
feat(Interacitve): Support LabelKey in filtering
MATCH(a) WHERE type(a) = 'labelA' return a;


Discussion / hints:

Fix patch:
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FilterMatchRule.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FilterMatchRule.java
index 5e9d582b93da..d69c1b93b991 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FilterMatchRule.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/rules/FilterMatchRule.java
@@ -1,15 +1,8 @@
 package com.alibaba.graphscope.common.ir.planner.rules;
 
-import com.alibaba.graphscope.common.ir.rel.graph.AbstractBindableTableScan;
+import com.alibaba.graphscope.common.ir.rel.PushFilterVisitor;
 import com.alibaba.graphscope.common.ir.rel.graph.match.AbstractLogicalMatch;
-import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch;
-import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalSingleMatch;
-import com.alibaba.graphscope.common.ir.rex.RexGraphVariable;
-import com.alibaba.graphscope.common.ir.rex.RexVariableAliasCollector;
-import com.alibaba.graphscope.common.ir.rex.RexVariableAliasConverter;
-import com.alibaba.graphscope.common.ir.tools.AliasInference;
 import com.alibaba.graphscope.common.ir.tools.GraphBuilder;
-import com.google.common.collect.ImmutableList;
 
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptUtil;
@@ -17,18 +10,12 @@
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.rules.TransformationRule;
-import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.tools.RelBuilderFactory;
-import org.apache.commons.lang3.ObjectUtils;
 import org.checkerframework.checker.nullness.qual.Nullable;
 
-import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.stream.Collectors;
 
 public class FilterMatchRule<C extends FilterMatchRule.Config> extends RelRule<C>
         implements TransformationRule {
@@ -41,13 +28,14 @@ public void onMatch(RelOptRuleCall call) {
         Filter filter = call.rel(0);
         AbstractLogicalMatch match = call.rel(1);
         // do the transformation
-        List<RelNode> sentences = getSentences(match);
         List<RexNode> conjunctions = RelOptUtil.conjunctions(filter.getCondition());
         int origSize = conjunctions.size();
         GraphBuilder graphBuilder = (GraphBuilder) call.builder();
         for (Iterator<RexNode> it = conjunctions.iterator(); it.hasNext(); ) {
             RexNode condition = it.next();
-            if (pushFilter(sentences, condition, graphBuilder)) {
+            PushFilterVisitor visitor = new PushFilterVisitor(graphBuilder, condition);
+            match = (AbstractLogicalMatch) match.accept(visitor);
+            if (visitor.isPushed()) {
                 it.remove();
             }
         }
@@ -59,76 +47,6 @@ public void onMatch(RelOptRuleCall call) {
         call.transformTo(newNode);
     }
 
-    private boolean pushFilter(
-            List<RelNode> sentences, RexNode condition, GraphBuilder graphBuilder) {
-        boolean pushed = false;
-        List<Integer> distinctAliasIds =
-                condition
-                        .accept(new RexVariableAliasCollector<>(true, RexGraphVariable::getAliasId))
-                        .stream()
-                        .distinct()
-                        .collect(Collectors.toList());
-        if (distinctAliasIds.size() != 1) {
-            return pushed;
-        }
-        int aliasId = distinctAliasIds.get(0);
-        List<RelNode> inputsQueue = new ArrayList<>();
-        for (RelNode node : sentences) {
-            inputsQueue.add(node);
-            while (!inputsQueue.isEmpty()) {
-                RelNode cur = inputsQueue.remove(0);
-                List<AbstractBindableTableScan> candidates = new ArrayList<>();
-                if (cur instanceof AbstractBindableTableScan) {
-                    candidates.add((AbstractBindableTableScan) cur);
-                }
-                for (AbstractBindableTableScan candidate : candidates) {
-                    RelDataType rowType = candidate.getRowType();
-                    for (RelDataTypeField field : rowType.getFieldList()) {
-                        if (aliasId != AliasInference.DEFAULT_ID && field.getIndex() == aliasId) {
-                            RexNode transform =
-                                    condition.accept(
-                                            new RexVariableAliasConverter(
-                                                    true,
-                                                    graphBuilder,
-                                                    AliasInference.DEFAULT_NAME,
-                                                    AliasInference.DEFAULT_ID));
-                            if (ObjectUtils.isEmpty(candidate.getFilters())) {
-                                candidate.setFilters(ImmutableList.of(transform));
-                            } else {
-                                ImmutableList.Builder builder = new ImmutableList.Builder();
-                                builder.addAll(candidate.getFilters()).add(transform);
-                                candidate.setFilters(
-                                        ImmutableList.of(
-                                                RexUtil.composeConjunction(
-                                                        graphBuilder.getRexBuilder(),
-                                                        builder.build())));
-                            }
-                            pushed = true;
-                            break;
-                        }
-                    }
-                }
-                if (!cur.getInputs().isEmpty()) {
-                    inputsQueue.addAll(cur.getInputs());
-                }
-            }
-        }
-        return pushed;
-    }
-
-    private List<RelNode> getSentences(AbstractLogicalMatch match) {
-        List<RelNode> sentences = new ArrayList<>();
-        if (match instanceof GraphLogicalSingleMatch) {
-            sentences.add(((GraphLogicalSingleMatch) match).getSentence());
-        } else if (match instanceof GraphLogicalMultiMatch) {
-            sentences.addAll(((GraphLogicalMultiMatch) match).getSentences());
-        } else {
-            throw new UnsupportedOperationException(
-                    "match type " + match.getClass() + " is unsupported");
-        }
-        return sentences;
-    }
-
     public static class Config implements RelRule.Config {
         public static FilterMatchRule.Config DEFAULT =
                 new Config()
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalAggregate.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalAggregate.java
index 991d71d4a842..829bccf9fb39 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalAggregate.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalAggregate.java
@@ -25,6 +25,7 @@
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
@@ -130,4 +131,12 @@ public GraphGroupKeys getGroupKey() {
     public List<GraphAggCall> getAggCalls() {
         return Collections.unmodifiableList(aggCalls);
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalProject.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalProject.java
index c42ebf885084..a12423855474 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalProject.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalProject.java
@@ -22,6 +22,7 @@
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.hint.RelHint;
@@ -80,4 +81,12 @@ public Project copy(
     public RelWriter explainTerms(RelWriter pw) {
         return super.explainTerms(pw).item("isAppend", isAppend);
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalSort.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalSort.java
index 87b08659d75e..837fb9ace294 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalSort.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphLogicalSort.java
@@ -24,6 +24,7 @@
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rex.RexNode;
@@ -78,4 +79,12 @@ public Sort copy(
         return new GraphLogicalSort(
                 getCluster(), traitSet, getHints(), newInput, newCollation, offset, fetch);
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphRelVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphRelVisitor.java
new file mode 100644
index 000000000000..bdb0a4354dca
--- /dev/null
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphRelVisitor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.graphscope.common.ir.rel;
+
+import com.alibaba.graphscope.common.ir.rel.graph.*;
+import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch;
+import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalSingleMatch;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttleImpl;
+
+/**
+ * the class is used to visit a graph relation tree
+ */
+public abstract class GraphRelVisitor extends RelShuttleImpl {
+    public RelNode visit(GraphLogicalSource source) {
+        return source;
+    }
+
+    public RelNode visit(GraphLogicalExpand expand) {
+        return visitChildren(expand);
+    }
+
+    public RelNode visit(GraphLogicalExpandDegree degree) {
+        return visitChildren(degree);
+    }
+
+    public RelNode visit(GraphLogicalGetV getV) {
+        return visitChildren(getV);
+    }
+
+    public RelNode visit(GraphLogicalPathExpand expand) {
+        return visitChildren(expand);
+    }
+
+    public RelNode visit(GraphLogicalSingleMatch match) {
+        return match;
+    }
+
+    public RelNode visit(GraphLogicalMultiMatch match) {
+        return match;
+    }
+
+    public RelNode visit(GraphLogicalAggregate aggregate) {
+        return visitChildren(aggregate);
+    }
+
+    public RelNode visit(GraphLogicalProject project) {
+        return visitChildren(project);
+    }
+
+    public RelNode visit(GraphLogicalSort sort) {
+        return visitChildren(sort);
+    }
+}
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/PushFilterVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/PushFilterVisitor.java
new file mode 100644
index 000000000000..f79238237b52
--- /dev/null
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/PushFilterVisitor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2020 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.graphscope.common.ir.rel;
+
+import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalExpand;
+import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalGetV;
+import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalSource;
+import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch;
+import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalSingleMatch;
+import com.alibaba.graphscope.common.ir.rex.RexGraphVariable;
+import com.alibaba.graphscope.common.ir.rex.RexVariableAliasCollector;
+import com.alibaba.graphscope.common.ir.tools.AliasInference;
+import com.alibaba.graphscope.common.ir.tools.GraphBuilder;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexNode;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * visit a graph relation tree and push filter to the corresponding source
+ */
+public class PushFilterVisitor extends GraphRelVisitor {
+    private final GraphBuilder builder;
+    private final RexNode condition;
+    private final List<Integer> distinctAliasIds;
+    private boolean pushed;
+
+    public PushFilterVisitor(GraphBuilder builder, RexNode condition) {
+        this.builder = builder;
+        this.condition = condition;
+        this.distinctAliasIds =
+                condition
+                        .accept(new RexVariableAliasCollector<>(true, RexGraphVariable::getAliasId))
+                        .stream()
+                        .distinct()
+                        .collect(Collectors.toList());
+    }
+
+    @Override
+    public RelNode visit(GraphLogicalSingleMatch match) {
+        RelNode sentence = match.getSentence().accept(this);
+        if (!sentence.equals(match.getSentence())) {
+            return builder.match(sentence, match.getMatchOpt()).build();
+        }
+        return match;
+    }
+
+    @Override
+    public RelNode visit(GraphLogicalMultiMatch match) {
+        List<RelNode> sentences =
+                match.getSentences().stream().map(k -> k.accept(this)).collect(Collectors.toList());
+        if (!sentences.equals(match.getSentences())) {
+            return builder.match(sentences.get(0), sentences.subList(1, sentences.size())).build();
+        }
+        return match;
+    }
+
+    @Override
+    public RelNode visit(GraphLogicalSource source) {
+        return fuseFilter(source);
+    }
+
+    @Override
+    public RelNode visit(GraphLogicalExpand expand) {
+        return fuseFilter(visitChildren(expand));
+    }
+
+    @Override
+    public RelNode visit(GraphLogicalGetV getV) {
+        return fuseFilter(visitChildren(getV));
+    }
+
+    public boolean isPushed() {
+        return pushed;
+    }
+
+    private RelNode fuseFilter(RelNode node) {
+        if (distinctAliasIds.size() != 1) return node;
+        int aliasId = distinctAliasIds.get(0);
+        RelDataType rowType = node.getRowType();
+        for (RelDataTypeField field : rowType.getFieldList()) {
+            if (aliasId != AliasInference.DEFAULT_ID && field.getIndex() == aliasId) {
+                pushed = true;
+                return builder.push(node).filter(condition).build();
+            }
+        }
+        return node;
+    }
+}
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java
index 90612cdc6ffb..798d8d28809d 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpand.java
@@ -16,6 +16,7 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.rel.type.AliasNameWithId;
 import com.alibaba.graphscope.common.ir.rel.type.TableConfig;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
@@ -23,6 +24,7 @@
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.hint.RelHint;
 import org.checkerframework.checker.nullness.qual.Nullable;
@@ -75,4 +77,12 @@ public GraphLogicalExpand copy(RelTraitSet traitSet, List<RelNode> inputs) {
                 this.getAliasName(),
                 this.getStartAlias());
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpandDegree.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpandDegree.java
index 77746fb32b25..58141cb5eb75 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpandDegree.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalExpandDegree.java
@@ -16,12 +16,14 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.tools.AliasInference;
 import com.google.common.collect.ImmutableList;
 
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.hint.RelHint;
@@ -99,4 +101,12 @@ public RelWriter explainTerms(RelWriter pw) {
                         fusedExpand.filters,
                         !ObjectUtils.isEmpty(fusedExpand.filters));
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java
index 9a4aa9500653..d4f896e6aec3 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalGetV.java
@@ -16,6 +16,7 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.rel.type.AliasNameWithId;
 import com.alibaba.graphscope.common.ir.rel.type.TableConfig;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
@@ -23,6 +24,7 @@
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.hint.RelHint;
 import org.checkerframework.checker.nullness.qual.Nullable;
@@ -75,4 +77,12 @@ public GraphLogicalGetV copy(RelTraitSet traitSet, List<RelNode> inputs) {
                 this.getAliasName(),
                 this.getStartAlias());
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalPathExpand.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalPathExpand.java
index e56deff31892..3b382e2da503 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalPathExpand.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalPathExpand.java
@@ -16,6 +16,7 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.rel.type.AliasNameWithId;
 import com.alibaba.graphscope.common.ir.tools.AliasInference;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
@@ -26,6 +27,7 @@
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.hint.RelHint;
@@ -185,4 +187,12 @@ protected RelDataType deriveRowType() {
                                                         .get(0)
                                                         .getType())))));
     }
+
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
 }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java
index 2d6fce56a41c..f7c331c23e49 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/GraphLogicalSource.java
@@ -16,10 +16,13 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.rel.type.TableConfig;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
 
 import org.apache.calcite.plan.GraphOptCluster;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rex.RexNode;
@@ -62,6 +65,14 @@ public RelWriter explainTerms(RelWriter pw) {
                 .itemIf("uniqueKeyFilters", uniqueKeyFilters, uniqueKeyFilters != null);
     }
 
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
+
     public void setUniqueKeyFilters(RexNode uniqueKeyFilters) {
         this.uniqueKeyFilters = Objects.requireNonNull(uniqueKeyFilters);
     }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalMultiMatch.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalMultiMatch.java
index c9deb9ccb0fe..f02089cfc8d3 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalMultiMatch.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalMultiMatch.java
@@ -16,12 +16,14 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph.match;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.type.RelDataType;
@@ -91,6 +93,14 @@ public RelDataType deriveRowType() {
         return new RelRecordType(StructKind.FULLY_QUALIFIED, dedup);
     }
 
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
+
     public List<RelNode> getSentences() {
         return Collections.unmodifiableList(sentences);
     }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalSingleMatch.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalSingleMatch.java
index 03b036f97dc9..7e8dc9181570 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalSingleMatch.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/graph/match/GraphLogicalSingleMatch.java
@@ -16,12 +16,14 @@
 
 package com.alibaba.graphscope.common.ir.rel.graph.match;
 
+import com.alibaba.graphscope.common.ir.rel.GraphRelVisitor;
 import com.alibaba.graphscope.common.ir.tools.config.GraphOpt;
 import com.google.common.collect.Lists;
 
 import org.apache.calcite.plan.GraphOptCluster;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.type.RelDataType;
@@ -71,6 +73,14 @@ public RelDataType deriveRowType() {
         return new RelRecordType(StructKind.FULLY_QUALIFIED, fields);
     }
 
+    @Override
+    public RelNode accept(RelShuttle shuttle) {
+        if (shuttle instanceof GraphRelVisitor) {
+            return ((GraphRelVisitor) shuttle).visit(this);
+        }
+        return shuttle.visit(this);
+    }
+
     public RelNode getSentence() {
         return sentence;
     }
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java
index c851059137a4..ba28b1002f37 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphBuilder.java
@@ -23,6 +23,7 @@
 import com.alibaba.graphscope.common.ir.rel.GraphLogicalAggregate;
 import com.alibaba.graphscope.common.ir.rel.GraphLogicalProject;
 import com.alibaba.graphscope.common.ir.rel.GraphLogicalSort;
+import com.alibaba.graphscope.common.ir.rel.PushFilterVisitor;
 import com.alibaba.graphscope.common.ir.rel.graph.*;
 import com.alibaba.graphscope.common.ir.rel.graph.match.AbstractLogicalMatch;
 import com.alibaba.graphscope.common.ir.rel.graph.match.GraphLogicalMultiMatch;
@@ -233,6 +234,8 @@ public GraphBuilder pathExpand(PathExpandConfig pxdConfig) {
      * @return
      */
     public TableConfig getTableConfig(LabelConfig labelConfig, GraphOpt.Source opt) {
+        Preconditions.checkArgument(
+                relOptSchema != null, "cannot create table config from the 'null' schema");
         List<RelOptTable> relOptTables = new ArrayList<>();
         if (!labelConfig.isAll()) {
             ObjectUtils.requireNonEmpty(labelConfig.getLabels());
@@ -719,36 +722,54 @@ public GraphBuilder filter(Iterable<? extends RexNode> conditions) {
                                 + type);
             }
         }
-        GraphBuilder builder = (GraphBuilder) super.filter(ImmutableSet.of(), conditions);
+        super.filter(ImmutableSet.of(), conditions);
         // fuse filter with the previous table scan if meets the conditions
-        Filter filter;
-        AbstractBindableTableScan tableScan;
-        if ((filter = topFilter()) != null && (tableScan = inputTableScan(filter)) != null) {
+        Filter filter = topFilter();
+        if (filter != null) {
+            GraphBuilder builder =
+                    (GraphBuilder)
+                            GraphPlanner.relBuilderFactory.create(getCluster(), getRelOptSchema());
             RexNode condition = filter.getCondition();
-            List<Integer> aliasIds =
-                    condition.accept(
-                            new RexVariableAliasCollector<>(true, RexGraphVariable::getAliasId));
-            // fuze all conditions into table scan
-            if (!aliasIds.isEmpty()
-                    && ImmutableList.of(AliasInference.DEFAULT_ID, tableScan.getAliasId())
-                            .containsAll(aliasIds)) {
-                condition =
+            RelNode input = !filter.getInputs().isEmpty() ? filter.getInput(0) : null;
+            if (input instanceof AbstractBindableTableScan) {
+                AbstractBindableTableScan tableScan = (AbstractBindableTableScan) input;
+                List<Integer> aliasIds =
                         condition.accept(
-                                new RexVariableAliasConverter(
-                                        true,
-                                        this,
-                                        AliasInference.SIMPLE_NAME(AliasInference.DEFAULT_NAME),
-                                        AliasInference.DEFAULT_ID));
-                // add condition into table scan
-                // pop the filter from the inner stack
-                replaceTop(fuseFilters(tableScan, condition));
+                                new RexVariableAliasCollector<>(
+                                        true, RexGraphVariable::getAliasId));
+                // fuze all conditions into table scan
+                if (!aliasIds.isEmpty()
+                        && ImmutableList.of(AliasInference.DEFAULT_ID, tableScan.getAliasId())
+                                .containsAll(aliasIds)) {
+                    condition =
+                            condition.accept(
+                                    new RexVariableAliasConverter(
+                                            true,
+                                            this,
+                                            AliasInference.SIMPLE_NAME(AliasInference.DEFAULT_NAME),
+                                            AliasInference.DEFAULT_ID));
+                    // add condition into table scan
+                    // pop the filter from the inner stack
+                    replaceTop(fuseFilters(tableScan, condition, builder));
+                }
+            } else if (input instanceof AbstractLogicalMatch) {
+                List<RexNode> extraFilters = Lists.newArrayList();
+                AbstractLogicalMatch match =
+                        fuseFilters((AbstractLogicalMatch) input, condition, extraFilters, builder);
+                if (!match.equals(input)) {
+                    if (extraFilters.isEmpty()) {
+                        replaceTop(match);
+                    } else {
+                        replaceTop(builder.push(match).filter(extraFilters).build());
+                    }
+                }
             }
         }
-        return builder;
+        return this;
     }
 
     private AbstractBindableTableScan fuseFilters(
-            AbstractBindableTableScan tableScan, RexNode condition) {
+            AbstractBindableTableScan tableScan, RexNode condition, GraphBuilder builder) {
         List<Comparable> labelValues = Lists.newArrayList();
         List<RexNode> uniqueKeyFilters = Lists.newArrayList();
         List<RexNode> extraFilters = Lists.newArrayList();
@@ -767,10 +788,6 @@ private AbstractBindableTableScan fuseFilters(
                     "cannot find common labels between values= " + labelValues + " and label=",
                     labelType);
             if (labelsToKeep.size() < labelType.getLabelsEntry().size()) {
-                GraphBuilder builder =
-                        (GraphBuilder)
-                                GraphPlanner.relBuilderFactory.create(
-                                        getCluster(), getRelOptSchema());
                 LabelConfig newLabelConfig = new LabelConfig(false);
                 labelsToKeep.forEach(k -> newLabelConfig.addLabel(k));
                 if (tableScan instanceof GraphLogicalSource) {
@@ -856,6 +873,37 @@ public Void visitInputRef(RexInputRef inputRef) {
         return tableScan;
     }
 
+    /**
+     * fuse label filters into the {@code match} if possible
+     * @param match
+     * @param condition
+     * @param extraFilters
+     * @param builder
+     * @return
+     */
+    private AbstractLogicalMatch fuseFilters(
+            AbstractLogicalMatch match,
+            RexNode condition,
+            List<RexNode> extraFilters,
+            GraphBuilder builder) {
+        List<RexNode> labelFilters = Lists.newArrayList();
+        for (RexNode conjunction : RelOptUtil.conjunctions(condition)) {
+            if (isLabelEqualFilter(conjunction) != null) {
+                labelFilters.add(conjunction);
+            } else {
+                extraFilters.add(conjunction);
+            }
+        }
+        for (RexNode labelFilter : labelFilters) {
+            PushFilterVisitor visitor = new PushFilterVisitor(builder, labelFilter);
+            match = (AbstractLogicalMatch) match.accept(visitor);
+            if (!visitor.isPushed()) {
+                extraFilters.add(labelFilter);
+            }
+        }
+        return match;
+    }
+
     private void classifyFilters(
             AbstractBindableTableScan tableScan,
             RexNode condition,
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/graphAlgo/GraphAlgoQueries.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/graphAlgo/GraphAlgoQueries.java
index c5b76f50f643..9508f0a4fe29 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/graphAlgo/GraphAlgoQueries.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/graphAlgo/GraphAlgoQueries.java
@@ -100,8 +100,8 @@ public static QueryContext get_graph_algo_test4() {
 
     public static QueryContext get_graph_algo_test5() {
         String query =
-                "MATCH(c: CCFField)<-[e]-(p:Paper) where type(e) = 6 return type(e) As edgeType"
-                        + " ORDER BY edgeType ASC LIMIT 10;";
+                "MATCH(c: CCFField)<-[e]-(p:Paper) where type(e) = 'HasField' return type(e) As"
+                        + " edgeType ORDER BY edgeType ASC LIMIT 10;";
         List<String> expected =
                 Arrays.asList(
                         "Record<{edgeType: \"HasField\"}>",
diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/movie/MovieQueries.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/movie/MovieQueries.java
index 2cf5403843b0..2f324ef1696b 100644
--- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/movie/MovieQueries.java
+++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/cypher/integration/suite/movie/MovieQueries.java
@@ -360,4 +360,17 @@ public static QueryContext get_movie_query23_test() {
                 Arrays.asList("Record<{name: \"Tom Hanks\", name0: \"Tom Hanks\"}>");
         return new QueryContext(query, expected);
     }
+
+    public static QueryContext get_movie_query24_test() {
+        String query = "Match (n) Where labels(n)='Movie' Return distinct labels(n) as label;";
+        List<String> expected = Arrays.asList("Record<{label: \"Movie\"}>");
+        return new QueryContext(query, expected);
+    }
+
+    public static QueryContext get_movie_query25_test() {
+        String query =
+                "Match (n)-[m]->(c) Where type(m)='ACTED_IN' Return distinct type(m) as type;";
+        List<String> expected = Arrays.asList("Record<{type: \"ACTED_IN\"}>");
+        return new QueryContext(query, expected);
+    }
 }


Test patch:
diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/plan/HepPlannerTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/plan/HepPlannerTest.java
index 59d34e3cadbd..1019752bbd4f 100644
--- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/plan/HepPlannerTest.java
+++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/plan/HepPlannerTest.java
@@ -253,6 +253,40 @@ public void push_filter_4_test() {
                     + " alias=[x], opt=[VERTEX])\n"
                     + "], matchOpt=[INNER])",
                 after2.explain().trim());
-        System.out.println(after2.getRowType());
+    }
+
+    @Test
+    public void push_filter_5_test() {
+        RelNode node =
+                com.alibaba.graphscope.cypher.antlr4.Utils.eval(
+                                "Match (a)-[b]-(c:person) Where labels(a)='person' and a.name ="
+                                        + " 'marko' Return a")
+                        .build();
+        Assert.assertEquals(
+                "GraphLogicalProject(a=[a], isAppend=[false])\n"
+                    + "  LogicalFilter(condition=[=(a.name, _UTF-8'marko')])\n"
+                    + "    GraphLogicalSingleMatch(input=[null],"
+                    + " sentence=[GraphLogicalGetV(tableConfig=[{isAll=false, tables=[person]}],"
+                    + " alias=[c], opt=[OTHER])\n"
+                    + "  GraphLogicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}],"
+                    + " alias=[b], opt=[BOTH])\n"
+                    + "    GraphLogicalSource(tableConfig=[{isAll=false, tables=[person]}],"
+                    + " alias=[a], opt=[VERTEX])\n"
+                    + "], matchOpt=[INNER])",
+                node.explain().trim());
+        RelOptPlanner planner = Utils.mockPlanner(FilterMatchRule.Config.DEFAULT);
+        planner.setRoot(node);
+        RelNode after = planner.findBestExp();
+        Assert.assertEquals(
+                "GraphLogicalProject(a=[a], isAppend=[false])\n"
+                    + "  GraphLogicalSingleMatch(input=[null],"
+                    + " sentence=[GraphLogicalGetV(tableConfig=[{isAll=false, tables=[person]}],"
+                    + " alias=[c], opt=[OTHER])\n"
+                    + "  GraphLogicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}],"
+                    + " alias=[b], opt=[BOTH])\n"
+                    + "    GraphLogicalSource(tableConfig=[{isAll=false, tables=[person]}],"
+                    + " alias=[a], fusedFilter=[[=(DEFAULT.name, _UTF-8'marko')]], opt=[VERTEX])\n"
+                    + "], matchOpt=[INNER])",
+                after.explain().trim());
     }
 }
diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/integration/movie/MovieTest.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/integration/movie/MovieTest.java
index 9f93565034c8..6dd900748989 100644
--- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/integration/movie/MovieTest.java
+++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/cypher/integration/movie/MovieTest.java
@@ -186,6 +186,20 @@ public void run_movie_query23_test() {
         Assert.assertEquals(testQuery.getExpectedResult().toString(), result.list().toString());
     }
 
+    @Test
+    public void run_movie_query24_test() {
+        QueryContext testQuery = MovieQueries.get_movie_query24_test();
+        Result result = session.run(testQuery.getQuery());
+        Assert.assertEquals(testQuery.getExpectedResult().toString(), result.list().toString());
+    }
+
+    @Test
+    public void run_movie_query25_test() {
+        QueryContext testQuery = MovieQueries.get_movie_query25_test();
+        Result result = session.run(testQuery.getQuery());
+        Assert.assertEquals(testQuery.getExpectedResult().toString(), result.list().toString());
+    }
+
     @AfterClass
     public static void afterClass() {
         if (session != null) {