diff --git a/src/graph/context/Iterator.cpp b/src/graph/context/Iterator.cpp index 80f4f7d1845..637caa61712 100644 --- a/src/graph/context/Iterator.cpp +++ b/src/graph/context/Iterator.cpp @@ -440,7 +440,7 @@ Value GetNeighborsIter::getVertex(const std::string& name) const { return Value::kNullValue; } - auto vidVal = getColumn(nebula::kVid); + auto vidVal = getColumn(0); if (UNLIKELY(!SchemaUtil::isValidVid(vidVal))) { return Value::kNullBadType; } @@ -502,12 +502,13 @@ Value GetNeighborsIter::getEdge() const { edge.name = edgeName; auto type = getEdgeProp(edgeName, kType); - if (!type.isInt()) { - return Value::kNullBadType; + if (type.isInt()) { + edge.type = type.getInt(); + } else { + edge.type = 0; } - edge.type = type.getInt(); - auto& srcVal = getColumn(kVid); + auto& srcVal = getColumn(0); if (!SchemaUtil::isValidVid(srcVal)) { return Value::kNullBadType; } @@ -520,10 +521,11 @@ Value GetNeighborsIter::getEdge() const { edge.dst = dstVal; auto& rank = getEdgeProp(edgeName, kRank); - if (!rank.isInt()) { - return Value::kNullBadType; + if (rank.isInt()) { + edge.ranking = rank.getInt(); + } else { + edge.ranking = 0; } - edge.ranking = rank.getInt(); auto& edgePropMap = currentDs_->edgePropsMap; auto edgeProp = edgePropMap.find(currentEdgeName()); @@ -535,7 +537,7 @@ Value GetNeighborsIter::getEdge() const { DCHECK_EQ(edgeNamePropList.size(), propList.size()); for (size_t i = 0; i < propList.size(); ++i) { auto propName = edgeNamePropList[i]; - if (propName == kSrc || propName == kDst || propName == kRank || propName == kType) { + if (propName == kDst || propName == kRank || propName == kType || propName == kSrc) { continue; } edge.props.emplace(edgeNamePropList[i], propList[i]); diff --git a/src/graph/executor/query/AppendVerticesExecutor.cpp b/src/graph/executor/query/AppendVerticesExecutor.cpp index 509e5cb4c2a..d4fe327e763 100644 --- a/src/graph/executor/query/AppendVerticesExecutor.cpp +++ b/src/graph/executor/query/AppendVerticesExecutor.cpp @@ -9,7 +9,7 @@ using nebula::storage::StorageClient; using nebula::storage::StorageRpcResponse; using nebula::storage::cpp2::GetPropResponse; - +DECLARE_bool(optimize_appendvertices); namespace nebula { namespace graph { folly::Future AppendVerticesExecutor::execute() { @@ -26,10 +26,12 @@ StatusOr AppendVerticesExecutor::buildRequestDataSet(const AppendVertic folly::Future AppendVerticesExecutor::appendVertices() { SCOPED_TIMER(&execTime_); - auto *av = asNode(node()); - StorageClient *storageClient = qctx()->getStorageClient(); + if (FLAGS_optimize_appendvertices && av != nullptr && av->props() == nullptr) { + return handleNullProp(av); + } + StorageClient *storageClient = qctx()->getStorageClient(); auto res = buildRequestDataSet(av); NG_RETURN_IF_ERROR(res); auto vertices = std::move(res).value(); @@ -69,6 +71,39 @@ folly::Future AppendVerticesExecutor::appendVertices() { }); } +Status AppendVerticesExecutor::handleNullProp(const AppendVertices *av) { + auto iter = ectx_->getResult(av->inputVar()).iter(); + auto *src = av->src(); + + auto size = iter->size(); + DataSet ds; + ds.colNames = av->colNames(); + ds.rows.reserve(size); + + QueryExpressionContext ctx(ectx_); + bool canBeMoved = movable(av->inputVars().front()); + + for (; iter->valid(); iter->next()) { + const auto &vid = src->eval(ctx(iter.get())); + if (vid.empty()) { + continue; + } + Vertex vertex; + vertex.vid = vid; + if (!av->trackPrevPath()) { + Row row; + row.values.emplace_back(std::move(vertex)); + ds.rows.emplace_back(std::move(row)); + } else { + Row row; + row = canBeMoved ? iter->moveRow() : *iter->row(); + row.values.emplace_back(std::move(vertex)); + ds.rows.emplace_back(std::move(row)); + } + } + return finish(ResultBuilder().value(Value(std::move(ds))).build()); +} + Status AppendVerticesExecutor::handleResp( storage::StorageRpcResponse &&rpcResp) { auto result = handleCompleteness(rpcResp, FLAGS_accept_partial_success); diff --git a/src/graph/executor/query/AppendVerticesExecutor.h b/src/graph/executor/query/AppendVerticesExecutor.h index 7138fdb6a08..88ce56870ac 100644 --- a/src/graph/executor/query/AppendVerticesExecutor.h +++ b/src/graph/executor/query/AppendVerticesExecutor.h @@ -28,6 +28,8 @@ class AppendVerticesExecutor final : public GetPropExecutor { Status handleResp(storage::StorageRpcResponse &&rpcResp); + Status handleNullProp(const AppendVertices *av); + folly::Future handleRespMultiJobs( storage::StorageRpcResponse &&rpcResp); diff --git a/src/graph/planner/match/MatchSolver.cpp b/src/graph/planner/match/MatchSolver.cpp index b1c5b95c6f1..1aa4e66ba6e 100644 --- a/src/graph/planner/match/MatchSolver.cpp +++ b/src/graph/planner/match/MatchSolver.cpp @@ -213,62 +213,6 @@ Expression* MatchSolver::makeIndexFilter(const std::string& label, return root; } -void MatchSolver::extractAndDedupVidColumn(QueryContext* qctx, - Expression** initialExpr, - PlanNode* dep, - const std::string& inputVar, - SubPlan& plan) { - auto columns = qctx->objPool()->makeAndAdd(); - auto* var = qctx->symTable()->getVar(inputVar); - Expression* vidExpr = initialExprOrEdgeDstExpr(qctx, initialExpr, var->colNames.back()); - if (initialExpr) { - *initialExpr = nullptr; - } - columns->addColumn(new YieldColumn(vidExpr)); - auto project = Project::make(qctx, dep, columns); - project->setInputVar(inputVar); - project->setColNames({kVid}); - auto dedup = Dedup::make(qctx, project); - dedup->setColNames({kVid}); - - plan.root = dedup; -} - -Expression* MatchSolver::initialExprOrEdgeDstExpr(QueryContext* qctx, - Expression** initialExpr, - const std::string& vidCol) { - if (initialExpr != nullptr && *initialExpr != nullptr) { - return *initialExpr; - } else { - return getEndVidInPath(qctx, vidCol); - } -} - -Expression* MatchSolver::getEndVidInPath(QueryContext* qctx, const std::string& colName) { - auto* pool = qctx->objPool(); - // expr: __Project_2[-1] => path - auto columnExpr = InputPropertyExpression::make(pool, colName); - // expr: endNode(path) => vn - auto args = ArgumentList::make(pool); - args->addArgument(columnExpr); - auto endNode = FunctionCallExpression::make(pool, "endNode", args); - // expr: en[_dst] => dst vid - auto vidExpr = ConstantExpression::make(pool, kVid); - return AttributeExpression::make(pool, endNode, vidExpr); -} - -Expression* MatchSolver::getStartVidInPath(QueryContext* qctx, const std::string& colName) { - auto* pool = qctx->objPool(); - // expr: __Project_2[0] => path - auto columnExpr = InputPropertyExpression::make(pool, colName); - // expr: startNode(path) => v1 - auto args = ArgumentList::make(pool); - args->addArgument(columnExpr); - auto firstVertexExpr = FunctionCallExpression::make(pool, "startNode", args); - // expr: v1[_vid] => vid - return AttributeExpression::make(pool, firstVertexExpr, ConstantExpression::make(pool, kVid)); -} - PlanNode* MatchSolver::filtPathHasSameEdge(PlanNode* input, const std::string& column, QueryContext* qctx) { @@ -282,45 +226,6 @@ PlanNode* MatchSolver::filtPathHasSameEdge(PlanNode* input, return filter; } -Status MatchSolver::appendFetchVertexPlan(const Expression* nodeFilter, - const SpaceInfo& space, - QueryContext* qctx, - Expression** initialExpr, - SubPlan& plan) { - return appendFetchVertexPlan(nodeFilter, space, qctx, initialExpr, plan.root->outputVar(), plan); -} - -Status MatchSolver::appendFetchVertexPlan(const Expression* nodeFilter, - const SpaceInfo& space, - QueryContext* qctx, - Expression** initialExpr, - std::string inputVar, - SubPlan& plan) { - auto* pool = qctx->objPool(); - // [Project && Dedup] - extractAndDedupVidColumn(qctx, initialExpr, plan.root, inputVar, plan); - auto srcExpr = InputPropertyExpression::make(pool, kVid); - // [Get vertices] - auto props = SchemaUtil::getAllVertexProp(qctx, space.id, true); - NG_RETURN_IF_ERROR(props); - auto gv = GetVertices::make(qctx, plan.root, space.id, srcExpr, std::move(props).value(), {}); - - PlanNode* root = gv; - if (nodeFilter != nullptr) { - auto* newFilter = MatchSolver::rewriteLabel2Vertex(qctx, nodeFilter); - root = Filter::make(qctx, root, newFilter); - } - - // Normalize all columns to one - auto columns = pool->makeAndAdd(); - auto pathExpr = PathBuildExpression::make(pool); - pathExpr->add(VertexExpression::make(pool)); - columns->addColumn(new YieldColumn(pathExpr)); - plan.root = Project::make(qctx, root, columns); - plan.root->setColNames({kPathStr}); - return Status::OK(); -} - static YieldColumn* buildVertexColumn(ObjectPool* pool, const std::string& alias) { return new YieldColumn(InputPropertyExpression::make(pool, alias), alias); } diff --git a/src/graph/planner/match/MatchSolver.h b/src/graph/planner/match/MatchSolver.h index 4db5cf86b23..4a619e77c04 100644 --- a/src/graph/planner/match/MatchSolver.h +++ b/src/graph/planner/match/MatchSolver.h @@ -44,39 +44,10 @@ class MatchSolver final { QueryContext* qctx, bool isEdgeProperties = false); - static void extractAndDedupVidColumn(QueryContext* qctx, - Expression** initialExpr, - PlanNode* dep, - const std::string& inputVar, - SubPlan& plan); - - static Expression* initialExprOrEdgeDstExpr(QueryContext* qctx, - Expression** initialExpr, - const std::string& vidCol); - - static Expression* getEndVidInPath(QueryContext* qctx, const std::string& colName); - - static Expression* getStartVidInPath(QueryContext* qctx, const std::string& colName); - static PlanNode* filtPathHasSameEdge(PlanNode* input, const std::string& column, QueryContext* qctx); - static Status appendFetchVertexPlan(const Expression* nodeFilter, - const SpaceInfo& space, - QueryContext* qctx, - Expression** initialExpr, - SubPlan& plan); - - // In 0 step left expansion case, the result of initial index scan - // will be passed as inputVar after right expansion is finished - static Status appendFetchVertexPlan(const Expression* nodeFilter, - const SpaceInfo& space, - QueryContext* qctx, - Expression** initialExpr, - std::string inputVar, - SubPlan& plan); - // Build yield columns for match & shortestPath statement static void buildProjectColumns(QueryContext* qctx, Path& path, SubPlan& plan); }; diff --git a/src/graph/planner/ngql/PathPlanner.cpp b/src/graph/planner/ngql/PathPlanner.cpp index 8b20c266f3b..639c0257eec 100644 --- a/src/graph/planner/ngql/PathPlanner.cpp +++ b/src/graph/planner/ngql/PathPlanner.cpp @@ -322,11 +322,12 @@ PlanNode* PathPlanner::buildVertexPlan(PlanNode* dep, const std::string& input) // col 0 of the project->output is [node...] auto* unwindExpr = ColumnExpression::make(pool, 0); auto* unwind = Unwind::make(qctx, project, unwindExpr); + unwind->setFromPipe(true); unwind->setColNames({"nodes"}); // extract vid from vertex, col 0 is vertex auto idArgs = ArgumentList::make(pool); - idArgs->addArgument(ColumnExpression::make(pool, 1)); + idArgs->addArgument(ColumnExpression::make(pool, 0)); auto* src = FunctionCallExpression::make(pool, "id", idArgs); // get all vertexprop auto vertexProp = SchemaUtil::getAllVertexProp(qctx, pathCtx_->space.id, true); @@ -355,23 +356,24 @@ PlanNode* PathPlanner::buildEdgePlan(PlanNode* dep, const std::string& input) { // col 0 of the project->output() is [edge...] auto* unwindExpr = ColumnExpression::make(pool, 0); auto* unwind = Unwind::make(qctx, project, unwindExpr); + unwind->setFromPipe(true); unwind->setColNames({"edges"}); // extract src from edge auto srcArgs = ArgumentList::make(pool); - srcArgs->addArgument(ColumnExpression::make(pool, 1)); + srcArgs->addArgument(ColumnExpression::make(pool, 0)); auto* src = FunctionCallExpression::make(pool, "src", srcArgs); // extract dst from edge auto dstArgs = ArgumentList::make(pool); - dstArgs->addArgument(ColumnExpression::make(pool, 1)); + dstArgs->addArgument(ColumnExpression::make(pool, 0)); auto* dst = FunctionCallExpression::make(pool, "dst", dstArgs); // extract rank from edge auto rankArgs = ArgumentList::make(pool); - rankArgs->addArgument(ColumnExpression::make(pool, 1)); + rankArgs->addArgument(ColumnExpression::make(pool, 0)); auto* rank = FunctionCallExpression::make(pool, "rank", rankArgs); // type auto typeArgs = ArgumentList::make(pool); - typeArgs->addArgument(ColumnExpression::make(pool, 1)); + typeArgs->addArgument(ColumnExpression::make(pool, 0)); auto* type = FunctionCallExpression::make(pool, "typeid", typeArgs); // prepare edgetype auto edgeProp = SchemaUtil::getEdgeProps(qctx, pathCtx_->space, pathCtx_->over.edgeTypes, true); diff --git a/src/graph/planner/plan/Algo.cpp b/src/graph/planner/plan/Algo.cpp index 6007dadab21..1f42394f49a 100644 --- a/src/graph/planner/plan/Algo.cpp +++ b/src/graph/planner/plan/Algo.cpp @@ -6,6 +6,7 @@ #include "graph/planner/plan/Algo.h" #include "PlanNode.h" +#include "graph/planner/plan/PlanNodeVisitor.h" #include "graph/util/ToJson.h" namespace nebula { namespace graph { @@ -125,6 +126,10 @@ BiCartesianProduct::BiCartesianProduct(QueryContext* qctx, PlanNode* left, PlanN setColNames(lColNames); } +void BiCartesianProduct::accept(PlanNodeVisitor* visitor) { + visitor->visit(this); +} + BiCartesianProduct::BiCartesianProduct(QueryContext* qctx) : BinaryInputNode(qctx, Kind::kBiCartesianProduct) {} diff --git a/src/graph/planner/plan/Algo.h b/src/graph/planner/plan/Algo.h index 43b0caa2505..513e49259a1 100644 --- a/src/graph/planner/plan/Algo.h +++ b/src/graph/planner/plan/Algo.h @@ -313,6 +313,8 @@ class BiCartesianProduct final : public BinaryInputNode { PlanNode* clone() const override; + void accept(PlanNodeVisitor* visitor) override; + private: friend ObjectPool; diff --git a/src/graph/planner/plan/PlanNodeVisitor.h b/src/graph/planner/plan/PlanNodeVisitor.h index eb8b6d8d0da..a9842205d45 100644 --- a/src/graph/planner/plan/PlanNodeVisitor.h +++ b/src/graph/planner/plan/PlanNodeVisitor.h @@ -6,6 +6,7 @@ #ifndef PLAN_PLANNODEVISITOR_H_ #define PLAN_PLANNODEVISITOR_H_ +#include "graph/planner/plan/Algo.h" #include "graph/planner/plan/PlanNode.h" #include "graph/planner/plan/Query.h" @@ -23,6 +24,9 @@ class PlanNodeVisitor { virtual void visit(Traverse *node) = 0; virtual void visit(AppendVertices *node) = 0; virtual void visit(BiJoin *node) = 0; + virtual void visit(Union *node) = 0; + virtual void visit(Unwind *node) = 0; + virtual void visit(BiCartesianProduct *node) = 0; }; } // namespace graph diff --git a/src/graph/planner/plan/Query.cpp b/src/graph/planner/plan/Query.cpp index 772740d7ab6..9965f9b745b 100644 --- a/src/graph/planner/plan/Query.cpp +++ b/src/graph/planner/plan/Query.cpp @@ -321,6 +321,10 @@ void Union::cloneMembers(const Union& f) { SetOp::cloneMembers(f); } +void Union::accept(PlanNodeVisitor* visitor) { + visitor->visit(this); +} + PlanNode* Intersect::clone() const { auto* newIntersect = Intersect::make(qctx_, nullptr, nullptr); newIntersect->cloneMembers(*this); @@ -401,6 +405,10 @@ void Unwind::cloneMembers(const Unwind& p) { alias_ = p.alias(); } +void Unwind::accept(PlanNodeVisitor* visitor) { + visitor->visit(this); +} + std::unique_ptr Sort::explain() const { auto desc = SingleInputNode::explain(); addDescription("factors", folly::toJson(util::toJson(factorsString())), desc.get()); diff --git a/src/graph/planner/plan/Query.h b/src/graph/planner/plan/Query.h index 4e219d4ea88..c4d7a817d0d 100644 --- a/src/graph/planner/plan/Query.h +++ b/src/graph/planner/plan/Query.h @@ -802,6 +802,8 @@ class Union final : public SetOp { return qctx->objPool()->makeAndAdd(qctx, left, right); } + void accept(PlanNodeVisitor* visitor) override; + PlanNode* clone() const override; private: @@ -886,7 +888,7 @@ class Unwind final : public SingleInputNode { return unwindExpr_; } - const std::string alias() const { + const std::string& alias() const { return alias_; } @@ -901,6 +903,8 @@ class Unwind final : public SingleInputNode { PlanNode* clone() const override; std::unique_ptr explain() const override; + void accept(PlanNodeVisitor* visitor) override; + private: friend ObjectPool; Unwind(QueryContext* qctx, PlanNode* input, Expression* unwindExpr, std::string alias) diff --git a/src/graph/service/GraphFlags.cpp b/src/graph/service/GraphFlags.cpp index 9029b393e94..be60913a10e 100644 --- a/src/graph/service/GraphFlags.cpp +++ b/src/graph/service/GraphFlags.cpp @@ -76,6 +76,8 @@ DEFINE_int32(max_sessions_per_ip_per_user, 300, "Maximum number of sessions that can be created per IP and per user"); +DEFINE_bool(optimize_appendvertices, false, "if true, return directly without go through RPC"); + // Sanity-checking Flag Values static bool ValidateSessIdleTimeout(const char* flagname, int32_t value) { // The max timeout is 604800 seconds(a week) diff --git a/src/graph/service/GraphFlags.h b/src/graph/service/GraphFlags.h index 480f97dff1a..f2c7ecf9c75 100644 --- a/src/graph/service/GraphFlags.h +++ b/src/graph/service/GraphFlags.h @@ -49,6 +49,7 @@ DECLARE_uint32(password_lock_time_in_secs); // Optimizer DECLARE_bool(enable_optimizer); +DECLARE_bool(optimize_appendvertice); DECLARE_int64(max_allowed_connections); diff --git a/src/graph/visitor/PropertyTrackerVisitor.cpp b/src/graph/visitor/PropertyTrackerVisitor.cpp index f12871829b9..755d0399497 100644 --- a/src/graph/visitor/PropertyTrackerVisitor.cpp +++ b/src/graph/visitor/PropertyTrackerVisitor.cpp @@ -13,6 +13,51 @@ namespace nebula { namespace graph { +void PropertyTracker::insertVertexProp(const std::string &name, + TagID tagId, + const std::string &propName) { + if (colsSet.find(name) != colsSet.end()) { + return; + } + auto iter = vertexPropsMap.find(name); + if (iter == vertexPropsMap.end()) { + vertexPropsMap[name][tagId].emplace(propName); + } else { + auto propIter = iter->second.find(tagId); + if (propIter == iter->second.end()) { + std::unordered_set temp({propName}); + iter->second.emplace(tagId, std::move(temp)); + } else { + propIter->second.emplace(propName); + } + } +} + +void PropertyTracker::insertEdgeProp(const std::string &name, + EdgeType type, + const std::string &propName) { + if (colsSet.find(name) != colsSet.end()) { + return; + } + auto iter = edgePropsMap.find(name); + if (iter == edgePropsMap.end()) { + edgePropsMap[name][type].emplace(propName); + } else { + auto propIter = iter->second.find(type); + if (propIter == iter->second.end()) { + std::unordered_set temp({propName}); + iter->second.emplace(type, std::move(temp)); + } else { + propIter->second.emplace(propName); + } + } +} + +void PropertyTracker::insertCols(const std::string &name) { + colsSet.emplace(name); + vertexPropsMap.erase(name); + edgePropsMap.erase(name); +} Status PropertyTracker::update(const std::string &oldName, const std::string &newName) { if (oldName == newName) { @@ -32,6 +77,7 @@ Status PropertyTracker::update(const std::string &oldName, const std::string &ne } vertexPropsMap[newName] = std::move(it1->second); vertexPropsMap.erase(it1); + colsSet.erase(oldName); } if (hasEdgeAlias) { if (edgePropsMap.find(newName) != edgePropsMap.end()) { @@ -39,12 +85,13 @@ Status PropertyTracker::update(const std::string &oldName, const std::string &ne } edgePropsMap[newName] = std::move(it2->second); edgePropsMap.erase(it2); + colsSet.erase(oldName); } auto it3 = colsSet.find(oldName); if (it3 != colsSet.end()) { colsSet.erase(it3); - colsSet.insert(newName); + insertCols(newName); } return Status::OK(); @@ -72,7 +119,7 @@ void PropertyTrackerVisitor::visit(TagPropertyExpression *expr) { return; } auto tagId = ret.value(); - propsUsed_.vertexPropsMap[entityAlias_][tagId].emplace(propName); + propsUsed_.insertVertexProp(entityAlias_, tagId, propName); } void PropertyTrackerVisitor::visit(EdgePropertyExpression *expr) { @@ -84,7 +131,7 @@ void PropertyTrackerVisitor::visit(EdgePropertyExpression *expr) { return; } auto edgeType = ret.value(); - propsUsed_.edgePropsMap[entityAlias_][edgeType].emplace(propName); + propsUsed_.insertEdgeProp(entityAlias_, edgeType, propName); } void PropertyTrackerVisitor::visit(LabelTagPropertyExpression *expr) { @@ -102,17 +149,17 @@ void PropertyTrackerVisitor::visit(LabelTagPropertyExpression *expr) { return; } auto tagId = ret.value(); - propsUsed_.vertexPropsMap[nodeAlias][tagId].emplace(propName); + propsUsed_.insertVertexProp(nodeAlias, tagId, propName); } void PropertyTrackerVisitor::visit(InputPropertyExpression *expr) { auto &colName = expr->prop(); - propsUsed_.colsSet.emplace(colName); + propsUsed_.insertCols(colName); } void PropertyTrackerVisitor::visit(VariablePropertyExpression *expr) { auto &colName = expr->prop(); - propsUsed_.colsSet.emplace(colName); + propsUsed_.insertCols(colName); } void PropertyTrackerVisitor::visit(AttributeExpression *expr) { @@ -129,16 +176,11 @@ void PropertyTrackerVisitor::visit(AttributeExpression *expr) { auto &propName = constVal.getStr(); static const int kUnknownEdgeType = 0; switch (lhs->kind()) { + case Expression::Kind::kInputProperty: case Expression::Kind::kVarProperty: { // $e.name - auto *varPropExpr = static_cast(lhs); + auto *varPropExpr = static_cast(lhs); auto &edgeAlias = varPropExpr->prop(); - propsUsed_.edgePropsMap[edgeAlias][kUnknownEdgeType].emplace(propName); - break; - } - case Expression::Kind::kInputProperty: { - auto *inputPropExpr = static_cast(lhs); - auto &edgeAlias = inputPropExpr->prop(); - propsUsed_.edgePropsMap[edgeAlias][kUnknownEdgeType].emplace(propName); + propsUsed_.insertEdgeProp(edgeAlias, kUnknownEdgeType, propName); break; } case Expression::Kind::kSubscript: { // $-.e[0].name @@ -147,8 +189,13 @@ void PropertyTrackerVisitor::visit(AttributeExpression *expr) { if (subLeftExpr->kind() == Expression::Kind::kInputProperty) { auto *inputPropExpr = static_cast(subLeftExpr); auto &edgeAlias = inputPropExpr->prop(); - propsUsed_.edgePropsMap[edgeAlias][kUnknownEdgeType].emplace(propName); + propsUsed_.insertEdgeProp(edgeAlias, kUnknownEdgeType, propName); } + break; + } + case Expression::Kind::kFunctionCall: { // properties(t3).name + // TODO(jmq) determine whether it is a vertex or edge + break; } default: break; @@ -156,32 +203,12 @@ void PropertyTrackerVisitor::visit(AttributeExpression *expr) { } void PropertyTrackerVisitor::visit(FunctionCallExpression *expr) { - static const std::unordered_set kVertexIgnoreFuncs = {"id"}; - static const std::unordered_set kEdgeIgnoreFuncs = { - "src", "dst", "type", "typeid", "rank"}; + static const std::unordered_set ignoreFuncs = { + "src", "dst", "type", "typeid", "id", "rank", "length"}; auto funName = expr->name(); - if (kVertexIgnoreFuncs.find(funName) != kVertexIgnoreFuncs.end()) { - DCHECK_EQ(expr->args()->numArgs(), 1); - auto argExpr = expr->args()->args()[0]; - auto nodeAlias = extractColNameFromInputPropOrVarPropExpr(argExpr); - if (!nodeAlias.empty()) { - auto it = propsUsed_.vertexPropsMap.find(nodeAlias); - if (it == propsUsed_.vertexPropsMap.end()) { - propsUsed_.vertexPropsMap[nodeAlias] = {}; - } - } - return; - } else if (kEdgeIgnoreFuncs.find(funName) != kEdgeIgnoreFuncs.end()) { - DCHECK_EQ(expr->args()->numArgs(), 1); - auto argExpr = expr->args()->args()[0]; - auto edgeAlias = extractColNameFromInputPropOrVarPropExpr(argExpr); - if (!edgeAlias.empty()) { - auto it = propsUsed_.edgePropsMap.find(edgeAlias); - if (it == propsUsed_.edgePropsMap.end()) { - propsUsed_.edgePropsMap[edgeAlias] = {}; - } - } + std::transform(funName.begin(), funName.end(), funName.begin(), ::tolower); + if (ignoreFuncs.find(funName) != ignoreFuncs.end()) { return; } @@ -193,6 +220,20 @@ void PropertyTrackerVisitor::visit(FunctionCallExpression *expr) { } } +void PropertyTrackerVisitor::visit(AggregateExpression *expr) { + auto funName = expr->name(); + std::transform(funName.begin(), funName.end(), funName.begin(), ::tolower); + if (funName == "count") { + auto kind = expr->arg()->kind(); + if (kind == Expression::Kind::kConstant || kind == Expression::Kind::kInputProperty || + kind == Expression::Kind::kVarProperty) { + return; + } + } + // count(v.player.age) + expr->arg()->accept(this); +} + void PropertyTrackerVisitor::visit(DestPropertyExpression *expr) { UNUSED(expr); } @@ -255,12 +296,10 @@ void PropertyTrackerVisitor::visit(EdgeExpression *expr) { std::string PropertyTrackerVisitor::extractColNameFromInputPropOrVarPropExpr( const Expression *expr) { - if (expr->kind() == Expression::Kind::kInputProperty) { - auto *inputPropExpr = static_cast(expr); - return inputPropExpr->prop(); - } else if (expr->kind() == Expression::Kind::kVarProperty) { - auto *varPropExpr = static_cast(expr); - return varPropExpr->prop(); + if (expr->kind() == Expression::Kind::kInputProperty || + expr->kind() == Expression::Kind::kVarProperty) { + auto *propExpr = static_cast(expr); + return propExpr->prop(); } return ""; } diff --git a/src/graph/visitor/PropertyTrackerVisitor.h b/src/graph/visitor/PropertyTrackerVisitor.h index e8750391656..ef4868cf109 100644 --- a/src/graph/visitor/PropertyTrackerVisitor.h +++ b/src/graph/visitor/PropertyTrackerVisitor.h @@ -27,6 +27,9 @@ struct PropertyTracker { Status update(const std::string& oldName, const std::string& newName); bool hasAlias(const std::string& name) const; + void insertVertexProp(const std::string& name, TagID tagId, const std::string& propName); + void insertEdgeProp(const std::string& name, EdgeType type, const std::string& propName); + void insertCols(const std::string& name); }; class PropertyTrackerVisitor : public ExprVisitorImpl { @@ -69,6 +72,7 @@ class PropertyTrackerVisitor : public ExprVisitorImpl { void visit(VertexExpression* expr) override; void visit(EdgeExpression* expr) override; void visit(ColumnExpression* expr) override; + void visit(AggregateExpression* expr) override; std::string extractColNameFromInputPropOrVarPropExpr(const Expression* expr); diff --git a/src/graph/visitor/PrunePropertiesVisitor.cpp b/src/graph/visitor/PrunePropertiesVisitor.cpp index bd50bd7b9ac..5a14a8c937d 100644 --- a/src/graph/visitor/PrunePropertiesVisitor.cpp +++ b/src/graph/visitor/PrunePropertiesVisitor.cpp @@ -4,7 +4,7 @@ */ #include "graph/visitor/PrunePropertiesVisitor.h" - +DECLARE_bool(optimize_appendvertices); namespace nebula { namespace graph { @@ -40,45 +40,55 @@ void PrunePropertiesVisitor::visit(Project *node) { void PrunePropertiesVisitor::visitCurrent(Project *node) { // TODO won't use properties of not-root Project - // bool used = used_; - used_ = false; - if (node->columns()) { - const auto &columns = node->columns()->columns(); - auto &colNames = node->colNames(); - std::vector aliasExists(colNames.size(), false); - for (size_t i = 0; i < columns.size(); ++i) { - aliasExists[i] = propsUsed_.hasAlias(colNames[i]); - } - for (size_t i = 0; i < columns.size(); ++i) { + if (!node->columns()) { + return; + } + const auto &columns = node->columns()->columns(); + auto &colNames = node->colNames(); + if (rootNode_) { + for (auto i = 0u; i < columns.size(); ++i) { auto *col = DCHECK_NOTNULL(columns[i]); auto *expr = col->expr(); - auto &alias = colNames[i]; - // If the alias exists, try to rename alias - if (aliasExists[i]) { - if (expr->kind() == Expression::Kind::kInputProperty) { - auto *inputPropExpr = static_cast(expr); - auto &newAlias = inputPropExpr->prop(); - status_ = propsUsed_.update(alias, newAlias); - if (!status_.ok()) { - return; - } - } else if (expr->kind() == Expression::Kind::kVarProperty) { - auto *varPropExpr = static_cast(expr); - auto &newAlias = varPropExpr->prop(); + status_ = extractPropsFromExpr(expr); + if (!status_.ok()) { + return; + } + } + rootNode_ = false; + return; + } + for (auto i = 0u; i < columns.size(); ++i) { + auto *col = DCHECK_NOTNULL(columns[i]); + auto *expr = col->expr(); + auto &alias = colNames[i]; + switch (expr->kind()) { + case Expression::Kind::kVarProperty: + case Expression::Kind::kInputProperty: { + if (propsUsed_.hasAlias(alias)) { + auto *propExpr = static_cast(expr); + auto &newAlias = propExpr->prop(); status_ = propsUsed_.update(alias, newAlias); if (!status_.ok()) { return; } - } else { // eg. "PathBuild[$-.x,$-.__VAR_0,$-.y] AS p" - // How to handle this case? - propsUsed_.colsSet.erase(alias); + } + break; + } + // $-.e[0] as e + // [e IN $-.e WHERE is_edge($e)] AS e + // PathBuild[$-.v,$-.e,$-.v2] AS p + case Expression::Kind::kSubscript: + case Expression::Kind::kPathBuild: + case Expression::Kind::kListComprehension: { + if (propsUsed_.hasAlias(alias)) { status_ = extractPropsFromExpr(expr); if (!status_.ok()) { return; } } - } else { - // Otherwise, extract properties from the column expression + break; + } + default: { status_ = extractPropsFromExpr(expr); if (!status_.ok()) { return; @@ -89,18 +99,29 @@ void PrunePropertiesVisitor::visitCurrent(Project *node) { } void PrunePropertiesVisitor::visit(Aggregate *node) { + rootNode_ = false; visitCurrent(node); status_ = depsPruneProperties(node->dependencies()); } void PrunePropertiesVisitor::visitCurrent(Aggregate *node) { for (auto *groupKey : node->groupKeys()) { + if (groupKey->kind() == Expression::Kind::kVarProperty || + groupKey->kind() == Expression::Kind::kInputProperty || + groupKey->kind() == Expression::Kind::kConstant) { + continue; + } status_ = extractPropsFromExpr(groupKey); if (!status_.ok()) { return; } } for (auto *groupItem : node->groupItems()) { + if (groupItem->kind() == Expression::Kind::kVarProperty || + groupItem->kind() == Expression::Kind::kInputProperty || + groupItem->kind() == Expression::Kind::kConstant) { + continue; + } status_ = extractPropsFromExpr(groupItem); if (!status_.ok()) { return; @@ -109,13 +130,12 @@ void PrunePropertiesVisitor::visitCurrent(Aggregate *node) { } void PrunePropertiesVisitor::visit(Traverse *node) { + rootNode_ = false; visitCurrent(node); status_ = depsPruneProperties(node->dependencies()); } void PrunePropertiesVisitor::visitCurrent(Traverse *node) { - bool used = used_; - used_ = false; auto &colNames = node->colNames(); DCHECK_GE(colNames.size(), 2); auto &nodeAlias = colNames[colNames.size() - 2]; @@ -134,32 +154,7 @@ void PrunePropertiesVisitor::visitCurrent(Traverse *node) { return; } } - - if (used) { - // All properties will be used - const auto *vertexProps = node->vertexProps(); - if (vertexProps != nullptr) { - for (const auto &vertexProp : *vertexProps) { - auto tagId = vertexProp.tag_ref().value(); - auto &props = vertexProp.props_ref().value(); - for (const auto &prop : props) { - propsUsed_.vertexPropsMap[nodeAlias][tagId].emplace(prop); - } - } - } - const auto *edgeProps = node->edgeProps(); - if (edgeProps != nullptr) { - for (const auto &edgeProp : *edgeProps) { - auto edgeType = edgeProp.type_ref().value(); - auto &props = edgeProp.props_ref().value(); - for (const auto &prop : props) { - propsUsed_.edgePropsMap[edgeAlias][edgeType].emplace(prop); - } - } - } - } else { - pruneCurrent(node); - } + pruneCurrent(node); } void PrunePropertiesVisitor::pruneCurrent(Traverse *node) { @@ -168,24 +163,29 @@ void PrunePropertiesVisitor::pruneCurrent(Traverse *node) { auto &nodeAlias = colNames[colNames.size() - 2]; auto &edgeAlias = colNames.back(); auto *vertexProps = node->vertexProps(); - if (propsUsed_.colsSet.find(nodeAlias) == propsUsed_.colsSet.end() && vertexProps != nullptr) { - auto it2 = propsUsed_.vertexPropsMap.find(nodeAlias); - if (it2 == propsUsed_.vertexPropsMap.end()) { // nodeAlias is not used + auto &colsSet = propsUsed_.colsSet; + auto &vertexPropsMap = propsUsed_.vertexPropsMap; + auto &edgePropsMap = propsUsed_.edgePropsMap; + + if (colsSet.find(nodeAlias) == colsSet.end()) { + auto aliasIter = vertexPropsMap.find(nodeAlias); + if (aliasIter == vertexPropsMap.end()) { node->setVertexProps(nullptr); } else { - auto prunedVertexProps = std::make_unique>(); - auto &usedVertexProps = it2->second; + auto &usedVertexProps = aliasIter->second; if (usedVertexProps.empty()) { node->setVertexProps(nullptr); - return; - } - prunedVertexProps->reserve(usedVertexProps.size()); - for (auto &vertexProp : *vertexProps) { - auto tagId = vertexProp.tag_ref().value(); - auto &props = vertexProp.props_ref().value(); - auto it3 = usedVertexProps.find(tagId); - if (it3 != usedVertexProps.end()) { - auto &usedProps = it3->second; + } else { + auto prunedVertexProps = std::make_unique>(); + prunedVertexProps->reserve(usedVertexProps.size()); + for (auto &vertexProp : *vertexProps) { + auto tagId = vertexProp.tag_ref().value(); + auto &props = vertexProp.props_ref().value(); + auto tagIter = usedVertexProps.find(tagId); + if (tagIter == usedVertexProps.end()) { + continue; + } + auto &usedProps = tagIter->second; VertexProp newVProp; newVProp.tag_ref() = tagId; std::vector newProps; @@ -197,49 +197,51 @@ void PrunePropertiesVisitor::pruneCurrent(Traverse *node) { newVProp.props_ref() = std::move(newProps); prunedVertexProps->emplace_back(std::move(newVProp)); } + node->setVertexProps(std::move(prunedVertexProps)); } - node->setVertexProps(std::move(prunedVertexProps)); } } - static const std::unordered_set reservedEdgeProps = { - nebula::kSrc, nebula::kType, nebula::kRank, nebula::kDst}; auto *edgeProps = node->edgeProps(); - if (propsUsed_.colsSet.find(edgeAlias) == propsUsed_.colsSet.end() && edgeProps != nullptr) { - auto prunedEdgeProps = std::make_unique>(); - prunedEdgeProps->reserve(edgeProps->size()); - auto it2 = propsUsed_.edgePropsMap.find(edgeAlias); + if (colsSet.find(edgeAlias) != colsSet.end()) { + // all edge properties are used + return; + } + auto prunedEdgeProps = std::make_unique>(); + prunedEdgeProps->reserve(edgeProps->size()); + auto edgeAliasIter = edgePropsMap.find(edgeAlias); - for (auto &edgeProp : *edgeProps) { - auto edgeType = edgeProp.type_ref().value(); - auto &props = edgeProp.props_ref().value(); - EdgeProp newEProp; - newEProp.type_ref() = edgeType; - std::vector newProps{reservedEdgeProps.begin(), reservedEdgeProps.end()}; - std::unordered_set usedProps; - if (it2 != propsUsed_.edgePropsMap.end()) { - auto &usedEdgeProps = it2->second; - auto it3 = usedEdgeProps.find(std::abs(edgeType)); - if (it3 != usedEdgeProps.end()) { - usedProps = {it3->second.begin(), it3->second.end()}; - } - static const int kUnknownEdgeType = 0; - auto it4 = usedEdgeProps.find(kUnknownEdgeType); - if (it4 != usedEdgeProps.end()) { - usedProps.insert(it4->second.begin(), it4->second.end()); - } + for (auto &edgeProp : *edgeProps) { + auto edgeType = edgeProp.type_ref().value(); + auto &props = edgeProp.props_ref().value(); + EdgeProp newEdgeProp; + newEdgeProp.type_ref() = edgeType; + if (edgeAliasIter == edgePropsMap.end()) { + // only type, dst are used + newEdgeProp.props_ref() = {nebula::kDst, nebula::kType, nebula::kRank}; + } else { + std::unordered_set uniqueProps{nebula::kDst, nebula::kType, nebula::kRank}; + std::vector newProps; + auto &usedEdgeProps = edgeAliasIter->second; + auto edgeTypeIter = usedEdgeProps.find(std::abs(edgeType)); + if (edgeTypeIter != usedEdgeProps.end()) { + uniqueProps.insert(edgeTypeIter->second.begin(), edgeTypeIter->second.end()); + } + int kUnknowEdgeType = 0; + auto unKnowEdgeIter = usedEdgeProps.find(kUnknowEdgeType); + if (unKnowEdgeIter != usedEdgeProps.end()) { + uniqueProps.insert(unKnowEdgeIter->second.begin(), unKnowEdgeIter->second.end()); } for (auto &prop : props) { - if (reservedEdgeProps.find(prop) == reservedEdgeProps.end() && - usedProps.find(prop) != usedProps.end()) { + if (uniqueProps.find(prop) != uniqueProps.end()) { newProps.emplace_back(prop); } } - newEProp.props_ref() = std::move(newProps); - prunedEdgeProps->emplace_back(std::move(newEProp)); + newEdgeProp.props_ref() = std::move(newProps); } - node->setEdgeProps(std::move(prunedEdgeProps)); + prunedEdgeProps->emplace_back(std::move(newEdgeProp)); } + node->setEdgeProps(std::move(prunedEdgeProps)); } // AppendVertices should be deleted when no properties it pulls are used by the parent node. @@ -249,38 +251,31 @@ void PrunePropertiesVisitor::visit(AppendVertices *node) { } void PrunePropertiesVisitor::visitCurrent(AppendVertices *node) { - bool used = used_; - used_ = false; + if (rootNode_) { + rootNode_ = false; + return; + } auto &colNames = node->colNames(); DCHECK(!colNames.empty()); auto &nodeAlias = colNames.back(); auto it = propsUsed_.colsSet.find(nodeAlias); - if (it != propsUsed_.colsSet.end()) { // All properties are used - // propsUsed_.colsSet.erase(it); + if (it != propsUsed_.colsSet.end()) { + // all properties are used return; } - - if (node->vFilter() != nullptr) { - status_ = extractPropsFromExpr(node->vFilter(), nodeAlias); + if (node->filter() != nullptr) { + status_ = extractPropsFromExpr(node->filter(), nodeAlias); if (!status_.ok()) { return; } } - if (used) { - // All properties will be used - auto *vertexProps = node->props(); - if (vertexProps != nullptr) { - for (const auto &vertexProp : *vertexProps) { - auto tagId = vertexProp.tag_ref().value(); - auto &props = vertexProp.props_ref().value(); - for (const auto &prop : props) { - propsUsed_.vertexPropsMap[nodeAlias][tagId].emplace(prop); - } - } + if (node->vFilter() != nullptr) { + status_ = extractPropsFromExpr(node->vFilter(), nodeAlias); + if (!status_.ok()) { + return; } - } else { - pruneCurrent(node); } + pruneCurrent(node); } void PrunePropertiesVisitor::pruneCurrent(AppendVertices *node) { @@ -288,60 +283,108 @@ void PrunePropertiesVisitor::pruneCurrent(AppendVertices *node) { DCHECK(!colNames.empty()); auto &nodeAlias = colNames.back(); auto *vertexProps = node->props(); - if (vertexProps != nullptr) { - auto prunedVertexProps = std::make_unique>(); - auto it2 = propsUsed_.vertexPropsMap.find(nodeAlias); - if (it2 != propsUsed_.vertexPropsMap.end()) { - auto &usedVertexProps = it2->second; - if (usedVertexProps.empty()) { - node->markDeleted(); - return; - } - prunedVertexProps->reserve(usedVertexProps.size()); - for (auto &vertexProp : *vertexProps) { - auto tagId = vertexProp.tag_ref().value(); - auto &props = vertexProp.props_ref().value(); - auto it3 = usedVertexProps.find(tagId); - if (it3 != usedVertexProps.end()) { - auto &usedProps = it3->second; - VertexProp newVProp; - newVProp.tag_ref() = tagId; - std::vector newProps; - for (auto &prop : props) { - if (usedProps.find(prop) != usedProps.end()) { - newProps.emplace_back(prop); - } - } - newVProp.props_ref() = std::move(newProps); - prunedVertexProps->emplace_back(std::move(newVProp)); - } - } + if (vertexProps == nullptr) { + return; + } + auto prunedVertexProps = std::make_unique>(); + auto &vertexPropsMap = propsUsed_.vertexPropsMap; + auto aliasIter = vertexPropsMap.find(nodeAlias); + if (aliasIter == vertexPropsMap.end()) { + if (FLAGS_optimize_appendvertices) { + node->setVertexProps(nullptr); } else { - node->markDeleted(); - return; + // only get _tag when props is nullptr + auto tagId = vertexProps->front().tag_ref().value(); + VertexProp newVProp; + newVProp.tag_ref() = tagId; + newVProp.props_ref() = {nebula::kTag}; + prunedVertexProps->emplace_back(std::move(newVProp)); + node->setVertexProps(std::move(prunedVertexProps)); + } + return; + } + auto &usedVertexProps = aliasIter->second; + if (usedVertexProps.empty()) { + if (FLAGS_optimize_appendvertices) { + node->setVertexProps(nullptr); + } else { + // only get _tag when props is nullptr + auto tagId = vertexProps->front().tag_ref().value(); + VertexProp newVProp; + newVProp.tag_ref() = tagId; + newVProp.props_ref() = {nebula::kTag}; + prunedVertexProps->emplace_back(std::move(newVProp)); + node->setVertexProps(std::move(prunedVertexProps)); + } + return; + } + + prunedVertexProps->reserve(usedVertexProps.size()); + for (auto &vertexProp : *vertexProps) { + auto tagId = vertexProp.tag_ref().value(); + auto &props = vertexProp.props_ref().value(); + auto tagIter = usedVertexProps.find(tagId); + if (tagIter == usedVertexProps.end()) { + continue; + } + auto &usedProps = tagIter->second; + VertexProp newVProp; + newVProp.tag_ref() = tagId; + std::vector newProps; + for (auto &prop : props) { + if (usedProps.find(prop) != usedProps.end()) { + newProps.emplace_back(prop); + } } - node->setVertexProps(std::move(prunedVertexProps)); + newVProp.props_ref() = std::move(newProps); + prunedVertexProps->emplace_back(std::move(newVProp)); } + node->setVertexProps(std::move(prunedVertexProps)); } void PrunePropertiesVisitor::visit(BiJoin *node) { + status_ = depsPruneProperties(node->dependencies()); +} + +void PrunePropertiesVisitor::visit(BiCartesianProduct *node) { + status_ = pruneMultiBranch(node->dependencies()); +} + +void PrunePropertiesVisitor::visit(Union *node) { + status_ = pruneMultiBranch(node->dependencies()); +} + +void PrunePropertiesVisitor::visit(Unwind *node) { visitCurrent(node); status_ = depsPruneProperties(node->dependencies()); } -void PrunePropertiesVisitor::visitCurrent(BiJoin *node) { - for (auto *hashKey : node->hashKeys()) { - status_ = extractPropsFromExpr(hashKey); +void PrunePropertiesVisitor::visitCurrent(Unwind *node) { + const auto &alias = node->alias(); + if (propsUsed_.hasAlias(alias)) { + status_ = extractPropsFromExpr(node->unwindExpr()); if (!status_.ok()) { return; } } - for (auto *probeKey : node->probeKeys()) { - status_ = extractPropsFromExpr(probeKey); - if (!status_.ok()) { - return; - } +} + +Status PrunePropertiesVisitor::pruneMultiBranch(std::vector &dependencies) { + DCHECK_EQ(dependencies.size(), 2); + auto rightPropsUsed = propsUsed_; + auto *leftDep = dependencies.front(); + const_cast(leftDep)->accept(this); + if (!status_.ok()) { + return status_; + } + rootNode_ = true; + propsUsed_ = std::move(rightPropsUsed); + auto *rightDep = dependencies.back(); + const_cast(rightDep)->accept(this); + if (!status_.ok()) { + return status_; } + return Status::OK(); } Status PrunePropertiesVisitor::depsPruneProperties(std::vector &dependencies) { diff --git a/src/graph/visitor/PrunePropertiesVisitor.h b/src/graph/visitor/PrunePropertiesVisitor.h index c6f67e18518..839354ff48f 100644 --- a/src/graph/visitor/PrunePropertiesVisitor.h +++ b/src/graph/visitor/PrunePropertiesVisitor.h @@ -63,20 +63,23 @@ class PrunePropertiesVisitor final : public PlanNodeVisitor { void pruneCurrent(AppendVertices *node); void visit(BiJoin *node) override; - // \param node, the current node to visit - // \param used, whether properties in current node are used - void visitCurrent(BiJoin *node); + + void visit(Union *node) override; + void visit(BiCartesianProduct *node) override; + + void visit(Unwind *node) override; + void visitCurrent(Unwind *node); private: Status depsPruneProperties(std::vector &dependencies); + Status pruneMultiBranch(std::vector &dependencies); Status extractPropsFromExpr(const Expression *expr, const std::string &entityAlias = ""); PropertyTracker &propsUsed_; QueryContext *qctx_; GraphSpaceID spaceID_; Status status_; - // force use all properties in current node, e.g. the root node in plan - bool used_{true}; + bool rootNode_{true}; }; } // namespace graph diff --git a/tests/tck/features/optimizer/PrunePropertiesRule.feature b/tests/tck/features/optimizer/PrunePropertiesRule.feature index 099eed96595..f3cc31eb070 100644 --- a/tests/tck/features/optimizer/PrunePropertiesRule.feature +++ b/tests/tck/features/optimizer/PrunePropertiesRule.feature @@ -21,12 +21,12 @@ Feature: Prune Properties rule | 33 | | 41 | And the execution plan should be: - | id | name | dependencies | operator info | - | 8 | Project | 4 | | - | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"age\"],\"tagId\":9}]" } | - | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | - | 7 | IndexScan | 2 | | - | 2 | Start | | | + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"age\"],\"tagId\":9}]" } | + | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]->(v2) @@ -38,12 +38,12 @@ Feature: Prune Properties rule | "Tony Parker" | | "Tony Parker" | And the execution plan should be: - | id | name | dependencies | operator info | - | 8 | Project | 4 | | - | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"name\", \"age\", \"_tag\"]}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]"} | - | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | - | 7 | IndexScan | 2 | | - | 2 | Start | | | + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"_tag\"]}]"} | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]-(v2) @@ -60,12 +60,12 @@ Feature: Prune Properties rule | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | And the execution plan should be: - | id | name | dependencies | operator info | - | 8 | Project | 4 | | - | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"name\", \"age\", \"_tag\"]}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]" } | - | 3 | Traverse | 7 | { "vertexProps": "[{\"props\": [\"name\", \"age\", \"_tag\"], \"tagId\": 9}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}]" } | - | 7 | IndexScan | 2 | | - | 2 | Start | | | + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"tagId\": 9, \"props\": [\"_tag\"]} ]" } | + | 3 | Traverse | 7 | { "vertexProps": "[{\"props\": [\"name\", \"age\", \"_tag\"], \"tagId\": 9}, {\"props\": [\"name\", \"speciality\", \"_tag\"], \"tagId\": 8}, {\"tagId\": 10, \"props\": [\"name\", \"_tag\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When profiling query: """ MATCH p = (v:player{name: "Tony Parker"})-[e:like]->(v2) @@ -80,7 +80,7 @@ Feature: Prune Properties rule | id | name | dependencies | operator info | | 8 | Project | 4 | | | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"], \"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | - | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | + | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\"],\"type\":3}]" } | | 7 | IndexScan | 2 | | | 2 | Start | | | # The rule will not take affect in this case because it returns the whole path @@ -110,12 +110,12 @@ Feature: Prune Properties rule | "like" | | "like" | And the execution plan should be: - | id | name | dependencies | operator info | - | 8 | Project | 4 | | - | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"], \"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | - | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\", \"_src\"],\"type\":3}]" } | - | 7 | IndexScan | 2 | | - | 2 | Start | | | + | id | name | dependencies | operator info | + | 8 | Project | 4 | | + | 4 | AppendVertices | 3 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9} ]" } | + | 3 | Traverse | 7 | { "vertexProps": "", "edgeProps": "[{\"props\":[\"_dst\", \"_rank\", \"_type\"],\"type\":3}]" } | + | 7 | IndexScan | 2 | | + | 2 | Start | | | When executing query: """ MATCH (v:player{name: "Tony Parker"})-[:like]-(v2)--(v3) @@ -171,20 +171,20 @@ Feature: Prune Properties rule | "Tim Duncan" | "Boris Diaw" | "Suns" | | "Tim Duncan" | "Boris Diaw" | "Tim Duncan" | And the execution plan should be: - | id | name | dependencies | operator info | - | 16 | TopN | 12 | | - | 12 | Project | 9 | | - | 9 | BiInnerJoin | 22, 23 | | - | 22 | Project | 5 | | - | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 2 | Dedup | 1 | | - | 1 | PassThrough | 3 | | - | 3 | Start | | | - | 23 | Project | 8 | | - | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}, {\"tagId\":10,\"props\":[\"name\"]}]" } | - | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | - | 6 | Argument | | | + | id | name | dependencies | operator info | + | 16 | TopN | 12 | | + | 12 | Project | 9 | | + | 9 | BiInnerJoin | 22, 23 | | + | 22 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 23 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}, {\"tagId\":10,\"props\":[\"name\"]}]" } | + | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 4}]" } | + | 6 | Argument | | | When profiling query: """ MATCH (m)-[]-(n), (n)-[]-(l), (l)-[]-(h) WHERE id(m)=="Tim Duncan" @@ -204,26 +204,26 @@ Feature: Prune Properties rule | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | And the execution plan should be: - | id | name | dependencies | operator info | - | 20 | TopN | 23 | | - | 23 | Project | 13 | | - | 13 | BiInnerJoin | 9, 30 | | - | 9 | BiInnerJoin | 28, 29 | | - | 28 | Project | 5 | | - | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | - | 2 | Dedup | 1 | | - | 1 | PassThrough | 3 | | - | 3 | Start | | | - | 29 | Project | 8 | | - | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | - | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | - | 6 | Argument | | | - | 31 | Start | | | - | 30 | Project | 12 | | - | 12 | AppendVertices | 11 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 11 | Traverse | 10 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":10}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | - | 10 | Argument | | | + | id | name | dependencies | operator info | + | 20 | TopN | 23 | | + | 23 | Project | 13 | | + | 13 | BiInnerJoin | 9, 30 | | + | 9 | BiInnerJoin | 28, 29 | | + | 28 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 4}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 29 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | + | 7 | Traverse | 6 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 4}]" } | + | 6 | Argument | | | + | 31 | Start | | | + | 30 | Project | 12 | | + | 12 | AppendVertices | 11 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 11 | Traverse | 10 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":10}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"type\": 5, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}, {\"type\": -4, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 4}]" } | + | 10 | Argument | | | # The schema id is not fixed in standalone cluster, so we skip it @distonly @@ -284,27 +284,27 @@ Feature: Prune Properties rule | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Aron Baynes" | | "Tim Duncan" | "Aron Baynes" | "Spurs" | "Boris Diaw" | And the execution plan should be: - | id | name | dependencies | operator info | - | 21 | TopN | 17 | | - | 17 | Project | 16 | | - | 16 | BiInnerJoin | 23, 14 | | - | 23 | Project | 5 | | - | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | - | 2 | Dedup | 1 | | - | 1 | PassThrough | 3 | | - | 3 | Start | | | - | 14 | BiInnerJoin | 33, 34 | | - | 33 | Project | 10 | | - | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | - | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | - | 8 | Argument | | | - | 35 | Start | | | - | 34 | Project | 13 | | - | 13 | AppendVertices | 12 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | - | 12 | Traverse | 11 | { "vertexProps": "[{\"tagId\":10,\"props\":[\"name\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -5}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 5}, {\"type\": -3, \"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 3}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": -4}, {\"props\": [\"_dst\", \"_rank\", \"_type\", \"_src\"], \"type\": 4}]" } | - | 11 | Argument | | | - | 36 | Start | | | + | id | name | dependencies | operator info | + | 21 | TopN | 17 | | + | 17 | Project | 16 | | + | 16 | BiInnerJoin | 23, 14 | | + | 23 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | { "vertexProps": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 14 | BiInnerJoin | 33, 34 | | + | 33 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"tagId\":10,\"props\":[\"name\"]}]" } | + | 9 | Traverse | 8 | { "vertexProps": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | + | 8 | Argument | | | + | 35 | Start | | | + | 34 | Project | 13 | | + | 13 | AppendVertices | 12 | { "props": "[{\"tagId\":9,\"props\":[\"name\"]}]" } | + | 12 | Traverse | 11 | { "vertexProps": "[{\"tagId\":10,\"props\":[\"name\"]}]", "edgeProps": "[{\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -5}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 5}, {\"type\": -3, \"props\": [\"_dst\", \"_rank\", \"_type\"]}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 3}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": -4}, {\"props\": [\"_dst\", \"_rank\", \"_type\"], \"type\": 4}]" } | + | 11 | Argument | | | + | 36 | Start | | | When profiling query: """ MATCH (v:player{name:"Tony Parker"}) @@ -317,17 +317,17 @@ Feature: Prune Properties rule | "Tim Duncan" | | "Tim Duncan" | And the execution plan should be: - | id | name | dependencies | operator info | - | 10 | Project | 11 | | - | 11 | BiInnerJoin | 14, 9 | | - | 14 | Project | 3 | | - | 3 | AppendVertices | 12 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | - | 12 | IndexScan | 2 | | - | 2 | Start | | | - | 9 | Project | 8 | | - | 8 | AppendVertices | 7 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | - | 7 | Traverse | 6 | { "vertexProps": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]", "edgeProps": "[{\"type\": -5, \"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"start_year\", \"end_year\"]}, {\"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"likeness\"], \"type\": -3}, {\"props\": [\"_src\", \"_type\", \"_rank\", \"_dst\", \"start_year\", \"end_year\"], \"type\": -4}]" } | - | 6 | Argument | | | + | id | name | dependencies | operator info | + | 10 | Project | 11 | | + | 11 | BiInnerJoin | 14, 9 | | + | 14 | Project | 3 | | + | 3 | AppendVertices | 12 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 12 | IndexScan | 2 | | + | 2 | Start | | | + | 9 | Project | 8 | | + | 8 | AppendVertices | 7 | { "props": "[{\"props\":[\"name\"],\"tagId\":9}]" } | + | 7 | Traverse | 6 | { "vertexProps": "", "edgeProps": "[{\"type\": -5, \"props\": [\"_type\", \"_rank\", \"_dst\"]}, {\"props\": [\"_type\", \"_rank\", \"_dst\"], \"type\": -3}, {\"props\": [\"_type\", \"_rank\", \"_dst\"], \"type\": -4}]" } | + | 6 | Argument | | | # The schema id is not fixed in standalone cluster, so we skip it @distonly @@ -376,15 +376,178 @@ Feature: Prune Properties rule | scount | | 270 | And the execution plan should be: - | id | name | dependencies | operator info | - | 12 | Aggregate | 13 | | - | 13 | BiInnerJoin | 15, 11 | | - | 15 | Project | 4 | | - | 4 | Traverse | 3 | { "vertexProps": "" } | - | 3 | Traverse | 14 | { "vertexProps": "" } | - | 14 | IndexScan | 2 | | - | 2 | Start | | | - | 11 | Project | 10 | | - | 10 | AppendVertices | 9 | { "props": "[{\"props\":[\"name\", \"age\", \"_tag\"],\"tagId\":9}, {\"props\":[\"name\", \"speciality\", \"_tag\"],\"tagId\":8}, {\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | - | 9 | Traverse | 8 | { "vertexProps": "" } | - | 8 | Argument | | | + | id | name | dependencies | operator info | + | 12 | Aggregate | 13 | | + | 13 | BiInnerJoin | 15, 11 | | + | 15 | Project | 4 | | + | 4 | Traverse | 3 | { "vertexProps": "" } | + | 3 | Traverse | 14 | { "vertexProps": "" } | + | 14 | IndexScan | 2 | | + | 2 | Start | | | + | 11 | Project | 10 | | + | 10 | AppendVertices | 9 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9}]" } | + | 9 | Traverse | 8 | { "vertexProps": "" } | + | 8 | Argument | | | + + @distonly + Scenario: return function + When profiling query: + """ + MATCH (v1)-[e:like*1..5]->(v2) + WHERE id(v1) == "Tim Duncan" + RETURN count(v2.player.age) + """ + Then the result should be, in order: + | count(v2.player.age) | + | 24 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 7 | Aggregate | 6 | | + | 6 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"age\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + When profiling query: + """ + MATCH (v1)-[e:like*1..5]->(v2) + WHERE id(v1) == "Tim Duncan" + RETURN count(v2) + """ + Then the result should be, in order: + | count(v2) | + | 24 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 7 | Aggregate | 6 | | + | 6 | Project | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + When profiling query: + """ + MATCH p = (v1)-[e:like*1..5]->(v2) + WHERE id(v1) == "Tim Duncan" + RETURN length(p) LIMIT 1 + """ + Then the result should be, in order: + | length(p) | + | 1 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 13 | Project | 11 | | + | 11 | Limit | 5 | | + | 5 | AppendVertices | 4 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9}]" } | + | 4 | Traverse | 2 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + When profiling query: + """ + MATCH p = (a:player)-[e:like*1..3]->(b:player{age:39}) + WHERE id(a) == 'Yao Ming' + WITH b, length(p) AS distance + MATCH (b)-[:serve]->(t:team) + RETURN b.player.name, distance + """ + Then the result should be, in order: + | b.player.name | distance | + | "Tracy McGrady" | 1 | + | "Tracy McGrady" | 3 | + | "Tracy McGrady" | 1 | + | "Tracy McGrady" | 3 | + | "Tracy McGrady" | 1 | + | "Tracy McGrady" | 3 | + | "Tracy McGrady" | 1 | + | "Tracy McGrady" | 3 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 14 | Project | 13 | | + | 13 | BiInnerJoin | 15,12 | | + | 15 | Project | 17 | | + | 17 | AppendVertices | 16 | { "props": "[{\"props\":[\"name\",\"age\"],\"tagId\":9}]" } | + | 16 | Traverse | 2 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 12 | Project | 18 | | + | 18 | AppendVertices | 10 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":10}]" } | + | 10 | Traverse | 8 | {"vertexProps": "[{\"props\":[\"name\",\"age\"],\"tagId\":9}]", "edgeProps": "[{\"type\": 4, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 8 | Argument | | | + | 9 | Start | | | + + @distonly + Scenario: union match + When profiling query: + """ + MATCH (v:player{name:'Tim Duncan'})-[:like]->(b) RETURN id(b) + UNION + MATCH (a:player{age:36})-[:like]-(b) RETURN id(b) + """ + Then the result should be, in any order: + | id(b) | + | "Tony Parker" | + | "Manu Ginobili" | + | "Marco Belinelli" | + | "Dejounte Murray" | + | "Tim Duncan" | + | "Boris Diaw" | + | "LaMarcus Aldridge" | + | "Steve Nash" | + And the execution plan should be: + | id | name | dependencies | operator info | + | 14 | Dedup | 13 | | + | 13 | Union | 18, 19 | | + | 18 | Project | 4 | | + | 4 | AppendVertices | 20 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9}]" } | + | 20 | Traverse | 16 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 16 | IndexScan | 2 | | + | 2 | Start | | | + | 19 | Project | 10 | | + | 10 | AppendVertices | 21 | { "props": "[{\"props\":[\"_tag\"],\"tagId\":9}]" } | + | 21 | Traverse | 17 | {"vertexProps": "", "edgeProps": "[{\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}, {\"type\": -3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 17 | IndexScan | 8 | | + | 8 | Start | | | + + @distonly + Scenario: optional match + When profiling query: + """ + MATCH (v:player)-[:like]-(:player)<-[:teammate]-(b:player)-[:serve]->(t:team) + WHERE id(v) == 'Tim Duncan' AND b.player.age > 20 + WITH v, count(b) AS countB, t + OPTIONAL MATCH (v)-[:like]-()<-[:like]-(oldB)-[:serve]->(t) + WHERE oldB.player.age > 10 + WITH v, countB, t, count(oldB) AS cb + RETURN t.team.name, sum(countB) + """ + Then the result should be, in any order: + | t.team.name | sum(countB) | + | "Spurs" | 11 | + | "Hornets" | 3 | + And the execution plan should be: + | id | name | dependencies | operator info | + | 21 | Aggregate | 20 | | + | 20 | Aggregate | 19 | | + | 19 | BiLeftJoin | 10, 25 | | + | 10 | Aggregate | 23 | | + | 23 | Project | 22 | | + | 22 | Filter | 29 | | + | 29 | AppendVertices | 28 | { "props": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 28 | Traverse | 27 | {"vertexProps": "[{\"props\":[\"age\"],\"tagId\":9}]", "edgeProps": "[{\"type\": 4, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 27 | Traverse | 26 | {"vertexProps": "", "edgeProps": "[{\"type\": -5, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 26 | Traverse | 2 | {"vertexProps": "", "edgeProps": "[{\"type\": -3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}, {\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 2 | Dedup | 1 | | + | 1 | PassThrough | 3 | | + | 3 | Start | | | + | 25 | Project | 24 | | + | 24 | Filter | 16 | | + | 16 | AppendVertices | 15 | { "props": "[{\"props\":[\"name\", \"_tag\"],\"tagId\":10}]" } | + | 15 | Traverse | 14 | {"vertexProps": "[{\"props\":[\"age\"],\"tagId\":9}]", "edgeProps": "[{\"type\": 4, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 14 | Traverse | 13 | {"vertexProps": "", "edgeProps": "[{\"type\": -3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 13 | Traverse | 11 | {"vertexProps": "", "edgeProps": "[{\"type\": -3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}, {\"type\": 3, \"props\": [\"_type\", \"_rank\", \"_dst\"]}]" } | + | 11 | Argument | | | + | 12 | Start | | |