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 extends RelRule 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 sentences = getSentences(match); List conjunctions = RelOptUtil.conjunctions(filter.getCondition()); int origSize = conjunctions.size(); GraphBuilder graphBuilder = (GraphBuilder) call.builder(); for (Iterator 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 sentences, RexNode condition, GraphBuilder graphBuilder) { - boolean pushed = false; - List 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 inputsQueue = new ArrayList<>(); - for (RelNode node : sentences) { - inputsQueue.add(node); - while (!inputsQueue.isEmpty()) { - RelNode cur = inputsQueue.remove(0); - List 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 getSentences(AbstractLogicalMatch match) { - List 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 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 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 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 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 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 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 relOptTables = new ArrayList<>(); if (!labelConfig.isAll()) { ObjectUtils.requireNonEmpty(labelConfig.getLabels()); @@ -719,36 +722,54 @@ public GraphBuilder filter(Iterable 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 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 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 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 labelValues = Lists.newArrayList(); List uniqueKeyFilters = Lists.newArrayList(); List 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 extraFilters, + GraphBuilder builder) { + List 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 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 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 expected = Arrays.asList("Record<{type: \"ACTED_IN\"}>"); + return new QueryContext(query, expected); + } } 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) {