diff --git a/src/context/Iterator.cpp b/src/context/Iterator.cpp index a9ed83304c68df6eba3818f04e95d4b4ba3f3552..1cde95c4c4535948da95cf31d12dafe3fbb8980f 100644 --- a/src/context/Iterator.cpp +++ b/src/context/Iterator.cpp @@ -686,28 +686,20 @@ std::ostream& operator<<(std::ostream& os, LogicalRow::Kind kind) { } std::ostream& operator<<(std::ostream& os, const LogicalRow& row) { - switch (row.kind()) { - case nebula::graph::LogicalRow::Kind::kSequential: - case nebula::graph::LogicalRow::Kind::kJoin: { - std::stringstream ss; - size_t cnt = 0; - for (auto* seg : row.segments()) { - if (seg == nullptr) { - ss << "nullptr"; - } else { - ss << *seg; - } - if (cnt < row.size() - 1) { - ss << ","; - } - ++cnt; - } - os << ss.str(); - break; + std::stringstream ss; + size_t cnt = 0; + for (auto* seg : row.segments()) { + if (seg == nullptr) { + ss << "nullptr"; + } else { + ss << *seg; } - default: - LOG(FATAL) << "Not support streaming for " << row.kind(); + if (cnt < row.size() - 1) { + ss << ","; + } + ++cnt; } + os << ss.str(); return os; } } // namespace graph diff --git a/src/context/ast/QueryAstContext.h b/src/context/ast/QueryAstContext.h index e2f12f0b05b215cd3237507df44ca76234a899e4..c7ed9351f56e6c4f3383ecb280565f52bd6880d8 100644 --- a/src/context/ast/QueryAstContext.h +++ b/src/context/ast/QueryAstContext.h @@ -150,11 +150,10 @@ struct NodeContext final : PatternContext { NodeInfo* info{nullptr}; // Output fields - ScanInfo scanInfo; - const Expression* ids{nullptr}; + ScanInfo scanInfo; + const Expression* ids{nullptr}; // initialize start expression in project node - // initialExpr will be used by YieldColumn, so no need to handle the lifecycle by object pool. - Expression* initialExpr{nullptr}; + std::unique_ptr<Expression> initialExpr; }; struct EdgeContext final : PatternContext { diff --git a/src/executor/query/ProjectExecutor.cpp b/src/executor/query/ProjectExecutor.cpp index 6559ea9a328cd7b7e4ae251573ba9e6f5bef7fb9..80604cbdbff4e186afe4679054faf410c61606e0 100644 --- a/src/executor/query/ProjectExecutor.cpp +++ b/src/executor/query/ProjectExecutor.cpp @@ -26,6 +26,7 @@ folly::Future<Status> ProjectExecutor::execute() { DataSet ds; ds.colNames = project->colNames(); for (; iter->valid(); iter->next()) { + VLOG(1) << "row: " << *iter->row(); Row row; for (auto& col : columns) { Value val = col->expr()->eval(ctx(iter.get())); diff --git a/src/planner/match/AddDependencyStrategy.h b/src/planner/match/AddDependencyStrategy.h index 7e6aa336edcb392e8ae622f06e9326d4311427f6..52494559349e63bbf20d84900bbb1e6384b5672d 100644 --- a/src/planner/match/AddDependencyStrategy.h +++ b/src/planner/match/AddDependencyStrategy.h @@ -8,7 +8,7 @@ #define PLANNER_MATCH_ADDDEPENDENCYSTRATEGY_H_ #include "planner/PlanNode.h" -#include "planner/match/SegmentsConnector.h" +#include "planner/match/SegmentsConnectStrategy.h" namespace nebula { namespace graph { diff --git a/src/planner/match/AddInputStrategy.h b/src/planner/match/AddInputStrategy.h index 5d0a298487f34f65fcf3cceac509444d05bdc5a2..54ffcbedc14087f9264f19d7b2fa75aa9ad87626 100644 --- a/src/planner/match/AddInputStrategy.h +++ b/src/planner/match/AddInputStrategy.h @@ -8,7 +8,7 @@ #define PLANNER_MATCH_ADDINPUTSTRATEGY_H_ #include "planner/PlanNode.h" -#include "planner/match/SegmentsConnector.h" +#include "planner/match/SegmentsConnectStrategy.h" namespace nebula { namespace graph { diff --git a/src/planner/match/Expand.cpp b/src/planner/match/Expand.cpp index 7d5c4c78f20e81bfb403c9739cb7ea00afc444c7..1d6bab1461ac47f9bd63abbe2fd02aa55c4657ca 100644 --- a/src/planner/match/Expand.cpp +++ b/src/planner/match/Expand.cpp @@ -26,26 +26,35 @@ static std::unique_ptr<std::vector<VertexProp>> genVertexProps() { } std::unique_ptr<std::vector<storage::cpp2::EdgeProp>> Expand::genEdgeProps(const EdgeInfo &edge) { - if (edge.edgeTypes.empty()) { - return std::make_unique<std::vector<storage::cpp2::EdgeProp>>( - buildAllEdgeProp().value()); - } - auto edgeProps = std::make_unique<std::vector<EdgeProp>>(); for (auto edgeType : edge.edgeTypes) { auto edgeSchema = matchCtx_->qctx->schemaMng()->getEdgeSchema( matchCtx_->space.id, edgeType); - if (edge.direction == Direction::IN_EDGE) { - edgeType = -edgeType; - } else if (edge.direction == Direction::BOTH) { - EdgeProp edgeProp; - edgeProp.set_type(-edgeType); - std::vector<std::string> props{kSrc, kType, kRank, kDst}; - for (std::size_t i = 0; i < edgeSchema->getNumFields(); ++i) { - props.emplace_back(edgeSchema->getFieldName(i)); + + switch (edge.direction) { + case Direction::OUT_EDGE: { + if (reversely_) { + edgeType = -edgeType; + } + break; + } + case Direction::IN_EDGE: { + if (!reversely_) { + edgeType = -edgeType; + } + break; + } + case Direction::BOTH: { + EdgeProp edgeProp; + edgeProp.set_type(-edgeType); + std::vector<std::string> props{kSrc, kType, kRank, kDst}; + for (std::size_t i = 0; i < edgeSchema->getNumFields(); ++i) { + props.emplace_back(edgeSchema->getFieldName(i)); + } + edgeProp.set_props(std::move(props)); + edgeProps->emplace_back(std::move(edgeProp)); + break; } - edgeProp.set_props(std::move(props)); - edgeProps->emplace_back(std::move(edgeProp)); } EdgeProp edgeProp; edgeProp.set_type(edgeType); @@ -75,25 +84,24 @@ static Expression* buildPathExpr() { Status Expand::doExpand(const NodeInfo& node, const EdgeInfo& edge, - const PlanNode* input, - SubPlan*plan) { - NG_RETURN_IF_ERROR(expandSteps(node, edge, input, plan)); + SubPlan* plan) { + NG_RETURN_IF_ERROR(expandSteps(node, edge, plan)); NG_RETURN_IF_ERROR(filterDatasetByPathLength(edge, plan->root, plan)); return Status::OK(); } Status Expand::expandSteps(const NodeInfo& node, const EdgeInfo& edge, - const PlanNode* input, SubPlan* plan) { SubPlan subplan; - NG_RETURN_IF_ERROR(expandStep(edge, input, node.filter, true, &subplan)); + NG_RETURN_IF_ERROR(expandStep(edge, dependency_, inputVar_, node.filter, true, &subplan)); // plan->tail = subplan.tail; PlanNode* passThrough = subplan.root; auto maxHop = edge.range ? edge.range->max() : 1; for (int64_t i = 1; i < maxHop; ++i) { SubPlan curr; - NG_RETURN_IF_ERROR(expandStep(edge, passThrough, nullptr, false, &curr)); + NG_RETURN_IF_ERROR( + expandStep(edge, passThrough, passThrough->outputVar(), nullptr, false, &curr)); auto rNode = subplan.root; DCHECK(rNode->kind() == PNKind::kUnion || rNode->kind() == PNKind::kPassThrough); NG_RETURN_IF_ERROR(collectData(passThrough, curr.root, rNode, &passThrough, &subplan)); @@ -104,17 +112,17 @@ Status Expand::expandSteps(const NodeInfo& node, // build subplan: Project->Dedup->GetNeighbors->[Filter]->Project Status Expand::expandStep(const EdgeInfo& edge, - const PlanNode* input, + PlanNode* dep, + const std::string& inputVar, const Expression* nodeFilter, bool needPassThrough, SubPlan* plan) { - DCHECK(input != nullptr); auto qctx = matchCtx_->qctx; // Extract dst vid from input project node which output dataset format is: [v1,e1,...,vn,en] SubPlan curr; - curr.root = const_cast<PlanNode*>(input); - MatchSolver::extractAndDedupVidColumn(qctx, initialExpr_, &curr); + curr.root = dep; + MatchSolver::extractAndDedupVidColumn(qctx, initialExpr_.release(), dep, inputVar, curr); auto gn = GetNeighbors::make(qctx, curr.root, matchCtx_->space.id); auto srcExpr = ExpressionUtils::inputPropExpr(kVid); @@ -152,7 +160,7 @@ Status Expand::expandStep(const EdgeInfo& edge, auto la = static_cast<const LabelAttributeExpression*>(expr); return new AttributeExpression(new EdgeExpression(), la->right()->clone().release()); }); - auto filter = edge.filter->clone().release(); + auto filter = saveObject(edge.filter->clone().release()); filter->accept(&visitor); auto filterNode = Filter::make(qctx, root, filter); filterNode->setColNames(root->colNames()); @@ -230,30 +238,5 @@ Status Expand::filterDatasetByPathLength(const EdgeInfo& edge, // plan->tail = curr.tail; return Status::OK(); } - -StatusOr<std::vector<storage::cpp2::EdgeProp>> Expand::buildAllEdgeProp() { - // list all edge properties - std::map<TagID, std::shared_ptr<const meta::SchemaProviderIf>> edgesSchema; - const auto allEdgesResult = matchCtx_->qctx->schemaMng()->getAllVerEdgeSchema( - matchCtx_->space.id); - NG_RETURN_IF_ERROR(allEdgesResult); - const auto allEdges = std::move(allEdgesResult).value(); - for (const auto &edge : allEdges) { - edgesSchema.emplace(edge.first, edge.second.back()); - } - std::vector<storage::cpp2::EdgeProp> eProps; - for (const auto &edgeSchema : edgesSchema) { - storage::cpp2::EdgeProp eProp; - eProp.set_type(edgeSchema.first); - std::vector<std::string> props{kSrc, kType, kRank, kDst}; - for (std::size_t i = 0; i < edgeSchema.second->getNumFields(); ++i) { - props.emplace_back(edgeSchema.second->getFieldName(i)); - } - eProp.set_props(std::move(props)); - eProps.emplace_back(std::move(eProp)); - } - return eProps; -} - } // namespace graph } // namespace nebula diff --git a/src/planner/match/Expand.h b/src/planner/match/Expand.h index 09e255c591cff29f87b7da8d4d9fede5d32ba41d..ba4e9cfc23bf28cf68f0d337284476101566fb45 100644 --- a/src/planner/match/Expand.h +++ b/src/planner/match/Expand.h @@ -19,21 +19,36 @@ namespace graph { */ class Expand final { public: - Expand(MatchClauseContext* matchCtx, Expression** initialExpr) - : matchCtx_(matchCtx), initialExpr_(initialExpr) {} + Expand(MatchClauseContext* matchCtx, std::unique_ptr<Expression> initialExpr) + : matchCtx_(matchCtx), initialExpr_(std::move(initialExpr)) {} + + Expand* reversely() { + reversely_ = true; + return this; + } + + Expand* depends(PlanNode* dep) { + dependency_ = dep; + return this; + } + + Expand* inputVar(const std::string& inputVar) { + inputVar_ = inputVar; + return this; + } Status doExpand(const NodeInfo& node, const EdgeInfo& edge, - const PlanNode* input, SubPlan* plan); +private: Status expandSteps(const NodeInfo& node, const EdgeInfo& edge, - const PlanNode* input, SubPlan* plan); Status expandStep(const EdgeInfo& edge, - const PlanNode* input, + PlanNode* dep, + const std::string& inputVar, const Expression* nodeFilter, bool needPassThrough, SubPlan* plan); @@ -46,22 +61,18 @@ public: Status filterDatasetByPathLength(const EdgeInfo& edge, PlanNode* input, SubPlan* plan); - Expression* initialExprOrEdgeDstExpr(const PlanNode* node); - - PlanNode* joinDataSet(const PlanNode* right, const PlanNode* left); - template <typename T> T* saveObject(T* obj) const { return matchCtx_->qctx->objPool()->add(obj); } -private: std::unique_ptr<std::vector<storage::cpp2::EdgeProp>> genEdgeProps(const EdgeInfo &edge); - StatusOr<std::vector<storage::cpp2::EdgeProp>> buildAllEdgeProp(); - - MatchClauseContext* matchCtx_; - Expression** initialExpr_; + MatchClauseContext* matchCtx_; + std::unique_ptr<Expression> initialExpr_; + bool reversely_{false}; + PlanNode* dependency_{nullptr}; + std::string inputVar_; }; } // namespace graph } // namespace nebula diff --git a/src/planner/match/InnerJoinStrategy.cpp b/src/planner/match/InnerJoinStrategy.cpp index ca26ff502db194f918e2e8d4ecff76420e99cdb0..9815301bc94208df94b6fd855800f7f608db0e5c 100644 --- a/src/planner/match/InnerJoinStrategy.cpp +++ b/src/planner/match/InnerJoinStrategy.cpp @@ -18,10 +18,24 @@ PlanNode* InnerJoinStrategy::connect(const PlanNode* left, const PlanNode* right } PlanNode* InnerJoinStrategy::joinDataSet(const PlanNode* left, const PlanNode* right) { - auto& leftKey = left->colNamesRef().back(); - auto& rightKey = right->colNamesRef().front(); - auto buildExpr = MatchSolver::getLastEdgeDstExprInLastPath(leftKey); - auto probeExpr = MatchSolver::getFirstVertexVidInFistPath(rightKey); + Expression* buildExpr = nullptr; + if (leftPos_ == JoinPos::kStart) { + auto& leftKey = left->colNamesRef().front(); + buildExpr = MatchSolver::getStartVidInPath(leftKey); + } else { + auto& leftKey = left->colNamesRef().back(); + buildExpr = MatchSolver::getEndVidInPath(leftKey); + } + + Expression* probeExpr = nullptr; + if (rightPos_ == JoinPos::kStart) { + auto& rightKey = right->colNamesRef().front(); + probeExpr = MatchSolver::getStartVidInPath(rightKey); + } else { + auto& rightKey = right->colNamesRef().back(); + probeExpr = MatchSolver::getEndVidInPath(rightKey); + } + qctx_->objPool()->add(buildExpr); qctx_->objPool()->add(probeExpr); auto join = DataJoin::make(qctx_, diff --git a/src/planner/match/InnerJoinStrategy.h b/src/planner/match/InnerJoinStrategy.h index 49f4034dd2596ebc67ed9384c4c4109c32e82d20..c11ece57181b6dda1f343531d6025b92230ea364 100644 --- a/src/planner/match/InnerJoinStrategy.h +++ b/src/planner/match/InnerJoinStrategy.h @@ -7,8 +7,8 @@ #ifndef PLANNER_PLANNERS_MATCH_INNERJOINSTRATEGY_H_ #define PLANNER_PLANNERS_MATCH_INNERJOINSTRATEGY_H_ -#include "planner/match/SegmentsConnector.h" #include "planner/PlanNode.h" +#include "planner/match/SegmentsConnectStrategy.h" namespace nebula { namespace graph { @@ -17,12 +17,30 @@ namespace graph { */ class InnerJoinStrategy final : public SegmentsConnectStrategy { public: + enum class JoinPos : int8_t { + kStart, + kEnd + }; + explicit InnerJoinStrategy(QueryContext* qctx) : SegmentsConnectStrategy(qctx) {} + InnerJoinStrategy* leftPos(JoinPos pos) { + leftPos_ = pos; + return this; + } + + InnerJoinStrategy* rightPos(JoinPos pos) { + rightPos_ = pos; + return this; + } + PlanNode* connect(const PlanNode* left, const PlanNode* right) override; private: PlanNode* joinDataSet(const PlanNode* left, const PlanNode* right); + + JoinPos leftPos_{JoinPos::kEnd}; + JoinPos rightPos_{JoinPos::kStart}; }; } // namespace graph } // namespace nebula diff --git a/src/planner/match/MatchClausePlanner.cpp b/src/planner/match/MatchClausePlanner.cpp index fa22755603469b7101ad34f847c5f79fc07da266..08b5f3ba027c55dde22826dc3495eccaebfae854 100644 --- a/src/planner/match/MatchClausePlanner.cpp +++ b/src/planner/match/MatchClausePlanner.cpp @@ -18,6 +18,7 @@ namespace nebula { namespace graph { + StatusOr<SubPlan> MatchClausePlanner::transform(CypherClauseContextBase* clauseCtx) { if (clauseCtx->kind != CypherClauseKind::kMatch) { return Status::Error("Not a valid context for MatchClausePlanner."); @@ -27,34 +28,39 @@ StatusOr<SubPlan> MatchClausePlanner::transform(CypherClauseContextBase* clauseC auto& nodeInfos = matchClauseCtx->nodeInfos; auto& edgeInfos = matchClauseCtx->edgeInfos; SubPlan matchClausePlan; - int64_t startIndex = -1; + size_t startIndex = 0; + bool startFromEdge = false; - NG_RETURN_IF_ERROR(findStarts(matchClauseCtx, startIndex, matchClausePlan)); - NG_RETURN_IF_ERROR(expand(nodeInfos, edgeInfos, matchClauseCtx, startIndex, matchClausePlan)); - NG_RETURN_IF_ERROR(projectColumnsBySymbols(matchClauseCtx, &matchClausePlan)); + NG_RETURN_IF_ERROR(findStarts(matchClauseCtx, startFromEdge, startIndex, matchClausePlan)); + NG_RETURN_IF_ERROR( + expand(nodeInfos, edgeInfos, matchClauseCtx, startFromEdge, startIndex, matchClausePlan)); + NG_RETURN_IF_ERROR(projectColumnsBySymbols(matchClauseCtx, startIndex, matchClausePlan)); NG_RETURN_IF_ERROR(appendFilterPlan(matchClauseCtx, matchClausePlan)); return matchClausePlan; } Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx, - int64_t& startIndex, + bool& startFromEdge, + size_t& startIndex, SubPlan& matchClausePlan) { auto& nodeInfos = matchClauseCtx->nodeInfos; auto& edgeInfos = matchClauseCtx->edgeInfos; auto& startVidFinders = StartVidFinder::finders(); + bool foundStart = false; // Find the start plan node - for (size_t i = 0; i < nodeInfos.size(); ++i) { - for (auto& finder : startVidFinders) { + for (auto& finder : startVidFinders) { + for (size_t i = 0; i < nodeInfos.size() && !foundStart; ++i) { auto nodeCtx = NodeContext(matchClauseCtx, &nodeInfos[i]); - auto finderObj = finder(); - if (finderObj->match(&nodeCtx)) { - auto plan = finderObj->transform(&nodeCtx); + auto nodeFinder = finder(); + if (nodeFinder->match(&nodeCtx)) { + auto plan = nodeFinder->transform(&nodeCtx); if (!plan.ok()) { return plan.status(); } matchClausePlan = std::move(plan).value(); startIndex = i; - initialExpr_ = nodeCtx.initialExpr; + foundStart = true; + initialExpr_ = nodeCtx.initialExpr->clone(); VLOG(1) << "Find starts: " << startIndex << " node: " << matchClausePlan.root->outputVar() << " colNames: " << folly::join(",", matchClausePlan.root->colNames()); @@ -63,22 +69,25 @@ Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx, if (i != nodeInfos.size() - 1) { auto edgeCtx = EdgeContext(matchClauseCtx, &edgeInfos[i]); - if (finderObj->match(&edgeCtx)) { - auto plan = finderObj->transform(&edgeCtx); + auto edgeFinder = finder(); + if (edgeFinder->match(&edgeCtx)) { + auto plan = edgeFinder->transform(&edgeCtx); if (!plan.ok()) { return plan.status(); } matchClausePlan = std::move(plan).value(); + startFromEdge = true; startIndex = i; + foundStart = true; break; } } } - if (startIndex != -1) { + if (foundStart) { break; } } - if (startIndex < 0) { + if (!foundStart) { return Status::Error("Can't solve the start vids from the sentence: %s", matchClauseCtx->sentence->toString().c_str()); } @@ -89,23 +98,121 @@ Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx, Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos, const std::vector<EdgeInfo>& edgeInfos, MatchClauseContext* matchClauseCtx, - int64_t startIndex, + bool startFromEdge, + size_t startIndex, SubPlan& subplan) { - // Do expand from startIndex and connect the the subplans. - // TODO: Only support start from the head node now. - if (startIndex != 0) { - return Status::Error("Only support start from the head node parttern."); + if (startFromEdge) { + return expandFromEdge(nodeInfos, edgeInfos, matchClauseCtx, startIndex, subplan); + } else { + return expandFromNode(nodeInfos, edgeInfos, matchClauseCtx, startIndex, subplan); } +} + +Status MatchClausePlanner::expandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan) { + SubPlan rightExpandPlan = subplan; + NG_RETURN_IF_ERROR( + rightExpandFromNode(nodeInfos, edgeInfos, matchClauseCtx, startIndex, rightExpandPlan)); - std::vector<std::string> joinColNames = {folly::stringPrintf("%s_%d", kPathStr, 0)}; - for (size_t i = 0; i < edgeInfos.size(); ++i) { + if (startIndex > 0) { + auto left = rightExpandPlan.root; + SubPlan leftExpandPlan = rightExpandPlan; + NG_RETURN_IF_ERROR(leftExpandFromNode(nodeInfos, + edgeInfos, + matchClauseCtx, + startIndex, + subplan.root->outputVar(), + leftExpandPlan)); + rightExpandPlan.root = leftExpandPlan.root; + if (startIndex < nodeInfos.size() - 1) { + // Connect the left expand and right expand part. + auto right = leftExpandPlan.root; + rightExpandPlan.root = + SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx, + left, + right, + InnerJoinStrategy::JoinPos::kStart, + InnerJoinStrategy::JoinPos::kStart); + } + } + subplan = rightExpandPlan; + return Status::OK(); +} + +Status MatchClausePlanner::leftExpandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + std::string inputVar, + SubPlan& subplan) { + std::vector<std::string> joinColNames = { + folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size())}; + for (size_t i = startIndex; i > 0; --i) { auto left = subplan.root; - auto status = std::make_unique<Expand>(matchClauseCtx, &initialExpr_) - ->doExpand(nodeInfos[i], edgeInfos[i], subplan.root, &subplan); + auto status = std::make_unique<Expand>(matchClauseCtx, + i == startIndex ? initialExpr_->clone() : nullptr) + ->depends(subplan.root) + ->inputVar(inputVar) + ->reversely() + ->doExpand(nodeInfos[i], edgeInfos[i - 1], &subplan); if (!status.ok()) { return status; } - if (i > 0) { + if (i < startIndex) { + auto right = subplan.root; + VLOG(1) << "left: " << folly::join(",", left->colNames()) + << " right: " << folly::join(",", right->colNames()); + subplan.root = SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx, left, right); + joinColNames.emplace_back( + folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size() + i)); + subplan.root->setColNames(joinColNames); + } + inputVar = subplan.root->outputVar(); + } + + VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar(); + auto left = subplan.root; + NG_RETURN_IF_ERROR( + appendFetchVertexPlan(nodeInfos.front().filter, + matchClauseCtx->space, + matchClauseCtx->qctx, + edgeInfos.empty() ? initialExpr_->clone().release() : nullptr, + subplan)); + if (!edgeInfos.empty()) { + auto right = subplan.root; + VLOG(1) << "left: " << folly::join(",", left->colNames()) + << " right: " << folly::join(",", right->colNames()); + subplan.root = SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx, left, right); + joinColNames.emplace_back( + folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size() + startIndex)); + subplan.root->setColNames(joinColNames); + } + + VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar(); + return Status::OK(); +} + +Status MatchClausePlanner::rightExpandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan) { + std::vector<std::string> joinColNames = {folly::stringPrintf("%s_%lu", kPathStr, startIndex)}; + for (size_t i = startIndex; i < edgeInfos.size(); ++i) { + auto left = subplan.root; + auto status = + std::make_unique<Expand>(matchClauseCtx, + i == startIndex ? initialExpr_->clone() : nullptr) + ->depends(subplan.root) + ->inputVar(subplan.root->outputVar()) + ->doExpand(nodeInfos[i], edgeInfos[i], &subplan); + if (!status.ok()) { + return status; + } + if (i > startIndex) { auto right = subplan.root; VLOG(1) << "left: " << folly::join(",", left->colNames()) << " right: " << folly::join(",", right->colNames()); @@ -117,8 +224,12 @@ Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos, VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar(); auto left = subplan.root; - NG_RETURN_IF_ERROR(appendFetchVertexPlan( - nodeInfos.back().filter, matchClauseCtx->qctx, matchClauseCtx->space, &subplan)); + NG_RETURN_IF_ERROR( + appendFetchVertexPlan(nodeInfos.back().filter, + matchClauseCtx->space, + matchClauseCtx->qctx, + edgeInfos.empty() ? initialExpr_->clone().release() : nullptr, + subplan)); if (!edgeInfos.empty()) { auto right = subplan.root; VLOG(1) << "left: " << folly::join(",", left->colNames()) @@ -132,14 +243,29 @@ Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos, return Status::OK(); } +Status MatchClausePlanner::expandFromEdge(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan) { + UNUSED(nodeInfos); + UNUSED(edgeInfos); + UNUSED(matchClauseCtx); + UNUSED(startIndex); + UNUSED(subplan); + return Status::Error("Expand from edge has not been implemented yet."); +} + Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter, + const SpaceInfo& space, QueryContext* qctx, - SpaceInfo& space, - SubPlan* plan) { - MatchSolver::extractAndDedupVidColumn(qctx, &initialExpr_, plan); + Expression* initialExpr, + SubPlan& plan) { + MatchSolver::extractAndDedupVidColumn( + qctx, initialExpr, plan.root, plan.root->outputVar(), plan); auto srcExpr = ExpressionUtils::inputPropExpr(kVid); auto gv = GetVertices::make( - qctx, plan->root, space.id, qctx->objPool()->add(srcExpr.release()), {}, {}); + qctx, plan.root, space.id, qctx->objPool()->add(srcExpr.release()), {}, {}); PlanNode* root = gv; if (nodeFilter != nullptr) { @@ -158,6 +284,7 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter, return new VertexExpression(); }); filter->accept(&visitor); + qctx->objPool()->add(filter); root = Filter::make(qctx, root, filter); } @@ -166,35 +293,54 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter, auto pathExpr = std::make_unique<PathBuildExpression>(); pathExpr->add(std::make_unique<VertexExpression>()); columns->addColumn(new YieldColumn(pathExpr.release())); - plan->root = Project::make(qctx, root, columns); - plan->root->setColNames({kPathStr}); + plan.root = Project::make(qctx, root, columns); + plan.root->setColNames({kPathStr}); return Status::OK(); } Status MatchClausePlanner::projectColumnsBySymbols(MatchClauseContext* matchClauseCtx, - SubPlan* plan) { + size_t startIndex, + SubPlan& plan) { auto qctx = matchClauseCtx->qctx; auto& nodeInfos = matchClauseCtx->nodeInfos; auto& edgeInfos = matchClauseCtx->edgeInfos; - auto columns = qctx->objPool()->add(new YieldColumns); - auto input = plan->root; + auto input = plan.root; const auto& inColNames = input->colNamesRef(); - DCHECK_EQ(inColNames.size(), nodeInfos.size()); + auto columns = qctx->objPool()->add(new YieldColumns); std::vector<std::string> colNames; auto addNode = [&, this](size_t i) { auto& nodeInfo = nodeInfos[i]; if (nodeInfo.alias != nullptr && !nodeInfo.anonymous) { - columns->addColumn(buildVertexColumn(inColNames[i], *nodeInfo.alias)); + if (i >= startIndex) { + columns->addColumn( + buildVertexColumn(inColNames[i - startIndex], *nodeInfo.alias)); + } else if (startIndex == (nodeInfos.size() - 1)) { + columns->addColumn(buildVertexColumn( + inColNames[startIndex - i], *nodeInfo.alias)); + } else { + columns->addColumn(buildVertexColumn( + inColNames[nodeInfos.size() - i], *nodeInfo.alias)); + } colNames.emplace_back(*nodeInfo.alias); } }; for (size_t i = 0; i < edgeInfos.size(); i++) { + VLOG(1) << "colSize: " << inColNames.size() << "i: " << i + << " nodesize: " << nodeInfos.size() << " start: " << startIndex; addNode(i); auto& edgeInfo = edgeInfos[i]; if (edgeInfo.alias != nullptr && !edgeInfo.anonymous) { - columns->addColumn(buildEdgeColumn(inColNames[i], edgeInfo)); + if (i >= startIndex) { + columns->addColumn(buildEdgeColumn(inColNames[i - startIndex], edgeInfo)); + } else if (startIndex == (nodeInfos.size() - 1)) { + columns->addColumn( + buildEdgeColumn(inColNames[edgeInfos.size() - 1 - i], edgeInfo)); + } else { + columns->addColumn( + buildEdgeColumn(inColNames[edgeInfos.size() - i], edgeInfo)); + } colNames.emplace_back(*edgeInfo.alias); } } @@ -207,15 +353,16 @@ Status MatchClausePlanner::projectColumnsBySymbols(MatchClauseContext* matchClau auto iter = std::find_if(aliases.begin(), aliases.end(), [](const auto& alias) { return alias.second == AliasType::kPath; }); - std::string alias = iter != aliases.end() ? iter->first : qctx->vctx()->anonColGen()->getCol(); - columns->addColumn(buildPathColumn(alias, input)); + std::string alias = + iter != aliases.end() ? iter->first : qctx->vctx()->anonColGen()->getCol(); + columns->addColumn(buildPathColumn(alias, startIndex, inColNames, nodeInfos.size())); colNames.emplace_back(alias); auto project = Project::make(qctx, input, columns); project->setColNames(std::move(colNames)); - plan->root = MatchSolver::filtPathHasSameEdge(project, alias, qctx); - VLOG(1) << "root: " << plan->root->outputVar() << " tail: " << plan->tail->outputVar(); + plan.root = MatchSolver::filtPathHasSameEdge(project, alias, qctx); + VLOG(1) << "root: " << plan.root->outputVar() << " tail: " << plan.tail->outputVar(); return Status::OK(); } @@ -250,12 +397,45 @@ YieldColumn* MatchClausePlanner::buildEdgeColumn(const std::string& colName, Edg } YieldColumn* MatchClausePlanner::buildPathColumn(const std::string& alias, - const PlanNode* input) const { - auto pathExpr = std::make_unique<PathBuildExpression>(); - for (const auto& colName : input->colNamesRef()) { - pathExpr->add(ExpressionUtils::inputPropExpr(colName)); + size_t startIndex, + const std::vector<std::string> colNames, + size_t nodeInfoSize) const { + auto colSize = colNames.size(); + DCHECK((nodeInfoSize == colSize) || (nodeInfoSize + 1 == colSize)); + size_t bound = 0; + if (colSize > nodeInfoSize) { + bound = colSize - startIndex - 1; + } else if (startIndex == (nodeInfoSize - 1)) { + bound = 0; + } else { + bound = colSize - startIndex; + } + + auto rightExpandPath = std::make_unique<PathBuildExpression>(); + for (size_t i = 0; i < bound; ++i) { + rightExpandPath->add(ExpressionUtils::inputPropExpr(colNames[i])); + } + + auto leftExpandPath = std::make_unique<PathBuildExpression>(); + for (size_t i = bound; i < colNames.size(); ++i) { + leftExpandPath->add(ExpressionUtils::inputPropExpr(colNames[i])); + } + + auto finalPath = std::make_unique<PathBuildExpression>(); + if (leftExpandPath->size() != 0) { + auto args = new ArgumentList(); + args->addArgument(std::move(leftExpandPath)); + auto reversePath = + std::make_unique<FunctionCallExpression>(new std::string("reversePath"), args); + if (rightExpandPath->size() == 0) { + return new YieldColumn(reversePath.release(), new std::string(alias)); + } + finalPath->add(std::move(reversePath)); + } + if (rightExpandPath->size() != 0) { + finalPath->add(std::move(rightExpandPath)); } - return new YieldColumn(pathExpr.release(), new std::string(alias)); + return new YieldColumn(finalPath.release(), new std::string(alias)); } Status MatchClausePlanner::appendFilterPlan(MatchClauseContext* matchClauseCtx, SubPlan& subplan) { diff --git a/src/planner/match/MatchClausePlanner.h b/src/planner/match/MatchClausePlanner.h index cd88e3dede23eebb84acd62847f46ca68a0eedb3..a726c3a2e438c1a97558100585d2775011a8972f 100644 --- a/src/planner/match/MatchClausePlanner.h +++ b/src/planner/match/MatchClausePlanner.h @@ -22,32 +22,67 @@ public: private: Status findStarts(MatchClauseContext* matchClauseCtx, - int64_t& startIndex, + bool& startFromEdge, + size_t& startIndex, SubPlan& matchClausePlan); Status expand(const std::vector<NodeInfo>& nodeInfos, const std::vector<EdgeInfo>& edgeInfos, MatchClauseContext* matchClauseCtx, - int64_t startIndex, + bool startFromEdge, + size_t startIndex, SubPlan& subplan); + Status expandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan); + + PlanNode* joinLeftAndRightExpandPart(QueryContext* qctx, PlanNode* left, PlanNode* right); + + Status leftExpandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + std::string inputVar, + SubPlan& subplan); + + Status rightExpandFromNode(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan); + + Status expandFromEdge(const std::vector<NodeInfo>& nodeInfos, + const std::vector<EdgeInfo>& edgeInfos, + MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& subplan); + Status appendFetchVertexPlan(const Expression* nodeFilter, + const SpaceInfo& space, QueryContext* qctx, - SpaceInfo& space, - SubPlan* plan); + Expression* initialExpr, + SubPlan& plan); - Status projectColumnsBySymbols(MatchClauseContext* matchClauseCtx, SubPlan *plan); + Status projectColumnsBySymbols(MatchClauseContext* matchClauseCtx, + size_t startIndex, + SubPlan& plan); YieldColumn* buildVertexColumn(const std::string& colName, const std::string& alias) const; YieldColumn* buildEdgeColumn(const std::string& colName, EdgeInfo& edge) const; - YieldColumn* buildPathColumn(const std::string& alias, const PlanNode* input) const; + YieldColumn* buildPathColumn(const std::string& alias, + size_t startIndex, + const std::vector<std::string> colNames, + size_t nodeInfoSize) const; Status appendFilterPlan(MatchClauseContext* matchClauseCtx, SubPlan& subplan); private: - Expression* initialExpr_; + std::unique_ptr<Expression> initialExpr_; }; } // namespace graph } // namespace nebula diff --git a/src/planner/match/MatchSolver.cpp b/src/planner/match/MatchSolver.cpp index e748bbf2716805f73ca1f2eb2cc8811205837e27..d28c8a2e39c8634005c793a8b3922195e55316ce 100644 --- a/src/planner/match/MatchSolver.cpp +++ b/src/planner/match/MatchSolver.cpp @@ -162,33 +162,33 @@ Status MatchSolver::buildFilter(const MatchClauseContext* mctx, SubPlan* plan) { } void MatchSolver::extractAndDedupVidColumn(QueryContext* qctx, - Expression** initialExpr, - SubPlan* plan) { + Expression* initialExpr, + PlanNode* dep, + const std::string& inputVar, + SubPlan& plan) { auto columns = qctx->objPool()->add(new YieldColumns); - auto input = plan->root; - Expression* vidExpr = initialExprOrEdgeDstExpr(input, initialExpr); + auto* var = qctx->symTable()->getVar(inputVar); + Expression* vidExpr = initialExprOrEdgeDstExpr(initialExpr, var->colNames.back()); columns->addColumn(new YieldColumn(vidExpr)); - auto project = Project::make(qctx, input, columns); + 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; - // plan->tail = dedup; + plan.root = dedup; } -Expression* MatchSolver::initialExprOrEdgeDstExpr(const PlanNode* node, Expression** initialExpr) { - Expression* vidExpr = *initialExpr; - if (vidExpr != nullptr) { - VLOG(1) << vidExpr->toString(); - *initialExpr = nullptr; +Expression* MatchSolver::initialExprOrEdgeDstExpr(Expression* initialExpr, + const std::string& vidCol) { + if (initialExpr != nullptr) { + return initialExpr; } else { - vidExpr = getLastEdgeDstExprInLastPath(node->colNamesRef().back()); + return getEndVidInPath(vidCol); } - return vidExpr; } -Expression* MatchSolver::getLastEdgeDstExprInLastPath(const std::string& colName) { +Expression* MatchSolver::getEndVidInPath(const std::string& colName) { // expr: __Project_2[-1] => path auto columnExpr = ExpressionUtils::inputPropExpr(colName); // expr: endNode(path) => vn @@ -201,7 +201,7 @@ Expression* MatchSolver::getLastEdgeDstExprInLastPath(const std::string& colName return new AttributeExpression(endNode.release(), vidExpr.release()); } -Expression* MatchSolver::getFirstVertexVidInFistPath(const std::string& colName) { +Expression* MatchSolver::getStartVidInPath(const std::string& colName) { // expr: __Project_2[0] => path auto columnExpr = ExpressionUtils::inputPropExpr(colName); // expr: startNode(path) => v1 diff --git a/src/planner/match/MatchSolver.h b/src/planner/match/MatchSolver.h index 5e6cf5a3d80d442ac078a1428c1838690e7a52f6..ddce44c342d3b99413aabcdd75117d1dcad7e9f0 100644 --- a/src/planner/match/MatchSolver.h +++ b/src/planner/match/MatchSolver.h @@ -45,14 +45,17 @@ public: static Status buildFilter(const MatchClauseContext* mctx, SubPlan* plan); static void extractAndDedupVidColumn(QueryContext* qctx, - Expression** initialExpr, - SubPlan* plan); + Expression* initialExpr, + PlanNode* dep, + const std::string& inputVar, + SubPlan& plan); - static Expression* initialExprOrEdgeDstExpr(const PlanNode* node, Expression** initialExpr); + static Expression* initialExprOrEdgeDstExpr(Expression* initialExpr, + const std::string& vidCol); - static Expression* getLastEdgeDstExprInLastPath(const std::string& colName); + static Expression* getEndVidInPath(const std::string& colName); - static Expression* getFirstVertexVidInFistPath(const std::string& colName); + static Expression* getStartVidInPath(const std::string& colName); static PlanNode* filtPathHasSameEdge(PlanNode* input, const std::string& column, diff --git a/src/planner/match/PropIndexSeek.cpp b/src/planner/match/PropIndexSeek.cpp index 2cb25c964fab59a5d31f318301e30343f5b7fcd7..5b42dddcae1bba510d889dc5cf5213e490079a87 100644 --- a/src/planner/match/PropIndexSeek.cpp +++ b/src/planner/match/PropIndexSeek.cpp @@ -74,7 +74,7 @@ StatusOr<SubPlan> PropIndexSeek::transformNode(NodeContext* nodeCtx) { plan.root = scan; // initialize start expression in project node - nodeCtx->initialExpr = ExpressionUtils::newVarPropExpr(kVid); + nodeCtx->initialExpr = std::unique_ptr<Expression>(ExpressionUtils::newVarPropExpr(kVid)); return plan; } diff --git a/src/planner/match/SegmentsConnectStrategy.h b/src/planner/match/SegmentsConnectStrategy.h new file mode 100644 index 0000000000000000000000000000000000000000..c18bbc6a26618872f03d594a36893b38b574f095 --- /dev/null +++ b/src/planner/match/SegmentsConnectStrategy.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_ +#define PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_ + +namespace nebula { +namespace graph { + +class SegmentsConnectStrategy { +public: + explicit SegmentsConnectStrategy(QueryContext* qctx) : qctx_(qctx) {} + + virtual ~SegmentsConnectStrategy() = default; + + virtual PlanNode* connect(const PlanNode* left, const PlanNode* right) = 0; + +protected: + QueryContext* qctx_; +}; +} // namespace graph +} // namespace nebula +#endif // PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_ diff --git a/src/planner/match/SegmentsConnector.cpp b/src/planner/match/SegmentsConnector.cpp index c6c790b05851ff84386f2355b28758960d5e602e..30eea7d56673cab2881b14704e801cc9ee62b9da 100644 --- a/src/planner/match/SegmentsConnector.cpp +++ b/src/planner/match/SegmentsConnector.cpp @@ -7,7 +7,6 @@ #include "planner/match/SegmentsConnector.h" #include "planner/match/AddDependencyStrategy.h" #include "planner/match/AddInputStrategy.h" -#include "planner/match/InnerJoinStrategy.h" #include "planner/match/CartesianProductStrategy.h" namespace nebula { @@ -36,9 +35,14 @@ StatusOr<SubPlan> SegmentsConnector::connectSegments(CypherClauseContextBase* le } PlanNode* SegmentsConnector::innerJoinSegments(QueryContext* qctx, - const PlanNode* left, - const PlanNode* right) { - return std::make_unique<InnerJoinStrategy>(qctx)->connect(left, right); + const PlanNode* left, + const PlanNode* right, + InnerJoinStrategy::JoinPos leftPos, + InnerJoinStrategy::JoinPos rightPos) { + return std::make_unique<InnerJoinStrategy>(qctx) + ->leftPos(leftPos) + ->rightPos(rightPos) + ->connect(left, right); } PlanNode* SegmentsConnector::cartesianProductSegments(QueryContext* qctx, diff --git a/src/planner/match/SegmentsConnector.h b/src/planner/match/SegmentsConnector.h index 39e54871558b9e42cbd175982d282a6ed664a6a2..f664e44090214fdde45f88c493b0d947c34f3e32 100644 --- a/src/planner/match/SegmentsConnector.h +++ b/src/planner/match/SegmentsConnector.h @@ -11,6 +11,7 @@ #include "context/ast/QueryAstContext.h" #include "planner/PlanNode.h" #include "planner/Planner.h" +#include "planner/match/InnerJoinStrategy.h" namespace nebula { namespace graph { @@ -36,9 +37,12 @@ public: SubPlan& left, SubPlan& right); - static PlanNode* innerJoinSegments(QueryContext* qctx, - const PlanNode* left, - const PlanNode* right); + static PlanNode* innerJoinSegments( + QueryContext* qctx, + const PlanNode* left, + const PlanNode* right, + InnerJoinStrategy::JoinPos leftPos = InnerJoinStrategy::JoinPos::kEnd, + InnerJoinStrategy::JoinPos rightPos = InnerJoinStrategy::JoinPos::kStart); static PlanNode* cartesianProductSegments(QueryContext* qctx, const PlanNode* left, @@ -48,18 +52,6 @@ public: static void addInput(const PlanNode* left, const PlanNode* right, bool copyColNames = false); }; - -class SegmentsConnectStrategy { -public: - explicit SegmentsConnectStrategy(QueryContext* qctx) : qctx_(qctx) {} - - virtual ~SegmentsConnectStrategy() = default; - - virtual PlanNode* connect(const PlanNode* left, const PlanNode* right) = 0; - -protected: - QueryContext* qctx_; -}; } // namespace graph } // namespace nebula #endif // PLANNER_MATCH_SEGMENTSCONNECTOR_H_ diff --git a/src/planner/match/VertexIdSeek.cpp b/src/planner/match/VertexIdSeek.cpp index 6d0a30f13f7af7bcdb19dff0f210baf383c52a6b..3e6c82979fdf37c0403d33673c0da40c180648e5 100644 --- a/src/planner/match/VertexIdSeek.cpp +++ b/src/planner/match/VertexIdSeek.cpp @@ -122,7 +122,7 @@ StatusOr<SubPlan> VertexIdSeek::transformNode(NodeContext* nodeCtx) { plan.root = passThrough; plan.tail = passThrough; - nodeCtx->initialExpr = vidsResult.second; + nodeCtx->initialExpr = std::unique_ptr<Expression>(vidsResult.second); VLOG(1) << "root: " << plan.root->kind() << " tail: " << plan.tail->kind(); return plan; } diff --git a/src/validator/MatchValidator.cpp b/src/validator/MatchValidator.cpp index 89ce6dc5b64c3f66e0b2543cb5e110b8f1e59557..497a4ce9de157984e89e60994dadcd11a4e72451 100644 --- a/src/validator/MatchValidator.cpp +++ b/src/validator/MatchValidator.cpp @@ -231,6 +231,16 @@ Status MatchValidator::buildEdgeInfo(const MatchPath *path, edgeInfos[i].edgeTypes.emplace_back(etype.value()); edgeInfos[i].types.emplace_back(*type); } + } else { + const auto allEdgesResult = + matchCtx_->qctx->schemaMng()->getAllVerEdgeSchema(space_.id); + NG_RETURN_IF_ERROR(allEdgesResult); + const auto allEdges = std::move(allEdgesResult).value(); + for (const auto &edgeSchema : allEdges) { + edgeInfos[i].edgeTypes.emplace_back(edgeSchema.first); + // TODO: + // edgeInfos[i].types.emplace_back(*type); + } } auto *stepRange = edge->range(); if (stepRange != nullptr) { diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp index b543e53cada7ca0adc9ebe20215ea01d5866b2dc..084add461315013eaad4a9cf8dd97e598e7f3453 100644 --- a/src/validator/test/QueryValidatorTest.cpp +++ b/src/validator/test/QueryValidatorTest.cpp @@ -1359,6 +1359,81 @@ TEST_F(QueryValidatorTest, TestMatch) { }; EXPECT_TRUE(checkResult(query, expected)); } + { + std::string query = "MATCH (v1)-[e:serve*2..3{start_year: 2000}]-(v2) " + "WHERE id(v1) == \"LeBron James\"" + "RETURN v1, v2"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kFilter, + PK::kFilter, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kDedup, + PK::kProject, + PK::kFilter, + PK::kUnion, + PK::kPassThrough, + PK::kUnion, + PK::kFilter, + PK::kPassThrough, + PK::kPassThrough, + PK::kProject, + PK::kFilter, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kFilter, + PK::kProject, + PK::kDataJoin, + PK::kGetNeighbors, + PK::kFilter, + PK::kProject, + PK::kDedup, + PK::kGetNeighbors, + PK::kFilter, + PK::kProject, + PK::kDedup, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kProject, + PK::kDedup, + PK::kStart, + PK::kProject, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "MATCH p = (n)-[]-(m:person{name:\"LeBron James\"}) RETURN p"; + std::vector<PlanNode::Kind> expected = { + PK::kProject, + PK::kFilter, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kGetVertices, + PK::kDedup, + PK::kProject, + PK::kFilter, + PK::kPassThrough, + PK::kProject, + PK::kFilter, + PK::kGetNeighbors, + PK::kDedup, + PK::kProject, + PK::kDataJoin, + PK::kProject, + PK::kFilter, + PK::kGetVertices, + PK::kDedup, + PK::kProject, + PK::kIndexScan, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } } diff --git a/tests/tck/features/bugfix/MatchNotFilterTheUndeclaredTag.feature b/tests/tck/features/bugfix/MatchNotFilterTheUndeclaredTag.feature new file mode 100644 index 0000000000000000000000000000000000000000..2ff81fadc004d05532c77491524ebcbe3d895545 --- /dev/null +++ b/tests/tck/features/bugfix/MatchNotFilterTheUndeclaredTag.feature @@ -0,0 +1,97 @@ +# Copyright (c) 2020 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License, +# attached with Common Clause Condition 1.0, found in the LICENSES directory. +Feature: Fix match not filter the undeclared tag, pr#501 + + Background: + Given a graph with space named "nba" + + Scenario: [1] one step given tag without property + When executing query: + """ + MATCH (v:player{name:"Tim Duncan"})-->(v2:team) + RETURN v2 AS Team + """ + Then the result should be, in any order: + | Team | + | ("Spurs" :team{name: "Spurs"}) | + + Scenario: [2] one step given tag without property + When executing query: + """ + MATCH (v:team{name:"Spurs"})--(v2) + RETURN v2 AS Player + """ + Then the result should be, in any order: + | Player | + | ("Aron Baynes" :player{age: 32, name: "Aron Baynes"}) | + | ("Boris Diaw" :player{age: 36, name: "Boris Diaw"}) | + | ("Cory Joseph" :player{age: 27, name: "Cory Joseph"}) | + | ("Danny Green" :player{age: 31, name: "Danny Green"}) | + | ("David West" :player{age: 38, name: "David West"}) | + | ("Dejounte Murray" :player{age: 29, name: "Dejounte Murray"}) | + | ("Jonathon Simmons" :player{age: 29, name: "Jonathon Simmons"}) | + | ("Kyle Anderson" :player{age: 25, name: "Kyle Anderson"}) | + | ("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"}) | + | ("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"}) | + | ("Marco Belinelli" :player{age: 32, name: "Marco Belinelli"}) | + | ("Paul Gasol" :player{age: 38, name: "Paul Gasol"}) | + | ("Rudy Gay" :player{age: 32, name: "Rudy Gay"}) | + | ("Tiago Splitter" :player{age: 34, name: "Tiago Splitter"}) | + | ("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"}) | + | ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | + | ("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"}) | + | ("Marco Belinelli" :player{age: 32, name: "Marco Belinelli"}) | + + Scenario: [1] multi steps given tag without property + When executing query: + """ + MATCH (v:player{name: "Tim Duncan"})-->(v2:team)<--(v3) + RETURN v2 AS Team + """ + Then the result should be, in any order: + | Team | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + + Scenario: multi steps given tag without property no direction + When executing query: + """ + MATCH (v:player{name: "Tim Duncan"})--(v2:team)--(v3) + RETURN v2 AS Team + """ + Then the result should be, in any order: + | Team | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | + | ("Spurs" :team{name: "Spurs"}) | diff --git a/tests/tck/features/match/Base.feature b/tests/tck/features/match/Base.feature index 441a53e400442a0c215f95c5e882c56551751a25..cd7ccb52b37f1bf225cab6e7636eea16640032c7 100644 --- a/tests/tck/features/match/Base.feature +++ b/tests/tck/features/match/Base.feature @@ -35,98 +35,3 @@ Feature: Basic match | 'Jonathon Simmons' | | 'Klay Thompson' | | 'Dejounte Murray' | - - Scenario: [1] one step given tag without property - Given a graph with space named "nba" - When executing query: - """ - MATCH (v:player{name:"Tim Duncan"})-->(v2:team) - RETURN v2 AS Team - """ - Then the result should be, in any order: - | Team | - | ("Spurs" :team{name: "Spurs"}) | - - @skip - Scenario: [2] one step given tag without property - When executing query: - """ - MATCH (v:team{name:"Spurs"})--(v2) - RETURN v2 AS Player - """ - Then the result should be, in any order: - | ("Player" ) | - | ("Boris Diaw" :player{name: "Boris Diaw",age: 36} ) | - | ("Kyle Anderson" :player{name: "Kyle Anderson",age: 25} ) | - | ("Cory Joseph" :player{name: "Cory Joseph",age: 27} ) | - | ("Tiago Splitter" :player{name: "Tiago Splitter",age: 34} ) | - | ("LaMarcus Aldridge" :player{name: "LaMarcus Aldridge",age: 33}) | - | ("Paul Gasol" :player{name: "Paul Gasol",age: 38} ) | - | ("Marco Belinelli" :player{name: "Marco Belinelli",age: 32} ) | - | ("Tracy McGrady" :player{name: "Tracy McGrady",age: 39} ) | - | ("David West" :player{name: "David West",age: 38} ) | - | ("Manu Ginobili" :player{name: "Manu Ginobili",age: 41} ) | - | ("Tony Parker" :player{name: "Tony Parker",age: 36} ) | - | ("Rudy Gay" :player{name: "Rudy Gay",age: 32} ) | - | ("Jonathon Simmons" :player{name: "Jonathon Simmons",age: 29} ) | - | ("Aron Baynes" :player{name: "Aron Baynes",age: 32} ) | - | ("Danny Green" :player{name: "Danny Green",age: 31} ) | - | ("Tim Duncan" :player{name: "Tim Duncan",age: 42} ) | - | ("Marco Belinelli" :player{name: "Marco Belinelli",age: 32} ) | - | ("Dejounte Murray" :player{name: "Dejounte Murray",age: 29} ) | - - @skip - Scenario: [1] multi steps given tag without property - Given a graph with space named "nba" - When executing query: - """ - MATCH (v:player{name: "Tim Duncan"})-->(v2:team)<--(v3) - RETURN v2 AS Team - """ - Then the result should be, in any order: - | Team | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - - @skip - Scenario: multi steps given tag without property no direction - Given a graph with space named "nba" - When executing query: - """ - MATCH (v:player{name: "Tim Duncan"})--(v2:team)--(v3) - RETURN v2 AS Team - """ - Then the result should be, in any order: - | Team | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | - | ("Spurs" :team{name: "Spurs"}) | diff --git a/tests/tck/features/match/MatchById.intVid.feature b/tests/tck/features/match/MatchById.IntVid.feature similarity index 99% rename from tests/tck/features/match/MatchById.intVid.feature rename to tests/tck/features/match/MatchById.IntVid.feature index b0e6b13e28a42eff2c8986afffd189f73b843c45..b709098589bf9c5e0cce15eca4cc5ddd43001a4e 100644 --- a/tests/tck/features/match/MatchById.intVid.feature +++ b/tests/tck/features/match/MatchById.IntVid.feature @@ -88,6 +88,7 @@ Feature: Integer Vid Match By Id """ Then the result should be, in any order, with relax comparison: | start | e | end | + | ("Paul George") | [:like "Paul George"<-"Russell Westbrook"] | ("Russell Westbrook") | | ("Paul George") | [:like "Paul George"->"Russell Westbrook"] | ("Russell Westbrook") | | ("Paul George") | [:serve "Paul George"->"Pacers"] | ("Pacers") | | ("Paul George") | [:serve "Paul George"->"Thunders"] | ("Thunders") | diff --git a/tests/tck/features/match/MatchById.feature b/tests/tck/features/match/MatchById.feature index d8db1206491dfc3f2995ef85d6e51f8f3c1fa3f7..d873163b80dbd91554fd0350a88bda96976acd15 100644 --- a/tests/tck/features/match/MatchById.feature +++ b/tests/tck/features/match/MatchById.feature @@ -88,6 +88,7 @@ Feature: Match By Id """ Then the result should be, in any order, with relax comparison: | start | e | end | + | ("Paul George") | [:like "Paul George"<-"Russell Westbrook"] | ("Russell Westbrook") | | ("Paul George") | [:like "Paul George"->"Russell Westbrook"] | ("Russell Westbrook") | | ("Paul George") | [:serve "Paul George"->"Pacers"] | ("Pacers") | | ("Paul George") | [:serve "Paul George"->"Thunders"] | ("Thunders") | diff --git a/tests/tck/features/match/StartFromAnyNode.IntVid.feature b/tests/tck/features/match/StartFromAnyNode.IntVid.feature new file mode 100644 index 0000000000000000000000000000000000000000..b5adb78af28ee79741725ebad929586b1d01672b --- /dev/null +++ b/tests/tck/features/match/StartFromAnyNode.IntVid.feature @@ -0,0 +1,533 @@ +# Copyright (c) 2020 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License, +# attached with Common Clause Condition 1.0, found in the LICENSES directory. +Feature: Start From Any Node + + Background: + Given a graph with space named "nba_int_vid" + + Scenario: start from middle node, with prop index, with totally 2 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l) + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") | + When executing query: + """ + MATCH (n)-[]-(m:player)-[]-(l) + WHERE m.name=="Kyle Anderson" + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l) + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Spurs")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Spurs")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Spurs")> | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + + Scenario: start from middle node, with prop index, with totally 3 steps + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 141 | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + WHERE k.name == "Marc Gasol" + RETURN n, m, l, k + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | k | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + WHERE k.name == "Marc Gasol" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + When executing query: + """ + MATCH p = ()-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-()-[e3]-(k) + WHERE k.name == "Marc Gasol" + RETURN e1, e2, e3 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | + | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + | [:serve "Kyle Anderson"->"Spurs" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 46 | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + WHERE l.name == "Lakers" + RETURN k, n, m, l + """ + Then the result should be, in any order, with relax comparison: + | k | n | m | l | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Vince Carter") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Yao Ming") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Rudy Gay") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Magic") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Raptors") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Rockets") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Spurs") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Bucks") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Bulls") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Grizzlies") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Lakers") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Spurs") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + WHERE l.name == "Lakers" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Grant Hill")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Vince Carter")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Yao Ming")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Grant Hill")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Rudy Gay")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Magic")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Raptors")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Rockets")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Spurs")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Marc Gasol")-[:like@0]->("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Marc Gasol")<-[:like@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Bucks")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Bulls")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Grizzlies")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Lakers")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Spurs")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Kobe Bryant"})-[e3]-(l) + WHERE l.name == "Lakers" + RETURN e1, e2, e3 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | + | [:like "Tracy McGrady"<-"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"<-"Vince Carter" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"<-"Yao Ming" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"->"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"->"Rudy Gay" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Magic" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Raptors" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Rockets" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Spurs" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Paul Gasol"<-"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Paul Gasol"->"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Bucks" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Bulls" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Grizzlies" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Lakers" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Spurs" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + + Scenario: start from middle node, with prop index, with totally 4 steps + When executing query: + """ + MATCH p = ()-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 348 | + When executing query: + """ + MATCH p = ()-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + WHERE k.name == "Paul Gasol" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Grant Hill")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Vince Carter")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Yao Ming")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Grant Hill")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Rudy Gay")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Magic")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Raptors")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Rockets")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Spurs")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Marc Gasol")-[:like@0]->("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Marc Gasol")<-[:like@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Bucks")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Bulls")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Grizzlies")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol" )> | + | <("Spurs")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + When executing query: + """ + MATCH (i)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + WHERE k.name == "Paul Gasol" + RETURN i, n, m, l, k + """ + Then the result should be, in any order, with relax comparison: + | i | n | m | l | k | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Vince Carter") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Yao Ming") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Rudy Gay") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Magic") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Raptors") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Rockets") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Spurs") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Bucks") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Bulls") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Grizzlies") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Spurs") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Kobe Bryant"})-[e3]-()-[e4]-(k) + WHERE k.name == "Paul Gasol" + RETURN e1, e2, e3, e4 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | e4 | + | [:like "Tracy McGrady"<-"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"<-"Vince Carter" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"<-"Yao Ming" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"->"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"->"Rudy Gay" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Magic" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Raptors" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Rockets" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Spurs" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Paul Gasol"<-"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Paul Gasol"->"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Bucks" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Bulls" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Grizzlies" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Spurs" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + + Scenario: start from end node, with prop index, with totally 1 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"}) + RETURN n, m + """ + Then the result should be, in any order, with relax comparison: + | n | m | + | ("Tony Parker") | ("Kyle Anderson") | + | ("Dejounte Murray") | ("Kyle Anderson") | + | ("Grizzlies") | ("Kyle Anderson") | + | ("Spurs") | ("Kyle Anderson") | + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"}) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | m | + | ("Tony Parker") | ("Kyle Anderson") | + | ("Dejounte Murray") | ("Kyle Anderson") | + | ("Grizzlies") | ("Kyle Anderson") | + | ("Spurs") | ("Kyle Anderson") | + + Scenario: start from end node, with prop index, with totally 2 steps + When executing query: + """ + MATCH p = (l)-[]-(n)-[]-(m:player{name:"Stephen Curry"}) + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Warriors")<-[:serve@0]-("Klay Thompson")-[:like@0]->("Stephen Curry")> | + | <("Amar'e Stoudemire")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Dirk Nowitzki")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Jason Kidd")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Amar'e Stoudemire")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Dirk Nowitzki")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Jason Kidd")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Lakers")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Mavericks")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Suns")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Suns")<-[:serve@1]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("David West")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("JaVale McGee")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Kevin Durant")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Klay Thompson")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Marco Belinelli")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + When executing query: + """ + MATCH (l)-[]-(n)-[]-(m:player{name:"Stephen Curry"}) + RETURN l, n, m + """ + Then the result should be, in any order, with relax comparison: + | l | n | m | + | ("Warriors") | ("Klay Thompson") | ("Stephen Curry") | + | ("Amar'e Stoudemire") | ("Steve Nash") | ("Stephen Curry") | + | ("Dirk Nowitzki") | ("Steve Nash") | ("Stephen Curry") | + | ("Jason Kidd") | ("Steve Nash") | ("Stephen Curry") | + | ("Amar'e Stoudemire") | ("Steve Nash") | ("Stephen Curry") | + | ("Dirk Nowitzki") | ("Steve Nash") | ("Stephen Curry") | + | ("Jason Kidd") | ("Steve Nash") | ("Stephen Curry") | + | ("Lakers") | ("Steve Nash") | ("Stephen Curry") | + | ("Mavericks") | ("Steve Nash") | ("Stephen Curry") | + | ("Suns") | ("Steve Nash") | ("Stephen Curry") | + | ("Suns") | ("Steve Nash") | ("Stephen Curry") | + | ("David West") | ("Warriors") | ("Stephen Curry") | + | ("JaVale McGee") | ("Warriors") | ("Stephen Curry") | + | ("Kevin Durant") | ("Warriors") | ("Stephen Curry") | + | ("Klay Thompson") | ("Warriors") | ("Stephen Curry") | + | ("Marco Belinelli") | ("Warriors") | ("Stephen Curry") | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Stephen Curry"}) + RETURN e1, e2 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | + | [:serve "Klay Thompson"->"Warriors" @0] | [:like "Stephen Curry"<-"Klay Thompson" @0] | + | [:like "Steve Nash"<-"Amar'e Stoudemire" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"<-"Dirk Nowitzki" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"<-"Jason Kidd" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Amar'e Stoudemire" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Dirk Nowitzki" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Jason Kidd" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Lakers" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Mavericks" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Suns" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Suns" @1] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Warriors"<-"David West" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"JaVale McGee" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Kevin Durant" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Klay Thompson" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Marco Belinelli" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + + Scenario: start from middle node, with prop index, with upto 3 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"})-[*1..2]-(l) + RETURN n,m,l + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 153 | + + Scenario: start from middle node, with prop index, with upto 4 steps + When executing query: + """ + MATCH (n)-[*1..2]-(m:player{name:"Kyle Anderson"})-[*1..2]-(l) + RETURN n,m,l + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 1846 | + When executing query: + """ + MATCH p = (n)-[*1..2]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 1693 | + + @skip + Scenario: start from middle node, with vertex id, with totally 2 steps + When executing query: + """ + MATCH (n)-[]-(m)-[]-(l) + WHERE id(m)=="Kyle Anderson" + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") | diff --git a/tests/tck/features/match/StartFromAnyNode.feature b/tests/tck/features/match/StartFromAnyNode.feature new file mode 100644 index 0000000000000000000000000000000000000000..725f20e6ec915ba743e5947c9b1b59766888a004 --- /dev/null +++ b/tests/tck/features/match/StartFromAnyNode.feature @@ -0,0 +1,533 @@ +# Copyright (c) 2020 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License, +# attached with Common Clause Condition 1.0, found in the LICENSES directory. +Feature: Start From Any Node + + Background: + Given a graph with space named "nba" + + Scenario: start from middle node, with prop index, with totally 2 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l) + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") | + When executing query: + """ + MATCH (n)-[]-(m:player)-[]-(l) + WHERE m.name=="Kyle Anderson" + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l) + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")<-[:teammate@0]-("Tony Parker")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")<-[:like@0]-("Dejounte Murray")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Grizzlies")> | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Spurs")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Spurs")> | + | <("Grizzlies")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Spurs")> | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]-(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)-[e1]->(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Dejounte Murray") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Tony Parker") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})-[e2]->(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Grizzlies") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Spurs") | + When executing query: + """ + MATCH (n)<-[e1]-(m:player{name:"Kyle Anderson"})<-[e2]-(l) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | e1 | m | e2 | l | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | ("Tony Parker") | + | ("Grizzlies") | [:serve "Kyle Anderson"->"Grizzlies" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + | ("Spurs") | [:serve "Kyle Anderson"->"Spurs" @0] | ("Kyle Anderson") | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | ("Dejounte Murray") | + + Scenario: start from middle node, with prop index, with totally 3 steps + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 141 | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + WHERE k.name == "Marc Gasol" + RETURN n, m, l, k + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | k | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | ("Marc Gasol") | + When executing query: + """ + MATCH p = (n)-[]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + WHERE k.name == "Marc Gasol" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Tony Parker")-[:teammate@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + | <("Dejounte Murray")-[:like@0]->("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + | <("Spurs")<-[:serve@0]-("Kyle Anderson")-[:serve@0]->("Grizzlies")<-[:serve@0]-("Marc Gasol")> | + When executing query: + """ + MATCH p = ()-[e1]-(m:player{name:"Kyle Anderson"})-[e2]-()-[e3]-(k) + WHERE k.name == "Marc Gasol" + RETURN e1, e2, e3 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | + | [:teammate "Kyle Anderson"<-"Tony Parker" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + | [:like "Kyle Anderson"<-"Dejounte Murray" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + | [:serve "Kyle Anderson"->"Spurs" @0] | [:serve "Kyle Anderson"->"Grizzlies" @0] | [:serve "Grizzlies"<-"Marc Gasol" @0] | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 46 | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + WHERE l.name == "Lakers" + RETURN k, n, m, l + """ + Then the result should be, in any order, with relax comparison: + | k | n | m | l | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Vince Carter") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Yao Ming") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Rudy Gay") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Magic") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Raptors") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Rockets") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Spurs") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Bucks") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Bulls") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Grizzlies") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Lakers") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + | ("Spurs") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | + When executing query: + """ + MATCH p = (k)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l) + WHERE l.name == "Lakers" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Grant Hill")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Vince Carter")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Yao Ming")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Grant Hill")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Rudy Gay")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Magic")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Raptors")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Rockets")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Spurs")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Marc Gasol")-[:like@0]->("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Marc Gasol")<-[:like@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Bucks")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Bulls")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Grizzlies")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Lakers")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + | <("Spurs")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")> | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Kobe Bryant"})-[e3]-(l) + WHERE l.name == "Lakers" + RETURN e1, e2, e3 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | + | [:like "Tracy McGrady"<-"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"<-"Vince Carter" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"<-"Yao Ming" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"->"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Tracy McGrady"->"Rudy Gay" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Magic" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Raptors" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Rockets" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Tracy McGrady"->"Spurs" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Paul Gasol"<-"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:like "Paul Gasol"->"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Bucks" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Bulls" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Grizzlies" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Lakers" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + | [:serve "Paul Gasol"->"Spurs" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | + + Scenario: start from middle node, with prop index, with totally 4 steps + When executing query: + """ + MATCH p = ()-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 348 | + When executing query: + """ + MATCH p = ()-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + WHERE k.name == "Paul Gasol" + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Grant Hill")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Vince Carter")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Yao Ming")-[:like@0]->("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Grant Hill")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Rudy Gay")<-[:like@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Magic")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Raptors")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Rockets")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Spurs")<-[:serve@0]-("Tracy McGrady")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Marc Gasol")-[:like@0]->("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Marc Gasol")<-[:like@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Bucks")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Bulls")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + | <("Grizzlies")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol" )> | + | <("Spurs")<-[:serve@0]-("Paul Gasol")-[:like@0]->("Kobe Bryant")-[:serve@0]->("Lakers")<-[:serve@0]-("Paul Gasol")> | + When executing query: + """ + MATCH (i)-[]-(n)-[]-(m:player{name:"Kobe Bryant"})-[]-(l)-[]-(k) + WHERE k.name == "Paul Gasol" + RETURN i, n, m, l, k + """ + Then the result should be, in any order, with relax comparison: + | i | n | m | l | k | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Vince Carter") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Yao Ming") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Grant Hill") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Rudy Gay") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Magic") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Raptors") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Rockets") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Spurs") | ("Tracy McGrady") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Marc Gasol") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Bucks") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Bulls") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Grizzlies") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + | ("Spurs") | ("Paul Gasol") | ("Kobe Bryant") | ("Lakers") | ("Paul Gasol") | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Kobe Bryant"})-[e3]-()-[e4]-(k) + WHERE k.name == "Paul Gasol" + RETURN e1, e2, e3, e4 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | e3 | e4 | + | [:like "Tracy McGrady"<-"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"<-"Vince Carter" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"<-"Yao Ming" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"->"Grant Hill" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Tracy McGrady"->"Rudy Gay" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Magic" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Raptors" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Rockets" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Tracy McGrady"->"Spurs" @0] | [:like "Kobe Bryant"<-"Tracy McGrady" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Paul Gasol"<-"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:like "Paul Gasol"->"Marc Gasol" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Bucks" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Bulls" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Grizzlies" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + | [:serve "Paul Gasol"->"Spurs" @0] | [:like "Kobe Bryant"<-"Paul Gasol" @0] | [:serve "Kobe Bryant"->"Lakers" @0] | [:serve "Lakers"<-"Paul Gasol" @0] | + + Scenario: start from end node, with prop index, with totally 1 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"}) + RETURN n, m + """ + Then the result should be, in any order, with relax comparison: + | n | m | + | ("Tony Parker") | ("Kyle Anderson") | + | ("Dejounte Murray") | ("Kyle Anderson") | + | ("Grizzlies") | ("Kyle Anderson") | + | ("Spurs") | ("Kyle Anderson") | + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"}) + RETURN * + """ + Then the result should be, in any order, with relax comparison: + | n | m | + | ("Tony Parker") | ("Kyle Anderson") | + | ("Dejounte Murray") | ("Kyle Anderson") | + | ("Grizzlies") | ("Kyle Anderson") | + | ("Spurs") | ("Kyle Anderson") | + + Scenario: start from end node, with prop index, with totally 2 steps + When executing query: + """ + MATCH p = (l)-[]-(n)-[]-(m:player{name:"Stephen Curry"}) + RETURN p + """ + Then the result should be, in any order, with relax comparison: + | p | + | <("Warriors")<-[:serve@0]-("Klay Thompson")-[:like@0]->("Stephen Curry")> | + | <("Amar'e Stoudemire")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Dirk Nowitzki")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Jason Kidd")-[:like@0]->("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Amar'e Stoudemire")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Dirk Nowitzki")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Jason Kidd")<-[:like@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Lakers")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Mavericks")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Suns")<-[:serve@0]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("Suns")<-[:serve@1]-("Steve Nash")-[:like@0]->("Stephen Curry")> | + | <("David West")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("JaVale McGee")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Kevin Durant")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Klay Thompson")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + | <("Marco Belinelli")-[:serve@0]->("Warriors")<-[:serve@0]-("Stephen Curry")> | + When executing query: + """ + MATCH (l)-[]-(n)-[]-(m:player{name:"Stephen Curry"}) + RETURN l, n, m + """ + Then the result should be, in any order, with relax comparison: + | l | n | m | + | ("Warriors") | ("Klay Thompson") | ("Stephen Curry") | + | ("Amar'e Stoudemire") | ("Steve Nash") | ("Stephen Curry") | + | ("Dirk Nowitzki") | ("Steve Nash") | ("Stephen Curry") | + | ("Jason Kidd") | ("Steve Nash") | ("Stephen Curry") | + | ("Amar'e Stoudemire") | ("Steve Nash") | ("Stephen Curry") | + | ("Dirk Nowitzki") | ("Steve Nash") | ("Stephen Curry") | + | ("Jason Kidd") | ("Steve Nash") | ("Stephen Curry") | + | ("Lakers") | ("Steve Nash") | ("Stephen Curry") | + | ("Mavericks") | ("Steve Nash") | ("Stephen Curry") | + | ("Suns") | ("Steve Nash") | ("Stephen Curry") | + | ("Suns") | ("Steve Nash") | ("Stephen Curry") | + | ("David West") | ("Warriors") | ("Stephen Curry") | + | ("JaVale McGee") | ("Warriors") | ("Stephen Curry") | + | ("Kevin Durant") | ("Warriors") | ("Stephen Curry") | + | ("Klay Thompson") | ("Warriors") | ("Stephen Curry") | + | ("Marco Belinelli") | ("Warriors") | ("Stephen Curry") | + When executing query: + """ + MATCH ()-[e1]-()-[e2]-(:player{name:"Stephen Curry"}) + RETURN e1, e2 + """ + Then the result should be, in any order, with relax comparison: + | e1 | e2 | + | [:serve "Klay Thompson"->"Warriors" @0] | [:like "Stephen Curry"<-"Klay Thompson" @0] | + | [:like "Steve Nash"<-"Amar'e Stoudemire" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"<-"Dirk Nowitzki" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"<-"Jason Kidd" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Amar'e Stoudemire" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Dirk Nowitzki" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:like "Steve Nash"->"Jason Kidd" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Lakers" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Mavericks" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Suns" @0] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Steve Nash"->"Suns" @1] | [:like "Stephen Curry"<-"Steve Nash" @0] | + | [:serve "Warriors"<-"David West" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"JaVale McGee" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Kevin Durant" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Klay Thompson" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + | [:serve "Warriors"<-"Marco Belinelli" @0] | [:serve "Stephen Curry"->"Warriors" @0] | + + Scenario: start from middle node, with prop index, with upto 3 steps + When executing query: + """ + MATCH (n)-[]-(m:player{name:"Kyle Anderson"})-[*1..2]-(l) + RETURN n,m,l + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 153 | + + Scenario: start from middle node, with prop index, with upto 4 steps + When executing query: + """ + MATCH (n)-[*1..2]-(m:player{name:"Kyle Anderson"})-[*1..2]-(l) + RETURN n,m,l + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 1846 | + When executing query: + """ + MATCH p = (n)-[*1..2]-(m:player{name:"Kyle Anderson"})-[]-(l)-[]-(k) + RETURN p + | YIELD count(*) AS count + """ + Then the result should be, in any order, with relax comparison: + | count | + | 1693 | + + @skip + Scenario: start from middle node, with vertex id, with totally 2 steps + When executing query: + """ + MATCH (n)-[]-(m)-[]-(l) + WHERE id(m)=="Kyle Anderson" + RETURN n,m,l + """ + Then the result should be, in any order, with relax comparison: + | n | m | l | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Tony Parker") | + | ("Grizzlies") | ("Kyle Anderson") | ("Tony Parker") | + | ("Spurs") | ("Kyle Anderson") | ("Tony Parker") | + | ("Tony Parker") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Grizzlies") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Spurs") | ("Kyle Anderson") | ("Dejounte Murray") | + | ("Tony Parker") | ("Kyle Anderson") | ("Grizzlies") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Grizzlies") | + | ("Spurs") | ("Kyle Anderson") | ("Grizzlies") | + | ("Tony Parker") | ("Kyle Anderson") | ("Spurs") | + | ("Dejounte Murray") | ("Kyle Anderson") | ("Spurs") | + | ("Grizzlies") | ("Kyle Anderson") | ("Spurs") |