diff --git a/interactive_engine/compiler/ir_experimental_ci.sh b/interactive_engine/compiler/ir_experimental_ci.sh index 600079df6589..49bb61cf7161 100755 --- a/interactive_engine/compiler/ir_experimental_ci.sh +++ b/interactive_engine/compiler/ir_experimental_ci.sh @@ -15,19 +15,6 @@ if [ $exit_code -ne 0 ]; then exit 1 fi -# restart compiler service -ps -ef | grep "com.alibaba.graphscope.GraphServer" | grep -v grep | awk '{print $2}' | xargs kill -9 || true -cd ${base_dir} && make run gremlin.script.language.name=antlr_gremlin_calcite & -sleep 5s -# run gremlin standard tests to test calcite-based IR layer -cd ${base_dir} && make gremlin_calcite_test -exit_code=$? -# report test result -if [ $exit_code -ne 0 ]; then - echo "ir\(calcite-based\) gremlin integration test on experimental store fail" - exit 1 -fi - # restart compiler service ps -ef | grep "com.alibaba.graphscope.GraphServer" | grep -v grep | awk '{print $2}' | xargs kill -9 || true cd ${base_dir} && make run gremlin.script.language.name=antlr_gremlin_calcite physical.opt.config=proto & diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/CommonOptTable.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/CommonOptTable.java new file mode 100644 index 000000000000..8d9cb9674781 --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/meta/schema/CommonOptTable.java @@ -0,0 +1,129 @@ +/* + * 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.meta.schema; + +import com.alibaba.graphscope.common.ir.tools.Utils; +import com.google.common.collect.ImmutableList; + +import org.apache.calcite.linq4j.tree.Expression; +import org.apache.calcite.plan.RelOptSchema; +import org.apache.calcite.plan.RelOptTable; +import org.apache.calcite.rel.RelCollation; +import org.apache.calcite.rel.RelDistribution; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.RelReferentialConstraint; +import org.apache.calcite.rel.type.RelDataType; +import org.apache.calcite.rel.type.RelDataTypeField; +import org.apache.calcite.schema.ColumnStrategy; +import org.apache.calcite.util.ImmutableBitSet; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.List; + +/** + * to support common partial computation, we need to wrap the common sub-plan into a {@code RelOptTable}, i.e. + * in the query `g.V().out().union(out(), out())`, `g.V().out()` is a common sub-plan, which is denoted as a {@code CommonOptTable} here. + */ +public class CommonOptTable implements RelOptTable { + private final RelNode common; + + public CommonOptTable(RelNode common) { + this.common = common; + } + + @Override + public List getQualifiedName() { + return ImmutableList.of("common#" + this.common.explain().hashCode()); + } + + public RelNode getCommon() { + return common; + } + + @Override + public RelDataType getRowType() { + return Utils.getOutputType(this.common); + } + + @Override + public @Nullable RelOptSchema getRelOptSchema() { + return null; + } + + @Override + public @Nullable C unwrap(Class clazz) { + if (clazz.isInstance(this)) { + return clazz.cast(this); + } + return null; + } + + @Override + public boolean isKey(ImmutableBitSet immutableBitSet) { + throw new UnsupportedOperationException("is key is unsupported yet in statistics"); + } + + @Override + public @Nullable List getKeys() { + throw new UnsupportedOperationException("get keys is unsupported yet in statistics"); + } + + @Override + public double getRowCount() { + throw new UnsupportedOperationException("row count is unsupported yet in statistics"); + } + + @Override + public @Nullable RelDistribution getDistribution() { + throw new UnsupportedOperationException("distribution is unsupported yet in statistics"); + } + + @Override + public @Nullable List getCollationList() { + throw new UnsupportedOperationException("collations is unsupported yet in statistics"); + } + + // not used currently + + @Override + public RelNode toRel(ToRelContext toRelContext) { + throw new UnsupportedOperationException("toRel is unsupported for it will never be used"); + } + + @Override + public @Nullable List getReferentialConstraints() { + throw new UnsupportedOperationException( + "referentialConstraints is unsupported for it will never be used"); + } + + @Override + public @Nullable Expression getExpression(Class aClass) { + throw new UnsupportedOperationException( + "expression is unsupported for it will never be used"); + } + + @Override + public RelOptTable extend(List list) { + throw new UnsupportedOperationException("extend is unsupported for it will never be used"); + } + + @Override + public List getColumnStrategies() { + throw new UnsupportedOperationException( + "columnStrategies is unsupported for it will never be used"); + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphHepPlanner.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphHepPlanner.java new file mode 100644 index 000000000000..918f8800642c --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/planner/GraphHepPlanner.java @@ -0,0 +1,159 @@ +package com.alibaba.graphscope.common.ir.planner; + +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import 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.plan.RelOptTable; +import org.apache.calcite.plan.hep.HepPlanner; +import org.apache.calcite.plan.hep.HepProgram; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.logical.LogicalFilter; +import org.apache.calcite.rel.logical.LogicalUnion; +import org.apache.calcite.rex.RexNode; +import org.apache.calcite.rex.RexShuttle; +import org.apache.calcite.rex.RexSubQuery; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.IdentityHashMap; + +/** + * Original {@code HepPlanner} skip optimizations to the nested structures, i.e. {@code RelNode} nested in the {@code CommonTableScan} or {@code RexSubQuery}, + * we supplement this functionality by overriding the {@code findBestExp} method. + */ +public class GraphHepPlanner extends HepPlanner { + private @Nullable RelNode originalRoot; + + public GraphHepPlanner(HepProgram program) { + super(program); + } + + @Override + public RelNode findBestExp() { + return originalRoot.accept(new PlannerVisitor(originalRoot)); + } + + @Override + public void setRoot(RelNode rel) { + this.originalRoot = rel; + } + + public RelNode findBestExpOfRoot(RelNode root) { + super.setRoot(root); + return super.findBestExp(); + } + + private class PlannerVisitor extends GraphShuttle { + private final RelNode root; + private final IdentityHashMap commonRelVisitedMap; + // apply optimization to the sub-query + private final RexShuttle subQueryPlanner; + + public PlannerVisitor(RelNode root) { + this.root = root; + this.commonRelVisitedMap = new IdentityHashMap<>(); + this.subQueryPlanner = + new RexShuttle() { + @Override + public RexNode visitSubQuery(RexSubQuery subQuery) { + RelNode subRel = subQuery.rel; + RelNode newSubRel = subRel.accept(new PlannerVisitor(subRel)); + if (newSubRel == subRel) { + return subQuery; + } + return subQuery.clone(newSubRel); + } + }; + } + + @Override + public RelNode visit(GraphLogicalSource source) { + return findBestIfRoot(source, source); + } + + @Override + public RelNode visit(GraphLogicalExpand expand) { + return findBestIfRoot(expand, visitChildren(expand)); + } + + @Override + public RelNode visit(GraphLogicalGetV getV) { + return findBestIfRoot(getV, visitChildren(getV)); + } + + @Override + public RelNode visit(GraphLogicalPathExpand expand) { + return findBestIfRoot(expand, visitChildren(expand)); + } + + @Override + public RelNode visit(GraphLogicalSingleMatch match) { + return findBestIfRoot(match, match); + } + + @Override + public RelNode visit(GraphLogicalMultiMatch match) { + return findBestIfRoot(match, match); + } + + @Override + public RelNode visit(GraphLogicalAggregate aggregate) { + return findBestIfRoot(aggregate, visitChildren(aggregate)); + } + + @Override + public RelNode visit(GraphLogicalProject project) { + return findBestIfRoot(project, visitChildren(project)); + } + + @Override + public RelNode visit(GraphLogicalSort sort) { + return findBestIfRoot(sort, visitChildren(sort)); + } + + @Override + public RelNode visit(GraphPhysicalExpand physicalExpand) { + return findBestIfRoot(physicalExpand, visitChildren(physicalExpand)); + } + + @Override + public RelNode visit(GraphPhysicalGetV physicalGetV) { + return findBestIfRoot(physicalGetV, visitChildren(physicalGetV)); + } + + @Override + public RelNode visit(LogicalUnion union) { + return findBestIfRoot(union, visitChildren(union)); + } + + @Override + public RelNode visit(LogicalFilter filter) { + return findBestIfRoot(filter, visitChildren(filter)); + } + + @Override + public RelNode visit(CommonTableScan tableScan) { + RelOptTable optTable = tableScan.getTable(); + if (optTable instanceof CommonOptTable) { + RelNode common = ((CommonOptTable) optTable).getCommon(); + RelNode visited = commonRelVisitedMap.get(common); + if (visited == null) { + visited = common.accept(new PlannerVisitor(common)); + commonRelVisitedMap.put(common, visited); + } + return new CommonTableScan( + tableScan.getCluster(), + tableScan.getTraitSet(), + new CommonOptTable(visited)); + } + return tableScan; + } + + private RelNode findBestIfRoot(RelNode oldRel, RelNode newRel) { + newRel = newRel.accept(this.subQueryPlanner); + return oldRel == root ? findBestExpOfRoot(newRel) : newRel; + } + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/CommonTableScan.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/CommonTableScan.java new file mode 100644 index 000000000000..0e7828c8be6d --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/CommonTableScan.java @@ -0,0 +1,43 @@ +/* + * 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.google.common.collect.ImmutableList; + +import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptTable; +import org.apache.calcite.plan.RelTraitSet; +import org.apache.calcite.rel.RelNode; +import org.apache.calcite.rel.RelShuttle; +import org.apache.calcite.rel.core.TableScan; + +/** + * represent a common sub-plan as a table scan, specific to a {@code CommonOptTable} + */ +public class CommonTableScan extends TableScan { + public CommonTableScan(RelOptCluster cluster, RelTraitSet traitSet, RelOptTable table) { + super(cluster, traitSet, ImmutableList.of(), table); + } + + @Override + public RelNode accept(RelShuttle shuttle) { + if (shuttle instanceof GraphShuttle) { + return ((GraphShuttle) 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 a114357028f1..7f89a5b9bc24 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 @@ -16,7 +16,9 @@ package com.alibaba.graphscope.common.ir.rel; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelOptCluster; @@ -28,6 +30,7 @@ import org.apache.calcite.rel.hint.RelHint; import org.apache.calcite.rel.type.*; import org.apache.calcite.rex.RexNode; +import org.apache.calcite.rex.RexShuttle; import java.util.List; @@ -77,6 +80,34 @@ public Project copy( this.isAppend); } + @Override + public RelNode accept(RexShuttle shuttle) { + List exps = shuttle.apply(this.exps); + if (this.exps == exps) { + return this; + } else { + // if exps are changed, we need to update the row type. + // here we override the original method to innovate and define our own type info for the + // project + Preconditions.checkArgument( + exps.size() == this.exps.size(), + "rex shuttle should not change the size of exps"); + List oldFields = this.getRowType().getFieldList(); + List newFields = Lists.newArrayList(); + for (int i = 0; i < exps.size(); ++i) { + RelDataTypeField oldField = oldFields.get(i); + newFields.add( + new RelDataTypeFieldImpl( + oldField.getName(), oldField.getIndex(), exps.get(i).getType())); + } + return this.copy( + this.traitSet, + this.input, + exps, + new RelRecordType(StructKind.FULLY_QUALIFIED, newFields)); + } + } + @Override public RelWriter explainTerms(RelWriter pw) { return super.explainTerms(pw).item("isAppend", isAppend); diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphShuttle.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphShuttle.java index bcec982547a4..ddba9ee43220 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphShuttle.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/rel/GraphShuttle.java @@ -79,4 +79,8 @@ public RelNode visit(GraphPhysicalGetV physicalGetV) { public RelNode visit(GraphLogicalDedupBy dedupBy) { return visitChildren(dedupBy); } + + public RelNode visit(CommonTableScan tableScan) { + return tableScan; + } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelProtoPhysicalBuilder.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelProtoPhysicalBuilder.java index 5fcb905b2195..e33739390874 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelProtoPhysicalBuilder.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelProtoPhysicalBuilder.java @@ -17,6 +17,8 @@ package com.alibaba.graphscope.common.ir.runtime.proto; import com.alibaba.graphscope.common.config.Configs; +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.CommonTableScan; import com.alibaba.graphscope.common.ir.rel.GraphShuttle; import com.alibaba.graphscope.common.ir.runtime.PhysicalBuilder; import com.alibaba.graphscope.common.ir.runtime.PhysicalPlan; @@ -24,12 +26,20 @@ import com.alibaba.graphscope.common.store.IrMeta; import com.alibaba.graphscope.gaia.proto.GraphAlgebra; import com.alibaba.graphscope.gaia.proto.GraphAlgebraPhysical; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.protobuf.util.JsonFormat; +import org.apache.calcite.plan.RelDigest; import org.apache.calcite.rel.RelNode; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -39,16 +49,24 @@ public class GraphRelProtoPhysicalBuilder extends PhysicalBuilder { private static final Logger logger = LoggerFactory.getLogger(GraphRelProtoPhysicalBuilder.class); - protected GraphShuttle relShuttle; - GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder; + private final GraphShuttle relShuttle; + private final GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder; + // map each rel (union/join...) to its corresponding common sub-plans, i.e. in query + // `g.V().out().union(out(), out())`, + // `g.V().out()` is a common sub-plan, the pair of is recorded in this map + private final IdentityHashMap> relToCommons; public GraphRelProtoPhysicalBuilder( Configs graphConfig, IrMeta irMeta, LogicalPlan logicalPlan) { super(logicalPlan); this.physicalBuilder = GraphAlgebraPhysical.PhysicalPlan.newBuilder(); + this.relToCommons = createRelToCommons(logicalPlan); this.relShuttle = new GraphRelToProtoConverter( - irMeta.getSchema().isColumnId(), graphConfig, this.physicalBuilder); + irMeta.getSchema().isColumnId(), + graphConfig, + this.physicalBuilder, + this.relToCommons); } @Override @@ -93,4 +111,89 @@ private String getPlanAsJson(GraphAlgebraPhysical.PhysicalPlan physicalPlan) { @Override public void close() throws Exception {} + + private IdentityHashMap> createRelToCommons( + LogicalPlan logicalPlan) { + IdentityHashMap> relToCommons = new IdentityHashMap<>(); + RelNode top = logicalPlan.getRegularQuery(); + if (top == null) return relToCommons; + List rels = Lists.newArrayList(top); + List commons = Lists.newArrayList(); + while (!rels.isEmpty()) { + List inputCommons = getInputCommons(rels.remove(0)); + commons.addAll(inputCommons); + inputCommons.forEach( + k -> { + rels.add(((CommonOptTable) k.getTable()).getCommon()); + }); + } + // use linked hash map to keep the order of commons + Map> digestToCommons = Maps.newLinkedHashMap(); + commons.forEach( + k -> { + RelDigest digest = k.getRelDigest(); + digestToCommons.computeIfAbsent(digest, k1 -> Lists.newArrayList()).add(k); + }); + digestToCommons.forEach( + (k, v) -> { + if (v.size() > 1) { + RelNode ancestor = lowestCommonAncestor(top, v, Lists.newArrayList()); + Preconditions.checkArgument( + ancestor != null, + "lowest common ancestor of [%s] should not be null", + v); + relToCommons + .computeIfAbsent(ancestor, k1 -> Lists.newArrayList()) + .add(v.get(0)); + } + }); + return relToCommons; + } + + /** + * find the lowest common ancestor (union/join...) of a list of common table scans + * @param top + * @param commons + * @param contains + * @return + */ + private @Nullable RelNode lowestCommonAncestor( + RelNode top, List commons, List contains) { + List> inputContains = Lists.newArrayList(); + for (RelNode input : top.getInputs()) { + inputContains.add(Lists.newArrayList()); + RelNode inputAncestor = + lowestCommonAncestor( + input, commons, inputContains.get(inputContains.size() - 1)); + if (inputAncestor != null) { + return inputAncestor; + } + } + if (top instanceof CommonTableScan) { + CommonTableScan common = (CommonTableScan) top; + inputContains.add(Lists.newArrayList()); + lowestCommonAncestor( + ((CommonOptTable) common.getTable()).getCommon(), + commons, + inputContains.get(inputContains.size() - 1)); + } + inputContains.forEach(k -> contains.addAll(k)); + if (top instanceof CommonTableScan && commons.contains(top)) { + contains.add((CommonTableScan) top); + } + if (contains.size() >= commons.size() && contains.containsAll(commons)) { + return top; + } + return null; + } + + private List getInputCommons(RelNode top) { + List inputCommons = Lists.newArrayList(); + if (top instanceof CommonTableScan) { + inputCommons.add((CommonTableScan) top); + } else { + top.getInputs().forEach(k -> inputCommons.addAll(getInputCommons(k))); + } + return inputCommons; + } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelToProtoConverter.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelToProtoConverter.java index 53f045c1c415..eeca97c8838b 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelToProtoConverter.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/runtime/proto/GraphRelToProtoConverter.java @@ -18,6 +18,8 @@ import com.alibaba.graphscope.common.config.Configs; import com.alibaba.graphscope.common.config.PegasusConfig; +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.*; import com.alibaba.graphscope.common.ir.rel.GraphLogicalAggregate; import com.alibaba.graphscope.common.ir.rel.GraphLogicalDedupBy; import com.alibaba.graphscope.common.ir.rel.GraphLogicalProject; @@ -49,20 +51,21 @@ import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.logical.LogicalFilter; import org.apache.calcite.rel.logical.LogicalJoin; +import org.apache.calcite.rel.logical.LogicalUnion; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rex.*; import org.apache.calcite.sql.SqlKind; import org.apache.commons.lang3.ObjectUtils; +import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; - public class GraphRelToProtoConverter extends GraphShuttle { private static final Logger logger = LoggerFactory.getLogger(GraphRelToProtoConverter.class); private final boolean isColumnId; @@ -71,11 +74,23 @@ public class GraphRelToProtoConverter extends GraphShuttle { private GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder; private final boolean isPartitioned; private boolean preCacheEdgeProps; + private final IdentityHashMap> relToCommons; + private final int depth; public GraphRelToProtoConverter( boolean isColumnId, Configs configs, - GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder) { + GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder, + IdentityHashMap> relToCommons) { + this(isColumnId, configs, physicalBuilder, relToCommons, 0); + } + + public GraphRelToProtoConverter( + boolean isColumnId, + Configs configs, + GraphAlgebraPhysical.PhysicalPlan.Builder physicalBuilder, + IdentityHashMap> relToCommons, + int depth) { this.isColumnId = isColumnId; this.rexBuilder = GraphPlanner.rexBuilderFactory.apply(configs); this.graphConfig = configs; @@ -86,6 +101,8 @@ public GraphRelToProtoConverter( // currently, since the store doesn't support get properties from edges, we always need to // precache edge properties. this.preCacheEdgeProps = true; + this.relToCommons = relToCommons; + this.depth = depth; } @Override @@ -644,9 +661,13 @@ public RelNode visit(LogicalJoin join) { GraphAlgebraPhysical.PhysicalPlan.newBuilder(); RelNode left = join.getLeft(); - left.accept(new GraphRelToProtoConverter(isColumnId, graphConfig, leftPlanBuilder)); + left.accept( + new GraphRelToProtoConverter( + isColumnId, graphConfig, leftPlanBuilder, this.relToCommons, depth + 1)); RelNode right = join.getRight(); - right.accept(new GraphRelToProtoConverter(isColumnId, graphConfig, rightPlanBuilder)); + right.accept( + new GraphRelToProtoConverter( + isColumnId, graphConfig, rightPlanBuilder, this.relToCommons, depth + 1)); if (isPartitioned) { Map> leftTagColumns = @@ -672,6 +693,53 @@ public RelNode visit(LogicalJoin join) { return join; } + @Override + public RelNode visit(LogicalUnion union) { + List commons = relToCommons.get(union); + if (ObjectUtils.isNotEmpty(commons)) { + // add commons before union + GraphAlgebraPhysical.PhysicalPlan.Builder commonPlanBuilder = + GraphAlgebraPhysical.PhysicalPlan.newBuilder(); + for (int i = commons.size() - 1; i >= 0; --i) { + RelNode commonRel = ((CommonOptTable) commons.get(i).getTable()).getCommon(); + commonRel.accept( + new GraphRelToProtoConverter( + isColumnId, + graphConfig, + commonPlanBuilder, + this.relToCommons, + depth + 1)); + } + physicalBuilder.addAllPlan(commonPlanBuilder.getPlanList()); + } else if (this.depth == 0) { + // add a dummy root if the root union does not have any common sub-plans + physicalBuilder.addPlan( + GraphAlgebraPhysical.PhysicalOpr.newBuilder() + .setOpr( + GraphAlgebraPhysical.PhysicalOpr.Operator.newBuilder() + .setRoot(GraphAlgebraPhysical.Root.newBuilder()))); + } + GraphAlgebraPhysical.PhysicalOpr.Builder oprBuilder = + GraphAlgebraPhysical.PhysicalOpr.newBuilder(); + GraphAlgebraPhysical.Union.Builder unionBuilder = GraphAlgebraPhysical.Union.newBuilder(); + for (RelNode input : union.getInputs()) { + GraphAlgebraPhysical.PhysicalPlan.Builder inputPlanBuilder = + GraphAlgebraPhysical.PhysicalPlan.newBuilder(); + input.accept( + new GraphRelToProtoConverter( + isColumnId, + graphConfig, + inputPlanBuilder, + this.relToCommons, + depth + 1)); + unionBuilder.addSubPlans(inputPlanBuilder); + } + oprBuilder.setOpr( + GraphAlgebraPhysical.PhysicalOpr.Operator.newBuilder().setUnion(unionBuilder)); + physicalBuilder.addPlan(oprBuilder.build()); + return union; + } + private List getLeftRightVariables(RexNode condition) { List vars = Lists.newArrayList(); if (condition instanceof RexCall) { 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 1b1aeae98f2f..6ebbccc83bac 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 @@ -558,13 +558,25 @@ private ColumnField getAliasField(String alias) { RelNode cur = inputQueue.remove(0); List fields = cur.getRowType().getFieldList(); // to support `head` in gremlin - if (nodeIdx++ == 0 && alias == AliasInference.DEFAULT_NAME && fields.size() == 1) { - return new ColumnField( - AliasInference.DEFAULT_COLUMN_ID, - new RelDataTypeFieldImpl( - AliasInference.DEFAULT_NAME, - AliasInference.DEFAULT_ID, - fields.get(0).getType())); + if (nodeIdx++ == 0 && alias == AliasInference.DEFAULT_NAME) { + if (fields.size() == 1) { + return new ColumnField( + AliasInference.DEFAULT_COLUMN_ID, + new RelDataTypeFieldImpl( + AliasInference.DEFAULT_NAME, + AliasInference.DEFAULT_ID, + fields.get(0).getType())); + } else if (cur + instanceof + CommonTableScan) { // specific implementation for gremlin, to get `head` + // in nested traversal + return new ColumnField( + AliasInference.DEFAULT_COLUMN_ID, + new RelDataTypeFieldImpl( + AliasInference.DEFAULT_NAME, + AliasInference.DEFAULT_ID, + fields.get(fields.size() - 1).getType())); + } } for (RelDataTypeField field : fields) { if (alias != AliasInference.DEFAULT_NAME && field.getName().equals(alias)) { @@ -1690,8 +1702,115 @@ public RexNode not(RexNode operand) { * @return */ @Override - public RelBuilder convert(RelDataType castRowType, boolean rename) { + public GraphBuilder convert(RelDataType castRowType, boolean rename) { // do nothing return this; } + + /** + * assign a new alias to the top node, we implement the function specifically to support the gremlin's `as` step + * @param alias + * @return + */ + @Override + public GraphBuilder as(String alias) { + RelNode top = requireNonNull(peek(), "frame stack is empty"); + // skip intermediate operations which make no changes to the row type, i.e. + // filter/limit/dedup... + while (!top.getInputs().isEmpty() && top.getInput(0).getRowType() == top.getRowType()) { + top = top.getInput(0); + } + if (top instanceof AbstractBindableTableScan + || top instanceof GraphLogicalPathExpand + || top instanceof GraphLogicalProject + || top instanceof GraphLogicalAggregate) { + RelDataType rowType = top.getRowType(); + // we can assign the alias only if the top node has only one field, otherwise we skip + // the + // operation + if (rowType.getFieldList().size() != 1) { + return this; + } + build(); + if (!top.getInputs().isEmpty()) { + push(top.getInput(0)); + } + if (top instanceof GraphLogicalSource) { + GraphLogicalSource source = (GraphLogicalSource) top; + source( + new SourceConfig( + source.getOpt(), getLabelConfig(source.getTableConfig()), alias)); + if (source.getUniqueKeyFilters() != null) { + filter(source.getUniqueKeyFilters()); + } + if (ObjectUtils.isNotEmpty(source.getFilters())) { + filter(source.getFilters()); + } + } else if (top instanceof GraphLogicalExpand) { + GraphLogicalExpand expand = (GraphLogicalExpand) top; + expand( + new ExpandConfig( + expand.getOpt(), getLabelConfig(expand.getTableConfig()), alias)); + if (ObjectUtils.isNotEmpty(expand.getFilters())) { + filter(expand.getFilters()); + } + } else if (top instanceof GraphLogicalGetV) { + GraphLogicalGetV getV = (GraphLogicalGetV) top; + getV(new GetVConfig(getV.getOpt(), getLabelConfig(getV.getTableConfig()), alias)); + if (ObjectUtils.isNotEmpty(getV.getFilters())) { + filter(getV.getFilters()); + } + } else if (top instanceof GraphLogicalPathExpand) { + GraphLogicalPathExpand pxdExpand = (GraphLogicalPathExpand) top; + GraphLogicalExpand expand = (GraphLogicalExpand) pxdExpand.getExpand(); + GraphLogicalGetV getV = (GraphLogicalGetV) pxdExpand.getGetV(); + PathExpandConfig.Builder pxdBuilder = PathExpandConfig.newBuilder(this); + RexNode offset = pxdExpand.getOffset(), fetch = pxdExpand.getFetch(); + pxdBuilder + .expand( + new ExpandConfig( + expand.getOpt(), + getLabelConfig(expand.getTableConfig()), + expand.getAliasName())) + .getV( + new GetVConfig( + getV.getOpt(), + getLabelConfig(getV.getTableConfig()), + getV.getAliasName())) + .pathOpt(pxdExpand.getPathOpt()) + .resultOpt(pxdExpand.getResultOpt()) + .range( + offset == null + ? 0 + : ((RexLiteral) offset).getValueAs(Integer.class), + fetch == null ? -1 : ((RexLiteral) fetch).getValueAs(Integer.class)) + .startAlias(pxdExpand.getStartAlias().getAliasName()) + .alias(alias); + pathExpand(pxdBuilder.build()); + } else if (top instanceof GraphLogicalProject) { + GraphLogicalProject project = (GraphLogicalProject) top; + project(project.getProjects(), Lists.newArrayList(alias), project.isAppend()); + } else if (top instanceof GraphLogicalAggregate) { + GraphLogicalAggregate aggregate = (GraphLogicalAggregate) top; + // if group key is empty, we can assign the alias to the single aggregated value in + // group + if (aggregate.getGroupKey().groupKeyCount() == 0 + && aggregate.getAggCalls().size() == 1) { + GraphAggCall aggCall = aggregate.getAggCalls().get(0); + aggregate(aggregate.getGroupKey(), ImmutableList.of(aggCall.as(alias))); + } + } + } + return this; + } + + private LabelConfig getLabelConfig(TableConfig tableConfig) { + List labels = + tableConfig.getTables().stream() + .map(k -> k.getQualifiedName().get(0)) + .collect(Collectors.toList()); + LabelConfig labelConfig = new LabelConfig(tableConfig.isAll()); + labels.forEach(k -> labelConfig.addLabel(k)); + return labelConfig; + } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java index dc692e7539b6..8691c2518b10 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/GraphPlanner.java @@ -23,6 +23,7 @@ import com.alibaba.graphscope.common.ir.meta.reader.LocalMetaDataReader; import com.alibaba.graphscope.common.ir.meta.schema.GraphOptSchema; import com.alibaba.graphscope.common.ir.meta.schema.IrGraphSchema; +import com.alibaba.graphscope.common.ir.planner.GraphHepPlanner; import com.alibaba.graphscope.common.ir.planner.rules.DegreeFusionRule; import com.alibaba.graphscope.common.ir.planner.rules.ExpandGetVFusionRule; import com.alibaba.graphscope.common.ir.planner.rules.FieldTrimRule; @@ -235,7 +236,7 @@ else if (k.equals( hepBuilder.addRuleInstance( k.withRelBuilderFactory(graphBuilderFactory).toRule()); }); - return new HepPlanner(hepBuilder.build()); + return new GraphHepPlanner(hepBuilder.build()); case CBO: default: throw new UnsupportedOperationException( diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/Utils.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/Utils.java index 130bd4f33547..0d38252fa7e9 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/Utils.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/common/ir/tools/Utils.java @@ -16,11 +16,13 @@ package com.alibaba.graphscope.common.ir.tools; -import com.google.common.collect.ImmutableList; +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.CommonTableScan; import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.collect.Sets; +import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeField; @@ -62,20 +64,51 @@ public static RelDataType getOutputType(RelNode topNode) { } public static List getValuesAsList(Comparable value) { - ImmutableList.Builder valueBuilder = ImmutableList.builder(); + List values = Lists.newArrayList(); if (value instanceof NlsString) { - valueBuilder.add(((NlsString) value).getValue()); + values.add(((NlsString) value).getValue()); } else if (value instanceof Sarg) { Sarg sarg = (Sarg) value; if (sarg.isPoints()) { Set> rangeSets = sarg.rangeSet.asRanges(); for (Range range : rangeSets) { - valueBuilder.addAll(getValuesAsList(range.lowerEndpoint())); + values.addAll(getValuesAsList(range.lowerEndpoint())); } } } else { - valueBuilder.add(value); + values.add(value); } - return valueBuilder.build(); + return values; + } + + /** + * print root {@code RelNode} and nested {@code RelNode}s in each {@code CommonTableScan} + * @param node + * @return + */ + public static String toString(RelNode node) { + return toString("root:", node, Sets.newHashSet()); + } + + private static String toString(String header, RelNode node, Set dedup) { + StringBuilder builder = new StringBuilder(); + if (!header.isEmpty()) { + dedup.add(header); + builder.append(header).append("\n"); + } + builder.append(RelOptUtil.toString(node)); + List inputs = Lists.newArrayList(node.getInputs()); + while (!inputs.isEmpty()) { + RelNode input = inputs.remove(0); + if (input instanceof CommonTableScan) { + CommonOptTable optTable = (CommonOptTable) ((CommonTableScan) input).getTable(); + String name = optTable.getQualifiedName().get(0) + ":"; + if (!dedup.contains(name)) { + builder.append(toString(name, optTable.getCommon(), dedup)); + } + } + inputs.addAll(input.getInputs()); + } + return builder.toString(); } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/GraphBuilderVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/GraphBuilderVisitor.java index c6cc194996f8..279245743e62 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/GraphBuilderVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/GraphBuilderVisitor.java @@ -38,7 +38,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import org.antlr.v4.runtime.tree.ParseTree; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Aggregate; import org.apache.calcite.rel.core.Project; @@ -77,14 +76,7 @@ public GraphBuilder visitQuery(GremlinGSParser.QueryContext ctx) { @Override public GraphBuilder visitTraversalSourceSpawnMethod_V( GremlinGSParser.TraversalSourceSpawnMethod_VContext ctx) { - builder.source( - new SourceConfig( - GraphOpt.Source.VERTEX, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalSourceSpawnMethodContext) - ctx.getParent())))); + builder.source(new SourceConfig(GraphOpt.Source.VERTEX, new LabelConfig(true))); if (ctx.integerLiteralList() != null) { Object[] ids = GenericLiteralVisitor.getIntegerLiteralList(ctx.integerLiteralList()); if (ids.length == 1) { @@ -109,14 +101,7 @@ public GraphBuilder visitTraversalSourceSpawnMethod_V( @Override public GraphBuilder visitTraversalSourceSpawnMethod_E( GremlinGSParser.TraversalSourceSpawnMethod_EContext ctx) { - builder.source( - new SourceConfig( - GraphOpt.Source.EDGE, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalSourceSpawnMethodContext) - ctx.getParent())))); + builder.source(new SourceConfig(GraphOpt.Source.EDGE, new LabelConfig(true))); if (ctx.integerLiteralList() != null) { Object[] ids = GenericLiteralVisitor.getIntegerLiteralList(ctx.integerLiteralList()); if (ids.length == 1) { @@ -269,13 +254,7 @@ public GraphBuilder visitTraversalMethod_hasNot( @Override public GraphBuilder visitTraversalMethod_outE(GremlinGSParser.TraversalMethod_outEContext ctx) { builder.expand( - new ExpandConfig( - GraphOpt.Expand.OUT, - getLabelConfig(ctx.stringLiteralList()), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + new ExpandConfig(GraphOpt.Expand.OUT, getLabelConfig(ctx.stringLiteralList()))); if (ctx.traversalMethod_inV() != null) { visitTraversalMethod_inV(ctx.traversalMethod_inV()); } @@ -285,13 +264,7 @@ public GraphBuilder visitTraversalMethod_outE(GremlinGSParser.TraversalMethod_ou @Override public GraphBuilder visitTraversalMethod_inE(GremlinGSParser.TraversalMethod_inEContext ctx) { builder.expand( - new ExpandConfig( - GraphOpt.Expand.IN, - getLabelConfig(ctx.stringLiteralList()), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + new ExpandConfig(GraphOpt.Expand.IN, getLabelConfig(ctx.stringLiteralList()))); if (ctx.traversalMethod_outV() != null) { visitTraversalMethod_outV(ctx.traversalMethod_outV()); } @@ -302,13 +275,7 @@ public GraphBuilder visitTraversalMethod_inE(GremlinGSParser.TraversalMethod_inE public GraphBuilder visitTraversalMethod_bothE( GremlinGSParser.TraversalMethod_bothEContext ctx) { builder.expand( - new ExpandConfig( - GraphOpt.Expand.BOTH, - getLabelConfig(ctx.stringLiteralList()), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + new ExpandConfig(GraphOpt.Expand.BOTH, getLabelConfig(ctx.stringLiteralList()))); if (ctx.traversalMethod_otherV() != null) { visitTraversalMethod_otherV(ctx.traversalMethod_otherV()); } @@ -317,48 +284,18 @@ public GraphBuilder visitTraversalMethod_bothE( @Override public GraphBuilder visitTraversalMethod_outV(GremlinGSParser.TraversalMethod_outVContext ctx) { - ParseTree parent = ctx.getParent(); - if (parent instanceof GremlinGSParser.TraversalMethod_inEContext) { - parent = parent.getParent(); - } - return builder.getV( - new GetVConfig( - GraphOpt.GetV.START, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) parent)))); + return builder.getV(new GetVConfig(GraphOpt.GetV.START, new LabelConfig(true))); } @Override public GraphBuilder visitTraversalMethod_inV(GremlinGSParser.TraversalMethod_inVContext ctx) { - ParseTree parent = ctx.getParent(); - if (parent instanceof GremlinGSParser.TraversalMethod_outEContext) { - parent = parent.getParent(); - } - return builder.getV( - new GetVConfig( - GraphOpt.GetV.END, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) parent)))); + return builder.getV(new GetVConfig(GraphOpt.GetV.END, new LabelConfig(true))); } @Override public GraphBuilder visitTraversalMethod_otherV( GremlinGSParser.TraversalMethod_otherVContext ctx) { - ParseTree parent = ctx.getParent(); - if (parent instanceof GremlinGSParser.TraversalMethod_bothEContext) { - parent = parent.getParent(); - } - return builder.getV( - new GetVConfig( - GraphOpt.GetV.OTHER, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) parent)))); + return builder.getV(new GetVConfig(GraphOpt.GetV.OTHER, new LabelConfig(true))); } @Override @@ -367,21 +304,14 @@ public GraphBuilder visitTraversalMethod_endV(GremlinGSParser.TraversalMethod_en if (peek instanceof GraphLogicalPathExpand) { GraphLogicalPathExpand pathExpand = (GraphLogicalPathExpand) peek; GraphLogicalExpand expand = (GraphLogicalExpand) pathExpand.getExpand(); - String tag = - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); switch (expand.getOpt()) { case OUT: - return builder.getV( - new GetVConfig(GraphOpt.GetV.END, new LabelConfig(true), tag)); + return builder.getV(new GetVConfig(GraphOpt.GetV.END, new LabelConfig(true))); case IN: - return builder.getV( - new GetVConfig(GraphOpt.GetV.START, new LabelConfig(true), tag)); + return builder.getV(new GetVConfig(GraphOpt.GetV.START, new LabelConfig(true))); case BOTH: default: - return builder.getV( - new GetVConfig(GraphOpt.GetV.OTHER, new LabelConfig(true), tag)); + return builder.getV(new GetVConfig(GraphOpt.GetV.OTHER, new LabelConfig(true))); } } throw new InvalidGremlinScriptException("endV should follow with path expand"); @@ -396,14 +326,7 @@ public GraphBuilder visitTraversalMethod_out(GremlinGSParser.TraversalMethod_out return builder.expand( new ExpandConfig( GraphOpt.Expand.OUT, getLabelConfig(ctx.stringLiteralList()))) - .getV( - new GetVConfig( - GraphOpt.GetV.END, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + .getV(new GetVConfig(GraphOpt.GetV.END, new LabelConfig(true))); } } @@ -416,14 +339,7 @@ public GraphBuilder visitTraversalMethod_in(GremlinGSParser.TraversalMethod_inCo return builder.expand( new ExpandConfig( GraphOpt.Expand.IN, getLabelConfig(ctx.stringLiteralList()))) - .getV( - new GetVConfig( - GraphOpt.GetV.START, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + .getV(new GetVConfig(GraphOpt.GetV.START, new LabelConfig(true))); } } @@ -436,14 +352,7 @@ public GraphBuilder visitTraversalMethod_both(GremlinGSParser.TraversalMethod_bo return builder.expand( new ExpandConfig( GraphOpt.Expand.BOTH, getLabelConfig(ctx.stringLiteralList()))) - .getV( - new GetVConfig( - GraphOpt.GetV.OTHER, - new LabelConfig(true), - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())))); + .getV(new GetVConfig(GraphOpt.GetV.OTHER, new LabelConfig(true))); } } @@ -454,7 +363,7 @@ public GraphBuilder visitTraversalMethod_with(GremlinGSParser.TraversalMethod_wi @Override public GraphBuilder visitTraversalMethod_as(GremlinGSParser.TraversalMethod_asContext ctx) { - return builder; + return builder.as(GenericLiteralVisitor.getStringLiteral(ctx.stringLiteral())); } @Override @@ -470,14 +379,7 @@ public GraphBuilder visitTraversalMethod_valueMap( builder.literal(k), builder.variable(null, k))) .collect(Collectors.toList())); - String tag = - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); - return builder.project( - ImmutableList.of(expr), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), - true); + return builder.project(ImmutableList.of(expr), ImmutableList.of(), true); } @Override @@ -487,17 +389,7 @@ public GraphBuilder visitTraversalMethod_values( RexNode expr = builder.variable( null, GenericLiteralVisitor.getStringLiteral(ctx.stringLiteral())); - String tag = - (ctx.getParent() instanceof GremlinGSParser.TraversalMethodContext) - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - ctx.getParent())) - : null; - return builder.project( - ImmutableList.of(expr), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), - true); + return builder.project(ImmutableList.of(expr), ImmutableList.of(), true); } throw new UnsupportedEvalException(ctx.getClass(), "supported pattern is [values('..')]"); } @@ -515,25 +407,12 @@ public GraphBuilder visitTraversalMethod_elementMap( builder.literal(k), builder.variable(null, k))) .collect(Collectors.toList())); - String tag = - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); - return builder.project( - ImmutableList.of(expr), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), - true); + return builder.project(ImmutableList.of(expr), ImmutableList.of(), true); } @Override public GraphBuilder visitTraversalMethod_select( GremlinGSParser.TraversalMethod_selectContext ctx) { - String tag = - (ctx.getParent() instanceof GremlinGSParser.TraversalMethodContext) - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())) - : null; RexNode expr; if (ctx.stringLiteral() != null) { List selectTags = @@ -578,10 +457,7 @@ public GraphBuilder visitTraversalMethod_select( GremlinGSParser.TraversalMethod_selectbyContext.class, ctx.getText() + " is unsupported yet"); } - return builder.project( - ImmutableList.of(expr), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), - true); + return builder.project(ImmutableList.of(expr), ImmutableList.of(), true); } @Override @@ -649,102 +525,42 @@ public GraphBuilder visitTraversalMethod_groupCount( @Override public GraphBuilder visitTraversalMethod_count( GremlinGSParser.TraversalMethod_countContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; - return (GraphBuilder) builder.aggregate(builder.groupKey(), builder.countStar(alias)); + return (GraphBuilder) builder.aggregate(builder.groupKey(), builder.countStar(null)); } @Override public GraphBuilder visitTraversalMethod_fold(GremlinGSParser.TraversalMethod_foldContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; - return (GraphBuilder) builder.aggregate(builder.groupKey(), builder.collect(false, alias)); + return (GraphBuilder) builder.aggregate(builder.groupKey(), builder.collect(false, null)); } @Override public GraphBuilder visitTraversalMethod_sum(GremlinGSParser.TraversalMethod_sumContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; return (GraphBuilder) builder.aggregate( builder.groupKey(), - builder.sum(false, alias, builder.variable((String) null))); + builder.sum(false, null, builder.variable((String) null))); } @Override public GraphBuilder visitTraversalMethod_min(GremlinGSParser.TraversalMethod_minContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; return (GraphBuilder) builder.aggregate( - builder.groupKey(), builder.min(alias, builder.variable((String) null))); + builder.groupKey(), builder.min(null, builder.variable((String) null))); } @Override public GraphBuilder visitTraversalMethod_max(GremlinGSParser.TraversalMethod_maxContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; return (GraphBuilder) builder.aggregate( - builder.groupKey(), builder.max(alias, builder.variable((String) null))); + builder.groupKey(), builder.max(null, builder.variable((String) null))); } @Override public GraphBuilder visitTraversalMethod_mean(GremlinGSParser.TraversalMethod_meanContext ctx) { - ParseTree aggCtx = ctx.getParent(); - String alias = - aggCtx != null - && aggCtx.getParent() - instanceof GremlinGSParser.TraversalMethodContext - ? getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) - aggCtx.getParent())) - : null; return (GraphBuilder) builder.aggregate( builder.groupKey(), - builder.avg(false, alias, builder.variable((String) null))); + builder.avg(false, null, builder.variable((String) null))); } @Override @@ -806,28 +622,46 @@ public GraphBuilder visitTraversalMethod_is(GremlinGSParser.TraversalMethod_isCo @Override public GraphBuilder visitTraversalMethod_label( GremlinGSParser.TraversalMethod_labelContext ctx) { - String tag = - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); return builder.project( ImmutableList.of(builder.variable(null, T.label.getAccessor())), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), + ImmutableList.of(), true); } @Override public GraphBuilder visitTraversalMethod_id(GremlinGSParser.TraversalMethod_idContext ctx) { - String tag = - getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); return builder.project( ImmutableList.of(builder.variable(null, T.id.getAccessor())), - tag == null ? ImmutableList.of() : ImmutableList.of(tag), + ImmutableList.of(), true); } + @Override + public GraphBuilder visitTraversalMethod_union( + GremlinGSParser.TraversalMethod_unionContext ctx) { + GremlinGSParser.NestedTraversalExprContext exprCtx = ctx.nestedTraversalExpr(); + NestedTraversalRelVisitor visitor = new NestedTraversalRelVisitor(builder); + List branches = Lists.newArrayList(); + for (int i = 0; i < exprCtx.getChildCount(); ++i) { + if (!(exprCtx.getChild(i) instanceof GremlinGSParser.NestedTraversalContext)) continue; + GremlinGSParser.NestedTraversalContext nestedCtx = + (GremlinGSParser.NestedTraversalContext) exprCtx.getChild(i); + branches.add(visitor.visitNestedTraversal(nestedCtx)); + } + Preconditions.checkArgument(branches.size() > 0, "union should have at least one branch"); + builder.build(); + for (RelNode branch : branches) { + builder.push(branch); + } + return (GraphBuilder) builder.union(true, branches.size()); + } + + @Override + public GraphBuilder visitTraversalMethod_identity( + GremlinGSParser.TraversalMethod_identityContext ctx) { + return builder; + } + public GraphBuilder getGraphBuilder() { return this.builder; } @@ -853,7 +687,7 @@ private RelBuilder.GroupKey convertGroupKeyBy( if (byCtx == null) continue; Pair exprWithAlias = Utils.convertExprToPair( - new NestedTraversalVisitor(this.builder, null) + new NestedTraversalRexVisitor(this.builder, null, keyCtx) .visitNestedTraversal(byCtx.nestedTraversal())); exprs.add(exprWithAlias.getValue0()); String alias = exprWithAlias.getValue1(); @@ -920,7 +754,7 @@ private RexNode convertDedupByCtx( } else if (byCtx.nestedTraversal() != null) { RexNode rex = Utils.convertExprToPair( - new NestedTraversalVisitor(this.builder, tag) + new NestedTraversalRexVisitor(this.builder, tag, byCtx) .visitNestedTraversal(byCtx.nestedTraversal())) .getValue0(); if (rex instanceof RexCall) { @@ -954,7 +788,7 @@ private List convertOrderByCtx(GremlinGSParser.TraversalMethod_orderbyC } else if (byCtx.nestedTraversal() != null) { RexNode rex = Utils.convertExprToPair( - new NestedTraversalVisitor(this.builder, null) + new NestedTraversalRexVisitor(this.builder, null, byCtx) .visitNestedTraversal(byCtx.nestedTraversal())) .getValue0(); // todo: RexCall need to be computed in advance which will change the current head @@ -1004,7 +838,7 @@ private RexNode convertSelectByCtx( .collect(Collectors.toList())); } else if (byCtx.nestedTraversal() != null) { return Utils.convertExprToPair( - (new NestedTraversalVisitor(this.builder, tag)) + (new NestedTraversalRexVisitor(this.builder, tag, byCtx)) .visitNestedTraversal(byCtx.nestedTraversal())) .getValue0(); } @@ -1064,34 +898,6 @@ private List getAllProperties(@Nullable String tag) { return dataType.getFieldList().stream().map(k -> k.getName()).collect(Collectors.toList()); } - protected @Nullable String getNextTag(TraversalMethodIterator methodIterator) { - List tags = Lists.newArrayList(); - while (methodIterator.hasNext()) { - GremlinGSParser.TraversalMethodContext next = methodIterator.next(); - if (next.traversalMethod_as() != null) { - tags.add( - GenericLiteralVisitor.getStringLiteral( - next.traversalMethod_as().stringLiteral())); - } else if (next.traversalMethod_has() != null - || next.traversalMethod_hasId() != null - || next.traversalMethod_hasLabel() != null - || next.traversalMethod_hasNot() != null - || next.traversalMethod_is() != null - || next.traversalMethod_where() != null - || next.traversalMethod_not() != null - || next.traversalMethod_identity() != null - || next.traversalMethod_limit() != null - || next.traversalMethod_order() != null - || next.traversalMethod_dedup() != null - || next.traversalMethod_with() != null) { - // continue - } else { - break; - } - } - return tags.isEmpty() ? null : tags.get(tags.size() - 1); - } - private GraphBuilder appendTailProject() { Preconditions.checkArgument(builder.size() > 0, "builder should not be empty"); RelNode top = builder.peek(); diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRelVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRelVisitor.java new file mode 100644 index 000000000000..1d6bcd37ed0b --- /dev/null +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRelVisitor.java @@ -0,0 +1,55 @@ +package com.alibaba.graphscope.gremlin.antlr4x.visitor; + +import static java.util.Objects.requireNonNull; + +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.CommonTableScan; +import com.alibaba.graphscope.common.ir.rel.graph.GraphLogicalSource; +import com.alibaba.graphscope.common.ir.tools.GraphBuilder; +import com.alibaba.graphscope.grammar.GremlinGSBaseVisitor; +import com.alibaba.graphscope.grammar.GremlinGSParser; + +import org.apache.calcite.plan.GraphOptCluster; +import org.apache.calcite.plan.RelOptTable; +import org.apache.calcite.rel.RelNode; +import org.apache.commons.lang3.ObjectUtils; + +/** + * convert sub traversal nested in {@code NestedTraversalContext} to RelNode + */ +public class NestedTraversalRelVisitor extends GremlinGSBaseVisitor { + private final GraphBuilder parentBuilder; + private final GraphBuilder nestedBuilder; + + public NestedTraversalRelVisitor(GraphBuilder parentBuilder) { + this.parentBuilder = parentBuilder; + this.nestedBuilder = + GraphBuilder.create( + this.parentBuilder.getContext(), + (GraphOptCluster) this.parentBuilder.getCluster(), + this.parentBuilder.getRelOptSchema()); + } + + @Override + public RelNode visitNestedTraversal(GremlinGSParser.NestedTraversalContext ctx) { + RelNode commonRel = + requireNonNull(parentBuilder.peek(), "parent builder should not be empty"); + if (!isGlobalSource(commonRel)) { + RelOptTable commonTable = new CommonOptTable(commonRel); + commonRel = + new CommonTableScan( + commonRel.getCluster(), commonRel.getTraitSet(), commonTable); + } + nestedBuilder.push(commonRel); + return new GraphBuilderVisitor(nestedBuilder).visitNestedTraversal(ctx).build(); + } + + private boolean isGlobalSource(RelNode rel) { + if (rel instanceof GraphLogicalSource + && ((GraphLogicalSource) rel).getTableConfig().isAll()) { + GraphLogicalSource source = (GraphLogicalSource) rel; + return source.getUniqueKeyFilters() == null && ObjectUtils.isEmpty(source.getFilters()); + } + return false; + } +} diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRexVisitor.java similarity index 52% rename from interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalVisitor.java rename to interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRexVisitor.java index 68a9118868a3..45503fc3189e 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/NestedTraversalRexVisitor.java @@ -16,25 +16,33 @@ package com.alibaba.graphscope.gremlin.antlr4x.visitor; +import com.alibaba.graphscope.common.ir.meta.schema.CommonOptTable; +import com.alibaba.graphscope.common.ir.rel.CommonTableScan; import com.alibaba.graphscope.common.ir.tools.GraphBuilder; import com.alibaba.graphscope.grammar.GremlinGSBaseVisitor; import com.alibaba.graphscope.grammar.GremlinGSParser; +import com.alibaba.graphscope.gremlin.antlr4.GenericLiteralVisitor; import com.alibaba.graphscope.gremlin.exception.UnsupportedEvalException; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import org.antlr.v4.runtime.ParserRuleContext; import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.core.Project; import org.apache.calcite.rex.RexNode; import org.checkerframework.checker.nullness.qual.Nullable; -public class NestedTraversalVisitor extends GremlinGSBaseVisitor { +import java.util.Objects; + +public class NestedTraversalRexVisitor extends GremlinGSBaseVisitor { private final GraphBuilder parentBuilder; private final GraphBuilder nestedBuilder; private final @Nullable String tag; + private final ParserRuleContext parentCtx; - public NestedTraversalVisitor(GraphBuilder parentBuilder, @Nullable String tag) { + public NestedTraversalRexVisitor( + GraphBuilder parentBuilder, @Nullable String tag, ParserRuleContext parentCtx) { this.parentBuilder = parentBuilder; this.nestedBuilder = GraphBuilder.create( @@ -42,12 +50,19 @@ public NestedTraversalVisitor(GraphBuilder parentBuilder, @Nullable String tag) (GraphOptCluster) this.parentBuilder.getCluster(), this.parentBuilder.getRelOptSchema()); this.tag = tag; + this.parentCtx = parentCtx; } @Override public RexNode visitNestedTraversal(GremlinGSParser.NestedTraversalContext ctx) { - Preconditions.checkArgument(parentBuilder.size() > 0, "parent builder should not be empty"); - RelNode commonRel = parentBuilder.peek(); + RelNode commonRel = + Objects.requireNonNull( + parentBuilder.peek(), "parentCtx builder should not be empty"); + commonRel = + new CommonTableScan( + commonRel.getCluster(), + commonRel.getTraitSet(), + new CommonOptTable(commonRel)); nestedBuilder.push(commonRel); if (tag != null) { nestedBuilder.project( @@ -55,18 +70,40 @@ public RexNode visitNestedTraversal(GremlinGSParser.NestedTraversalContext ctx) } GraphBuilderVisitor visitor = new GraphBuilderVisitor(nestedBuilder); RelNode subRel = visitor.visitNestedTraversal(ctx).build(); + String alias = null; + // set query given alias + TraversalMethodIterator iterator = new TraversalMethodIterator(ctx); + while (iterator.hasNext()) { + GremlinGSParser.TraversalMethodContext cur = iterator.next(); + if (cur.traversalMethod_as() != null) { + alias = + GenericLiteralVisitor.getStringLiteral( + cur.traversalMethod_as().stringLiteral()); + } + } + if (alias != null) { + subRel = nestedBuilder.push(subRel).as(null).build(); + } + RexNode expr; if (new SubQueryChecker(commonRel).test(subRel)) { // todo: return sub query in RexSubQuery throw new UnsupportedEvalException( GremlinGSParser.NestedTraversalContext.class, "sub query is unsupported yet"); + } else { + // convert subRel to expression + Project project = (Project) subRel; + Preconditions.checkArgument( + project.getProjects().size() == 1, + "value returned from sub query should be single value"); + expr = project.getProjects().get(0); + if (parentCtx instanceof GremlinGSParser.TraversalMethod_whereContext) { + // convert to IS_NOT_NULL + expr = nestedBuilder.isNotNull(expr); + } else if (parentCtx instanceof GremlinGSParser.TraversalMethod_notContext) { + // convert to IS_NULL + expr = nestedBuilder.isNull(expr); + } } - // convert subRel to expression - Project project = (Project) subRel; - Preconditions.checkArgument( - project.getProjects().size() == 1, - "value returned from sub query should be single value"); - RexNode expr = project.getProjects().get(0); - String alias = project.getRowType().getFieldList().get(0).getName(); return nestedBuilder.alias(expr, alias); } } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/PathExpandBuilderVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/PathExpandBuilderVisitor.java index aec29f295d64..bf40883fceb8 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/PathExpandBuilderVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/PathExpandBuilderVisitor.java @@ -25,14 +25,11 @@ import com.google.common.collect.Lists; import java.util.List; -import java.util.Objects; public class PathExpandBuilderVisitor extends GremlinGSBaseVisitor { - private final GraphBuilderVisitor parent; private final PathExpandConfig.Builder builder; public PathExpandBuilderVisitor(GraphBuilderVisitor parent) { - this.parent = Objects.requireNonNull(parent); // PATH_OPT = ARBITRARY and RESULT_OPT = END_V are set by default this.builder = PathExpandConfig.newBuilder(parent.getGraphBuilder()); } @@ -50,15 +47,10 @@ public PathExpandConfig.Builder visitTraversalMethod_out( List nextWith = getNextWith((GremlinGSParser.TraversalMethodContext) ctx.getParent()); nextWith.forEach(k -> visitTraversalMethod_with(k)); - // set expand config, getV config, range, alias - String alias = - parent.getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); + // set expand config, getV config, range return builder.expand(new ExpandConfig(GraphOpt.Expand.OUT, getExpandLabelConfig(labels))) .getV(new GetVConfig(GraphOpt.GetV.END)) - .range(lower, upper - lower) - .alias(alias); + .range(lower, upper - lower); } @Override @@ -74,15 +66,10 @@ public PathExpandConfig.Builder visitTraversalMethod_in( List nextWith = getNextWith((GremlinGSParser.TraversalMethodContext) ctx.getParent()); nextWith.forEach(k -> visitTraversalMethod_with(k)); - // set expand config, getV config, range, alias - String alias = - parent.getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); + // set expand config, getV config, range return builder.expand(new ExpandConfig(GraphOpt.Expand.IN, getExpandLabelConfig(labels))) .getV(new GetVConfig(GraphOpt.GetV.START)) - .range(lower, upper - lower) - .alias(alias); + .range(lower, upper - lower); } @Override @@ -98,15 +85,10 @@ public PathExpandConfig.Builder visitTraversalMethod_both( List nextWith = getNextWith((GremlinGSParser.TraversalMethodContext) ctx.getParent()); nextWith.forEach(k -> visitTraversalMethod_with(k)); - // set expand config, getV config, range, alias - String alias = - parent.getNextTag( - new TraversalMethodIterator( - (GremlinGSParser.TraversalMethodContext) ctx.getParent())); + // set expand config, getV config, range return builder.expand(new ExpandConfig(GraphOpt.Expand.BOTH, getExpandLabelConfig(labels))) .getV(new GetVConfig(GraphOpt.GetV.OTHER)) - .range(lower, upper - lower) - .alias(alias); + .range(lower, upper - lower); } @Override diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/TraversalMethodIterator.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/TraversalMethodIterator.java index c48a2a5b1e8b..2368a1d741dd 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/TraversalMethodIterator.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/TraversalMethodIterator.java @@ -27,6 +27,7 @@ public class TraversalMethodIterator implements Iterator { private GremlinGSParser.TraversalMethodContext next; private GremlinGSParser.TraversalSourceSpawnMethodContext sourceNext; + private GremlinGSParser.NestedTraversalContext nestedNext; public TraversalMethodIterator(GremlinGSParser.TraversalMethodContext next) { this.next = Objects.requireNonNull(next); @@ -34,15 +35,22 @@ public TraversalMethodIterator(GremlinGSParser.TraversalMethodContext next) { public TraversalMethodIterator(GremlinGSParser.TraversalSourceSpawnMethodContext sourceNext) { this.sourceNext = Objects.requireNonNull(sourceNext); - this.next = null; + } + + public TraversalMethodIterator(GremlinGSParser.NestedTraversalContext nestedNext) { + this.nestedNext = Objects.requireNonNull(nestedNext); } @Override public boolean hasNext() { if (this.next == null) { - GremlinGSParser.RootTraversalContext rootCtx = - (GremlinGSParser.RootTraversalContext) sourceNext.getParent(); - return rootCtx != null && rootCtx.chainedTraversal() != null; + if (sourceNext != null) { + GremlinGSParser.RootTraversalContext rootCtx = + (GremlinGSParser.RootTraversalContext) sourceNext.getParent(); + return rootCtx != null && rootCtx.chainedTraversal() != null; + } else if (nestedNext != null) { + return nestedNext.chainedTraversal() != null; + } } ParseTree parent = getParent(getParent(next)); return parent != null && parent.getChildCount() >= 3; @@ -51,11 +59,17 @@ public boolean hasNext() { @Override public GremlinGSParser.TraversalMethodContext next() { if (this.next == null) { - GremlinGSParser.RootTraversalContext rootCtx = - (GremlinGSParser.RootTraversalContext) sourceNext.getParent(); - if (rootCtx == null || rootCtx.chainedTraversal() == null) return null; - next = leftDfs(rootCtx.chainedTraversal()); - return next; + if (sourceNext != null) { + GremlinGSParser.RootTraversalContext rootCtx = + (GremlinGSParser.RootTraversalContext) sourceNext.getParent(); + if (rootCtx == null || rootCtx.chainedTraversal() == null) return null; + next = leftDfs(rootCtx.chainedTraversal()); + return next; + } else if (nestedNext != null) { + if (nestedNext.chainedTraversal() == null) return null; + next = leftDfs(nestedNext.chainedTraversal()); + return next; + } } ParseTree parent = getParent(getParent(next)); if (parent == null || parent.getChildCount() < 3) return null; diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/WherePredicateVisitor.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/WherePredicateVisitor.java index cd3a84efea50..14a51ad19630 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/WherePredicateVisitor.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/antlr4x/visitor/WherePredicateVisitor.java @@ -201,7 +201,7 @@ private static RexNode visitTraversalMethod_whereby( byCtx.traversalMethod_values().stringLiteral())); } else if (byCtx.nestedTraversal() != null) { return Utils.convertExprToPair( - new NestedTraversalVisitor(builder, tag) + new NestedTraversalRexVisitor(builder, tag, byCtx) .visitNestedTraversal(byCtx.nestedTraversal())) .getValue0(); } diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/graph/calcite/RemoteTestGraph.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/graph/calcite/RemoteTestGraph.java index 23af172497f7..defe21a781b6 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/graph/calcite/RemoteTestGraph.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/graph/calcite/RemoteTestGraph.java @@ -581,10 +581,10 @@ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest", method = "g_V_selectXall_a_bX", reason = "unsupported") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest", - method = "g_V_hasXperson_name_markoX_count_asXaX_unionXidentity_identityX_selectXaX", - reason = "unsupported") +// @Graph.OptOut( +// test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest", +// method = "g_V_hasXperson_name_markoX_count_asXaX_unionXidentity_identityX_selectXaX", +// reason = "unsupported") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SelectTest", method = "g_V_outE_weight_groupCount_selectXvaluesX_unfold", @@ -1519,18 +1519,18 @@ // method = "g_V_valueMapXname_ageX", // reason = "unsupported") -@Graph.OptOut( - method = "g_V_unionXout__inX_name", - test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionTest", - reason = "union is unsupported yet") -@Graph.OptOut( - test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionTest", - method = "g_VX1_2X_unionXoutE_count__inE_count__outE_weight_sumX", - reason = "union is unsupported yet") -@Graph.OptOut( - method = "g_V_haslabel_union_identity_out_values", - test = "com.alibaba.graphscope.gremlin.integration.suite.standard.IrGremlinQueryTest", - reason = "union is unsupported yet") +// @Graph.OptOut( +// method = "g_V_unionXout__inX_name", +// test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionTest", +// reason = "union is unsupported yet") +// @Graph.OptOut( +// test = "org.apache.tinkerpop.gremlin.process.traversal.step.branch.UnionTest", +// method = "g_VX1_2X_unionXoutE_count__inE_count__outE_weight_sumX", +// reason = "union is unsupported yet") +// @Graph.OptOut( +// method = "g_V_haslabel_union_identity_out_values", +// test = "com.alibaba.graphscope.gremlin.integration.suite.standard.IrGremlinQueryTest", +// reason = "union is unsupported yet") @Graph.OptOut( test = "org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasTest", method = "g_V_hasLabelXpersonX_hasLabelXsoftwareX", diff --git a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java index c206dd1ab928..8eaaab41c255 100644 --- a/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java +++ b/interactive_engine/compiler/src/main/java/com/alibaba/graphscope/gremlin/integration/suite/standard/IrGremlinQueryTest.java @@ -124,6 +124,78 @@ public abstract class IrGremlinQueryTest extends AbstractGremlinProcessTest { get_g_VX1X_outEXknowsX_asXhereX_hasXweight_1X_inV_hasXname_joshX_selectXhereX( final Object v1Id); + // g.V().out().union(out(), in(), in()).count() + public abstract Traversal get_g_V_out_union_out_in_in_count(); + + // g.V().union(out(), in().union(out(), out())).count() + public abstract Traversal get_g_V_union_out_inXunion_out_outX_count(); + + // g.V().out().union(out(), in().union(out(), out())).count() + public abstract Traversal get_g_V_out_union_out_inXunion_out_outX_count(); + + // g.V().out().union(out().union(out(), out()), in().union(out(), out())).count() + public abstract Traversal + get_g_V_out_union_outXunion_out_outX_inXunion_out_outX_count(); + + // g.V().union(in().union(out(), out()), in().union(out(), out())).count() + public abstract Traversal + get_g_V_union_inXunion_out_outX_inXunion_out_outX_count(); + + // g.V().out().union(in().union(out(), out()), in().union(out(), out())).count() + public abstract Traversal + get_g_V_out_union_inXunion_out_outX_inXunion_out_outX_count(); + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_out_union_out_in_in_count() { + Traversal traversal = this.get_g_V_out_union_out_in_in_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(26, traversal.next().intValue()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_union_out_inXunion_out_outX_count() { + Traversal traversal = this.get_g_V_union_out_inXunion_out_outX_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(34, traversal.next().intValue()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_out_union_out_inXunion_out_outX_count() { + Traversal traversal = this.get_g_V_out_union_out_inXunion_out_outX_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(54, traversal.next().intValue()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_out_union_outXunion_out_outX_inXunion_out_outX_count() { + Traversal traversal = + this.get_g_V_out_union_outXunion_out_outX_inXunion_out_outX_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(52, traversal.next().intValue()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_union_inXunion_out_outX_inXunion_out_outX_count() { + Traversal traversal = + this.get_g_V_union_inXunion_out_outX_inXunion_out_outX_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(56, traversal.next().intValue()); + } + + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) + @Test + public void g_V_out_union_inXunion_out_outX_inXunion_out_outX_count() { + Traversal traversal = + this.get_g_V_out_union_inXunion_out_outX_inXunion_out_outX_count(); + this.printTraversalForm(traversal); + Assert.assertEquals(104, traversal.next().intValue()); + } + @LoadGraphWith(LoadGraphWith.GraphData.MODERN) @Test public void g_V_group_by_by_dedup_count_test() { @@ -703,6 +775,44 @@ public void g_VX1X_outEXknowsX_asXhereX_hasXweight_1X_inV_hasXname_joshX_selectX public static class Traversals extends IrGremlinQueryTest { + // g.V().out().union(out(), in(), in()).count() + @Override + public Traversal get_g_V_out_union_out_in_in_count() { + return g.V().out().union(out(), in(), in()).count(); + } + + // g.V().union(out(), in().union(out(), out())).count() + @Override + public Traversal get_g_V_union_out_inXunion_out_outX_count() { + return g.V().union(out(), in().union(out(), out())).count(); + } + + // g.V().out().union(out(), in().union(out(), out())).count() + @Override + public Traversal get_g_V_out_union_out_inXunion_out_outX_count() { + return g.V().out().union(out(), in().union(out(), out())).count(); + } + + // g.V().out().union(out().union(out(), out()), in().union(out(), out())).count() + @Override + public Traversal + get_g_V_out_union_outXunion_out_outX_inXunion_out_outX_count() { + return g.V().out().union(out().union(out(), out()), in().union(out(), out())).count(); + } + + // g.V().union(in().union(out(), out()), in().union(out(), out())).count() + @Override + public Traversal get_g_V_union_inXunion_out_outX_inXunion_out_outX_count() { + return g.V().union(in().union(out(), out()), in().union(out(), out())).count(); + } + + // g.V().out().union(in().union(out(), out()), in().union(out(), out())).count() + @Override + public Traversal + get_g_V_out_union_inXunion_out_outX_inXunion_out_outX_count() { + return g.V().out().union(in().union(out(), out()), in().union(out(), out())).count(); + } + @Override public Traversal> get_g_V_group_by_by_dedup_count_order_by_key() { return (IrCustomizedTraversal) diff --git a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/Utils.java b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/Utils.java index ae3cab54da00..7f8ae7d79e8f 100644 --- a/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/Utils.java +++ b/interactive_engine/compiler/src/test/java/com/alibaba/graphscope/common/ir/Utils.java @@ -21,6 +21,7 @@ import com.alibaba.graphscope.common.config.GraphConfig; import com.alibaba.graphscope.common.ir.meta.reader.LocalMetaDataReader; import com.alibaba.graphscope.common.ir.meta.schema.GraphOptSchema; +import com.alibaba.graphscope.common.ir.planner.GraphHepPlanner; import com.alibaba.graphscope.common.ir.tools.GraphBuilder; import com.alibaba.graphscope.common.ir.tools.GraphBuilderFactory; import com.alibaba.graphscope.common.ir.tools.GraphRexBuilder; @@ -32,7 +33,6 @@ import org.apache.calcite.plan.GraphOptCluster; import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.plan.RelRule; -import org.apache.calcite.plan.hep.HepPlanner; import org.apache.calcite.plan.hep.HepProgram; import org.apache.calcite.plan.hep.HepProgramBuilder; import org.apache.calcite.rel.type.RelDataTypeFactory; @@ -77,7 +77,7 @@ public static final RelOptPlanner mockPlanner(RelRule.Config... rules) { ruleConfig.withRelBuilderFactory(relBuilderFactory).toRule()); } } - return new HepPlanner(hepBuilder.build()); + return new GraphHepPlanner(hepBuilder.build()); } private static IrMeta mockSchemaMeta(String schemaJson) { 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 f1071ea5d09c..6f71d6bc40c7 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 @@ -17,6 +17,7 @@ package com.alibaba.graphscope.gremlin.antlr4x; import com.alibaba.graphscope.common.ir.Utils; +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; import com.alibaba.graphscope.common.ir.tools.GraphStdOperatorTable; @@ -31,6 +32,7 @@ import com.google.protobuf.util.JsonFormat; import org.antlr.v4.runtime.tree.ParseTree; +import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; @@ -690,4 +692,314 @@ public void id_in_list_expr_test() throws Exception { FileUtils.readJsonFromResource("proto/id_in_list_expr.json"), JsonFormat.printer().print(exprProto)); } + + @Test + public void g_V_identity() { + // = g.V().as('a'), `identity()` do nothing + RelNode node = eval("g.V().identity().as('a')"); + Assert.assertEquals( + "GraphLogicalProject(a=[a], isAppend=[false])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[a], opt=[VERTEX])", + node.explain().trim()); + } + + @Test + public void g_V_union_out_in_test() { + RelNode node = eval("g.V().union(out(), in())"); + Assert.assertEquals( + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphLogicalGetV(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[END])\n" + + " GraphLogicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])\n" + + " GraphLogicalGetV(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[START])\n" + + " GraphLogicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[IN])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])", + node.explain().trim()); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])", + after.explain().trim()); + } + + @Test + public void g_V_out_union_out_in_test() { + RelNode node = eval("g.V().out().union(out(), in())"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + "common#1035160246:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + @Test + public void g_V_out_union_out_in_inXin_test() { + RelNode node = eval("g.V().out().union(out(), in(), in().in())"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + "common#1035160246:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + @Test + public void g_V_union_out_inXunion_out_outX_test() { + RelNode node = eval("g.V().union(out(), in().union(out(), out()))"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + "common#1107010235:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + @Test + public void g_V_out_union_out_inXunion_out_outX_test() { + RelNode node = eval("g.V().out().union(out(), in().union(out(), out()))"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + "common#1035160246:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])\n" + + "common#1345574839:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + @Test + public void g_V_out_union_outXunion_out_outX_inXunion_out_outX_test() { + RelNode node = + eval("g.V().out().union(out().union(out(), out()), in().union(out(), out()))"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#-1243454926]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#-1243454926]])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + "common#-1243454926:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + "common#1035160246:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])\n" + + "common#1345574839:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + // `g.V().in()` is extracted as the common sub-plan of the root union + @Test + public void g_V_union_inXunion_out_outX_inXunion_out_outX_test() { + RelNode node = eval("g.V().union(in().union(out(), out()), in().union(out(), out()))"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1107010235]])\n" + + "common#1107010235:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + // `g.V().out().in()` is extracted as the common sub-plan of the root union + @Test + public void g_V_out_union_inXunion_out_outX_inXunion_out_outX_test() { + RelNode node = + eval("g.V().out().union(in().union(out(), out()), in().union(out(), out()))"); + RelOptPlanner planner = + Utils.mockPlanner(ExpandGetVFusionRule.BasicExpandGetVFusionRule.Config.DEFAULT); + planner.setRoot(node); + RelNode after = planner.findBestExp(); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + " LogicalUnion(all=[true])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + " GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created," + + " knows]}], alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1345574839]])\n" + + "common#1345574839:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[IN], physicalOpt=[VERTEX])\n" + + " CommonTableScan(table=[[common#1035160246]])\n" + + "common#1035160246:\n" + + "GraphPhysicalExpand(tableConfig=[{isAll=true, tables=[created, knows]}]," + + " alias=[DEFAULT], opt=[OUT], physicalOpt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(after).trim()); + } + + @Test + public void g_V_union_identity_test() { + RelNode node = eval("g.V().union(identity(), identity())"); + Assert.assertEquals( + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])\n" + + " GraphLogicalSource(tableConfig=[{isAll=true, tables=[software," + + " person]}], alias=[DEFAULT], opt=[VERTEX])", + node.explain().trim()); + } + + @Test + public void g_V_has_union_identity_identity_test() { + RelNode node = eval("g.V().has('name', 'marko').union(identity(), identity())"); + Assert.assertEquals( + "root:\n" + + "GraphLogicalProject(DEFAULT=[DEFAULT], isAppend=[false])\n" + + " LogicalUnion(all=[true])\n" + + " CommonTableScan(table=[[common#318359437]])\n" + + " CommonTableScan(table=[[common#318359437]])\n" + + "common#318359437:\n" + + "GraphLogicalSource(tableConfig=[{isAll=true, tables=[software, person]}]," + + " alias=[DEFAULT], fusedFilter=[[=(DEFAULT.name, _UTF-8'marko')]]," + + " opt=[VERTEX])", + com.alibaba.graphscope.common.ir.tools.Utils.toString(node).trim()); + } }