From ad355b58e9f649555826233316c4d9c40b6d6821 Mon Sep 17 00:00:00 2001
From: jimingquan <mingquan.ji@vesoft.com>
Date: Fri, 2 Apr 2021 15:23:52 +0800
Subject: [PATCH] Remove redundant project&dedup in findpath & fix path error
 (#912)

* remove redundant project&dedup in findpath

* fix path error & add testcase

* add testcase

Co-authored-by: Yee <2520865+yixinglu@users.noreply.github.com>
---
 .gitignore                                    |   3 +
 src/executor/algo/ConjunctPathExecutor.cpp    |  33 +--
 src/executor/algo/ConjunctPathExecutor.h      |  10 +-
 src/validator/FindPathValidator.cpp           | 103 +++------
 src/validator/FindPathValidator.h             |  12 +-
 src/validator/test/FindPathValidatorTest.cpp  | 204 +++---------------
 .../features/path/ShortestPath.IntVid.feature |  38 +++-
 tests/tck/features/path/ShortestPath.feature  |  38 +++-
 8 files changed, 148 insertions(+), 293 deletions(-)

diff --git a/.gitignore b/.gitignore
index c895fcef..6a89523d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,3 +58,6 @@ workspace.*
 *.py[co]
 __pycache__
 venv/
+
+#lock
+*.lock
diff --git a/src/executor/algo/ConjunctPathExecutor.cpp b/src/executor/algo/ConjunctPathExecutor.cpp
index c12879fd..2ccbaf97 100644
--- a/src/executor/algo/ConjunctPathExecutor.cpp
+++ b/src/executor/algo/ConjunctPathExecutor.cpp
@@ -33,6 +33,9 @@ folly::Future<Status> ConjunctPathExecutor::bfsShortestPath() {
             << " right input: " << conjunct->rightInputVar();
     DCHECK(!!lIter);
 
+    auto steps = conjunct->steps();
+    count_++;
+
     DataSet ds;
     ds.colNames = conjunct->colNames();
 
@@ -62,14 +65,16 @@ folly::Future<Status> ConjunctPathExecutor::bfsShortestPath() {
         }
     }
 
-    auto latest = rHist.back().iter();
-    isLatest = true;
-    backward_.emplace_back();
-    VLOG(1) << "Find even length path.";
-    auto rows = findBfsShortestPath(latest.get(), isLatest, forward_.back());
-    if (!rows.empty()) {
-        VLOG(1) << "Meet even length path.";
-        ds.rows = std::move(rows);
+    if (count_ * 2 <= steps) {
+        auto latest = rHist.back().iter();
+        isLatest = true;
+        backward_.emplace_back();
+        VLOG(1) << "Find even length path.";
+        auto rows = findBfsShortestPath(latest.get(), isLatest, forward_.back());
+        if (!rows.empty()) {
+            VLOG(1) << "Meet even length path.";
+            ds.rows = std::move(rows);
+        }
     }
     return finish(ResultBuilder().value(Value(std::move(ds))).finish());
 }
@@ -77,7 +82,7 @@ folly::Future<Status> ConjunctPathExecutor::bfsShortestPath() {
 std::vector<Row> ConjunctPathExecutor::findBfsShortestPath(
     Iterator* iter,
     bool isLatest,
-    std::multimap<Value, const Edge*>& table) {
+    std::unordered_multimap<Value, const Edge*>& table) {
     std::unordered_set<Value> meets;
     for (; iter->valid(); iter->next()) {
         auto& dst = iter->getColumn(kVid);
@@ -118,10 +123,10 @@ std::vector<Row> ConjunctPathExecutor::findBfsShortestPath(
     return rows;
 }
 
-std::multimap<Value, Path> ConjunctPathExecutor::buildBfsInterimPath(
+std::unordered_multimap<Value, Path> ConjunctPathExecutor::buildBfsInterimPath(
     std::unordered_set<Value>& meets,
-    std::vector<std::multimap<Value, const Edge*>>& hists) {
-    std::multimap<Value, Path> results;
+    std::vector<std::unordered_multimap<Value, const Edge*>>& hists) {
+    std::unordered_multimap<Value, Path> results;
     for (auto& v : meets) {
         VLOG(1) << "Meet at: " << v;
         Path start;
@@ -205,7 +210,7 @@ folly::Future<Status> ConjunctPathExecutor::floydShortestPath() {
         findPath(previous.get(), forwardCostPathMap, ds);
     }
 
-    if (count_ * 2 < steps) {
+    if (count_ * 2 <= steps) {
         VLOG(1) << "Find even length path.";
         auto latest = rHist.back().iter();
         findPath(latest.get(), forwardCostPathMap, ds);
@@ -224,7 +229,7 @@ Status ConjunctPathExecutor::conjunctPath(const List& forwardPaths,
         }
         for (auto& j : backwardPaths.values) {
             if (!j.isPath()) {
-                return Status::Error("Forward Path Type Error");
+                return Status::Error("Backward Path Type Error");
             }
             Row row;
             auto forward = i.getPath();
diff --git a/src/executor/algo/ConjunctPathExecutor.h b/src/executor/algo/ConjunctPathExecutor.h
index c2ddabbd..ac866174 100644
--- a/src/executor/algo/ConjunctPathExecutor.h
+++ b/src/executor/algo/ConjunctPathExecutor.h
@@ -33,11 +33,11 @@ private:
 
     std::vector<Row> findBfsShortestPath(Iterator* iter,
                                          bool isLatest,
-                                         std::multimap<Value, const Edge*>& table);
+                                         std::unordered_multimap<Value, const Edge*>& table);
 
-    std::multimap<Value, Path> buildBfsInterimPath(
+    std::unordered_multimap<Value, Path> buildBfsInterimPath(
         std::unordered_set<Value>& meets,
-        std::vector<std::multimap<Value, const Edge*>>& hist);
+        std::vector<std::unordered_multimap<Value, const Edge*>>& hist);
 
     folly::Future<Status> floydShortestPath();
 
@@ -54,8 +54,8 @@ private:
     void delPathFromConditionalVar(const Value& start, const Value& end);
 
 private:
-    std::vector<std::multimap<Value, const Edge*>> forward_;
-    std::vector<std::multimap<Value, const Edge*>> backward_;
+    std::vector<std::unordered_multimap<Value, const Edge*>> forward_;
+    std::vector<std::unordered_multimap<Value, const Edge*>> backward_;
     size_t count_{0};
     // startVid : {endVid, cost}
     std::unordered_map<Value, std::unordered_map<Value, Value>> historyCostMap_;
diff --git a/src/validator/FindPathValidator.cpp b/src/validator/FindPathValidator.cpp
index e7e9cc5c..8e70eb40 100644
--- a/src/validator/FindPathValidator.cpp
+++ b/src/validator/FindPathValidator.cpp
@@ -56,18 +56,14 @@ Status FindPathValidator::singlePairPlan() {
     auto* bodyStart = StartNode::make(qctx_);
     auto* passThrough = PassThroughNode::make(qctx_, bodyStart);
 
-    std::string fromPathVar;
-    auto* forward = bfs(passThrough, from_, fromPathVar, false);
-    VLOG(1) << "forward: " << fromPathVar;
+    auto* forward = bfs(passThrough, from_, false);
+    VLOG(1) << "forward: " << forward->outputVar();
 
-    std::string toPathVar;
-    auto* backward = bfs(passThrough, to_, toPathVar, true);
-    VLOG(1) << "backward: " << toPathVar;
+    auto* backward = bfs(passThrough, to_, true);
+    VLOG(1) << "backward: " << backward->outputVar();
 
     auto* conjunct =
         ConjunctPath::make(qctx_, forward, backward, ConjunctPath::PathKind::kBiBFS, steps_.steps);
-    conjunct->setLeftVar(fromPathVar);
-    conjunct->setRightVar(toPathVar);
     conjunct->setColNames({"_path"});
 
     auto* loop = Loop::make(
@@ -82,45 +78,28 @@ Status FindPathValidator::singlePairPlan() {
     return Status::OK();
 }
 
-PlanNode* FindPathValidator::bfs(PlanNode* dep,
-                                 Starts& starts,
-                                 std::string& pathVar,
-                                 bool reverse) {
+PlanNode* FindPathValidator::bfs(PlanNode* dep, Starts& starts, bool reverse) {
     std::string startVidsVar;
     buildConstantInput(starts, startVidsVar);
-
     auto* gn = GetNeighbors::make(qctx_, dep, space_.id);
     gn->setSrc(starts.src);
     gn->setEdgeProps(buildEdgeKey(reverse));
+    gn->setDedup();
     gn->setInputVar(startVidsVar);
 
     auto* bfs = BFSShortestPath::make(qctx_, gn);
-    bfs->setColNames({"_vid", "edge"});
-    pathVar = bfs->outputVar();
-
-    auto* columns = qctx_->objPool()->add(new YieldColumns());
-    auto* column =
-        new YieldColumn(new VariablePropertyExpression(new std::string("*"), new std::string(kVid)),
-                        new std::string(kVid));
-    columns->addColumn(column);
-    auto* project = Project::make(qctx_, bfs, columns);
-    project->setColNames(deduceColNames(columns));
-
-    auto* dedup = Dedup::make(qctx_, project);
-    auto* startVidsVarPtr = qctx_->symTable()->getVar(startVidsVar);
-    startVidsVarPtr->colNames = project->colNames();
-    dedup->setOutputVar(startVidsVar);
-
+    bfs->setOutputVar(startVidsVar);
+    bfs->setColNames({nebula::kVid, "edge"});
 
     DataSet ds;
-    ds.colNames = {"_vid", "edge"};
+    ds.colNames = {nebula::kVid, "edge"};
     Row row;
     row.values.emplace_back(starts.vids.front());
     row.values.emplace_back(Value::kEmpty);
     ds.rows.emplace_back(std::move(row));
-    qctx_->ectx()->setResult(pathVar, ResultBuilder().value(Value(std::move(ds))).finish());
+    qctx_->ectx()->setResult(startVidsVar, ResultBuilder().value(Value(std::move(ds))).finish());
 
-    return dedup;
+    return bfs;
 }
 
 Expression* FindPathValidator::buildBfsLoopCondition(uint32_t steps, const std::string& pathVar) {
@@ -186,9 +165,7 @@ GetNeighbors::EdgeProps FindPathValidator::buildEdgeKey(bool reverse) {
     return edgeProps;
 }
 
-PlanNode* FindPathValidator::buildAllPairFirstDataSet(PlanNode* dep,
-                                                      const std::string& inputVar,
-                                                      const std::string& outputVar) {
+PlanNode* FindPathValidator::buildAllPairFirstDataSet(PlanNode* dep, const std::string& inputVar) {
     auto* vid =
         new YieldColumn(new VariablePropertyExpression(new std::string("*"), new std::string(kVid)),
                         new std::string(kVid));
@@ -206,9 +183,9 @@ PlanNode* FindPathValidator::buildAllPairFirstDataSet(PlanNode* dep,
 
     auto* project = Project::make(qctx_, dep, columns);
     project->setInputVar(inputVar);
-    auto* outputVarPtr = qctx_->symTable()->getVar(outputVar);
-    outputVarPtr->colNames = {kVid, "path"};
-    project->setOutputVar(outputVar);
+    auto* outputVarPtr = qctx_->symTable()->getVar(inputVar);
+    outputVarPtr->colNames = {nebula::kVid, "path"};
+    project->setOutputVar(inputVar);
     return project;
 }
 
@@ -218,28 +195,25 @@ Status FindPathValidator::allPairPaths() {
 
     std::string fromStartVidsVar;
     buildStart(from_, fromStartVidsVar, false);
-    std::string fromPathVar;
-    auto* forward = allPaths(passThrough, from_, fromStartVidsVar, fromPathVar, false);
-    VLOG(1) << "forward: " << fromPathVar;
+
+    auto* forward = allPaths(passThrough, from_, fromStartVidsVar, false);
+    VLOG(1) << "forward: " << forward->outputVar();
 
     std::string toStartVidsVar;
     buildStart(to_, toStartVidsVar, true);
-    std::string toPathVar;
-    auto* backward = allPaths(passThrough, to_, toStartVidsVar, toPathVar, true);
-    VLOG(1) << "backward: " << toPathVar;
+    auto* backward = allPaths(passThrough, to_, toStartVidsVar, true);
+    VLOG(1) << "backward: " << backward->outputVar();
 
     auto* conjunct = ConjunctPath::make(
         qctx_, forward, backward, ConjunctPath::PathKind::kAllPaths, steps_.steps);
-    conjunct->setLeftVar(fromPathVar);
-    conjunct->setRightVar(toPathVar);
     conjunct->setColNames({"_path"});
     conjunct->setNoLoop(noLoop_);
 
     PlanNode* projectFromDep = nullptr;
     linkLoopDepFromTo(projectFromDep);
 
-    auto* projectFrom = buildAllPairFirstDataSet(projectFromDep, fromStartVidsVar, fromPathVar);
-    auto* projectTo = buildAllPairFirstDataSet(projectFrom, toStartVidsVar, toPathVar);
+    auto* projectFrom = buildAllPairFirstDataSet(projectFromDep, fromStartVidsVar);
+    auto* projectTo = buildAllPairFirstDataSet(projectFrom, toStartVidsVar);
 
     auto* loop =
         Loop::make(qctx_, projectTo, conjunct, buildAllPathsLoopCondition(steps_.steps));
@@ -256,31 +230,17 @@ Status FindPathValidator::allPairPaths() {
 PlanNode* FindPathValidator::allPaths(PlanNode* dep,
                                       Starts& starts,
                                       std::string& startVidsVar,
-                                      std::string& pathVar,
                                       bool reverse) {
     auto* gn = GetNeighbors::make(qctx_, dep, space_.id);
     gn->setSrc(starts.src);
     gn->setEdgeProps(buildEdgeKey(reverse));
     gn->setInputVar(startVidsVar);
+    gn->setDedup();
 
     auto* allPaths = ProduceAllPaths::make(qctx_, gn);
-    allPaths->setColNames({kVid, "path"});
-    pathVar = allPaths->outputVar();
-
-    auto* columns = qctx_->objPool()->add(new YieldColumns());
-    auto* column =
-        new YieldColumn(new VariablePropertyExpression(new std::string("*"), new std::string(kVid)),
-                        new std::string(kVid));
-    columns->addColumn(column);
-    auto* project = Project::make(qctx_, allPaths, columns);
-    project->setColNames(deduceColNames(columns));
-
-    auto* dedup = Dedup::make(qctx_, project);
-    auto* startVidsVarPtr = qctx_->symTable()->getVar(startVidsVar);
-    startVidsVarPtr->colNames = project->colNames();
-    dedup->setOutputVar(startVidsVar);
-
-    return dedup;
+    allPaths->setOutputVar(startVidsVar);
+    allPaths->setColNames({nebula::kVid, "path"});
+    return allPaths;
 }
 
 Expression* FindPathValidator::buildAllPathsLoopCondition(uint32_t steps) {
@@ -419,9 +379,10 @@ PlanNode* FindPathValidator::multiPairShortestPath(PlanNode* dep,
     gn->setSrc(starts.src);
     gn->setEdgeProps(buildEdgeKey(reverse));
     gn->setInputVar(startVidsVar);
+    gn->setDedup();
 
     auto* pssp = ProduceSemiShortestPath::make(qctx_, gn);
-    pssp->setColNames({kDst, kSrc, "cost", "paths"});
+    pssp->setColNames({nebula::kDst, nebula::kSrc, "cost", "paths"});
     pathVar = pssp->outputVar();
 
     auto* columns = qctx_->objPool()->add(new YieldColumns());
@@ -430,14 +391,10 @@ PlanNode* FindPathValidator::multiPairShortestPath(PlanNode* dep,
                         new std::string(kVid));
     columns->addColumn(column);
     auto* project = Project::make(qctx_, pssp, columns);
+    project->setOutputVar(startVidsVar);
     project->setColNames(deduceColNames(columns));
 
-    auto* dedup = Dedup::make(qctx_, project);
-    auto* startVidsVarPtr = qctx_->symTable()->getVar(startVidsVar);
-    startVidsVarPtr->colNames = project->colNames();
-    dedup->setOutputVar(startVidsVar);
-
-    return dedup;
+    return project;
 }
 
 Expression* FindPathValidator::buildMultiPairLoopCondition(uint32_t steps,
diff --git a/src/validator/FindPathValidator.h b/src/validator/FindPathValidator.h
index 07b45b64..6a6b06c3 100644
--- a/src/validator/FindPathValidator.h
+++ b/src/validator/FindPathValidator.h
@@ -28,20 +28,14 @@ private:
     void linkLoopDepFromTo(PlanNode*& projectDep);
     // bfs
     Status singlePairPlan();
-    PlanNode* bfs(PlanNode* dep, Starts& starts, std::string& pathVar, bool reverse);
+    PlanNode* bfs(PlanNode* dep, Starts& starts, bool reverse);
     Expression* buildBfsLoopCondition(uint32_t steps, const std::string& pathVar);
 
     // allPath
     Status allPairPaths();
-    PlanNode* allPaths(PlanNode* dep,
-                       Starts& starts,
-                       std::string& startVidsVar,
-                       std::string& pathVar,
-                       bool reverse);
+    PlanNode* allPaths(PlanNode* dep, Starts& starts, std::string& startVidsVar, bool reverse);
     Expression* buildAllPathsLoopCondition(uint32_t steps);
-    PlanNode* buildAllPairFirstDataSet(PlanNode* dep,
-                                       const std::string& inputVar,
-                                       const std::string& outputVar);
+    PlanNode* buildAllPairFirstDataSet(PlanNode* dep, const std::string& inputVar);
 
     // multi-pair
     Status multiPairPlan();
diff --git a/src/validator/test/FindPathValidatorTest.cpp b/src/validator/test/FindPathValidatorTest.cpp
index 69f74dd2..127f9609 100644
--- a/src/validator/test/FindPathValidatorTest.cpp
+++ b/src/validator/test/FindPathValidatorTest.cpp
@@ -27,10 +27,6 @@ TEST_F(FindPathValidatorTest, SinglePairPath) {
             PK::kLoop,
             PK::kStart,
             PK::kConjunctPath,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
             PK::kBFSShortest,
             PK::kBFSShortest,
             PK::kGetNeighbors,
@@ -47,30 +43,6 @@ TEST_F(FindPathValidatorTest, SinglePairPath) {
             PK::kLoop,
             PK::kStart,
             PK::kConjunctPath,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
-            PK::kBFSShortest,
-            PK::kBFSShortest,
-            PK::kGetNeighbors,
-            PK::kGetNeighbors,
-            PK::kPassThrough,
-            PK::kStart,
-        };
-        EXPECT_TRUE(checkResult(query, expected));
-    }
-    {
-        std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\" OVER * UPTO 5 STEPS";
-        std::vector<PlanNode::Kind> expected = {
-            PK::kDataCollect,
-            PK::kLoop,
-            PK::kStart,
-            PK::kConjunctPath,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
             PK::kBFSShortest,
             PK::kBFSShortest,
             PK::kGetNeighbors,
@@ -91,37 +63,12 @@ TEST_F(FindPathValidatorTest, MultiPairPath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kStart,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kGetNeighbors,
-            PK::kGetNeighbors,
-            PK::kPassThrough,
             PK::kStart,
-        };
-        EXPECT_TRUE(checkResult(query, expected));
-    }
-    {
-        std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\",\"3\" OVER *";
-        std::vector<PlanNode::Kind> expected = {
-            PK::kDataCollect,
-            PK::kLoop,
-            PK::kCartesianProduct,
-            PK::kConjunctPath,
-            PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
-            PK::kProject,
-            PK::kStart,
-            PK::kProduceSemiShortestPath,
-            PK::kProduceSemiShortestPath,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
             PK::kPassThrough,
@@ -138,14 +85,12 @@ TEST_F(FindPathValidatorTest, MultiPairPath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kStart,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
+            PK::kStart,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
             PK::kPassThrough,
@@ -164,35 +109,9 @@ TEST_F(FindPathValidatorTest, ALLPath) {
             PK::kProject,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kStart,
-            PK::kProject,
-            PK::kProject,
             PK::kProduceAllPaths,
             PK::kProduceAllPaths,
-            PK::kGetNeighbors,
-            PK::kGetNeighbors,
-            PK::kPassThrough,
             PK::kStart,
-        };
-        EXPECT_TRUE(checkResult(query, expected));
-    }
-    {
-        std::string query = "FIND ALL PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS";
-        std::vector<PlanNode::Kind> expected = {
-            PK::kDataCollect,
-            PK::kLoop,
-            PK::kProject,
-            PK::kConjunctPath,
-            PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kStart,
-            PK::kProject,
-            PK::kProject,
-            PK::kProduceAllPaths,
-            PK::kProduceAllPaths,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
             PK::kPassThrough,
@@ -201,42 +120,16 @@ TEST_F(FindPathValidatorTest, ALLPath) {
         EXPECT_TRUE(checkResult(query, expected));
     }
     {
-        std::string query = "FIND ALL PATH FROM \"1\",\"2\" TO \"3\",\"4\" OVER like UPTO 5 STEPS";
+        std::string query = "FIND ALL PATH FROM \"1\" TO \"2\",\"3\" OVER like UPTO 5 STEPS";
         std::vector<PlanNode::Kind> expected = {
             PK::kDataCollect,
             PK::kLoop,
             PK::kProject,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kStart,
-            PK::kProject,
-            PK::kProject,
             PK::kProduceAllPaths,
             PK::kProduceAllPaths,
-            PK::kGetNeighbors,
-            PK::kGetNeighbors,
-            PK::kPassThrough,
-            PK::kStart,
-        };
-        EXPECT_TRUE(checkResult(query, expected));
-    }
-    {
-        std::string query = "FIND ALL PATH FROM \"1\" TO \"2\" OVER like, serve UPTO 5 STEPS";
-        std::vector<PlanNode::Kind> expected = {
-            PK::kDataCollect,
-            PK::kLoop,
-            PK::kProject,
-            PK::kConjunctPath,
-            PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kStart,
-            PK::kProject,
-            PK::kProject,
-            PK::kProduceAllPaths,
-            PK::kProduceAllPaths,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
             PK::kPassThrough,
@@ -257,22 +150,20 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kDedup,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kProject,
+            PK::kDedup,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
-            PK::kDedup,
-            PK::kPassThrough,
             PK::kProject,
+            PK::kPassThrough,
+            PK::kDedup,
             PK::kStart,
             PK::kProject,
+            PK::kProject,
             PK::kGetNeighbors,
             PK::kStart,
         };
@@ -288,12 +179,6 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kProject,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
-            PK::kProject,
             PK::kProduceAllPaths,
             PK::kProduceAllPaths,
             PK::kDedup,
@@ -301,8 +186,10 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kGetNeighbors,
             PK::kProject,
             PK::kPassThrough,
-            PK::kProject,
+            PK::kDedup,
             PK::kStart,
+            PK::kProject,
+            PK::kProject,
             PK::kGetNeighbors,
             PK::kStart,
         };
@@ -318,21 +205,19 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kDedup,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kProject,
+            PK::kDedup,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
             PK::kProject,
             PK::kPassThrough,
-            PK::kGetNeighbors,
+            PK::kProject,
             PK::kStart,
+            PK::kGetNeighbors,
             PK::kStart,
         };
         EXPECT_TRUE(checkResult(query, expected));
@@ -347,51 +232,18 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kDedup,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kProject,
-            PK::kGetNeighbors,
-            PK::kGetNeighbors,
-            PK::kProject,
-            PK::kPassThrough,
-            PK::kGetNeighbors,
-            PK::kStart,
-            PK::kStart,
-        };
-        EXPECT_TRUE(checkResult(query, expected));
-    }
-    {
-        std::string query =
-            "$a = GO FROM \"1\" OVER like YIELD like._src AS src, like._dst AS dst; "
-            "FIND SHORTEST PATH FROM $a.src TO $a.dst OVER like, serve UPTO 5 STEPS";
-        std::vector<PlanNode::Kind> expected = {
-            PK::kDataCollect,
-            PK::kLoop,
-            PK::kCartesianProduct,
-            PK::kConjunctPath,
-            PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
-            PK::kProject,
             PK::kDedup,
-            PK::kProduceSemiShortestPath,
-            PK::kProduceSemiShortestPath,
-            PK::kProject,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
-            PK::kDedup,
+            PK::kProject,
             PK::kPassThrough,
             PK::kProject,
             PK::kStart,
-            PK::kProject,
             PK::kGetNeighbors,
             PK::kStart,
         };
@@ -407,22 +259,20 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kDedup,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kProject,
+            PK::kDedup,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
-            PK::kDedup,
-            PK::kPassThrough,
             PK::kProject,
+            PK::kPassThrough,
+            PK::kDedup,
             PK::kStart,
             PK::kProject,
+            PK::kProject,
             PK::kGetNeighbors,
             PK::kProject,
             PK::kGetNeighbors,
@@ -440,22 +290,20 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kCartesianProduct,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
             PK::kProject,
             PK::kProject,
             PK::kProject,
-            PK::kDedup,
             PK::kProduceSemiShortestPath,
             PK::kProduceSemiShortestPath,
-            PK::kProject,
+            PK::kDedup,
             PK::kGetNeighbors,
             PK::kGetNeighbors,
-            PK::kDedup,
-            PK::kPassThrough,
             PK::kProject,
+            PK::kPassThrough,
+            PK::kDedup,
             PK::kStart,
             PK::kProject,
+            PK::kProject,
             PK::kStart,
         };
         EXPECT_TRUE(checkResult(query, expected));
@@ -469,12 +317,6 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kProject,
             PK::kConjunctPath,
             PK::kProject,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kDedup,
-            PK::kProject,
-            PK::kProject,
-            PK::kProject,
             PK::kProduceAllPaths,
             PK::kProduceAllPaths,
             PK::kDedup,
@@ -482,8 +324,10 @@ TEST_F(FindPathValidatorTest, RunTimePath) {
             PK::kGetNeighbors,
             PK::kProject,
             PK::kPassThrough,
-            PK::kProject,
+            PK::kDedup,
             PK::kStart,
+            PK::kProject,
+            PK::kProject,
             PK::kStart,
         };
         EXPECT_TRUE(checkResult(query, expected));
diff --git a/tests/tck/features/path/ShortestPath.IntVid.feature b/tests/tck/features/path/ShortestPath.IntVid.feature
index 7cd51bb7..a2f410b8 100644
--- a/tests/tck/features/path/ShortestPath.IntVid.feature
+++ b/tests/tck/features/path/ShortestPath.IntVid.feature
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 vesoft inc. All rights reserved.
+# Copyright (c) 2021 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.
@@ -52,6 +52,21 @@ Feature: Integer Vid Shortest Path
       | path                                                                            |
       | <("Tiago Splitter")-[:like]->("Tim Duncan")-[:teammate]->("LaMarcus Aldridge")> |
 
+  Scenario: Integer Vid [6] SinglePair Shortest Path limit steps
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM hash("Tiago Splitter") TO hash("Tony Parker") OVER * UPTO 1 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path |
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM hash("Tiago Splitter") TO hash("Tim Duncan") OVER * UPTO 1 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path                                         |
+      | <("Tiago Splitter")-[:like]->("Tim Duncan")> |
+
   Scenario: Integer Vid [1] MultiPair Shortest Path
     When executing query:
       """
@@ -104,6 +119,15 @@ Feature: Integer Vid Shortest Path
       | <("Tony Parker")-[:like]->("Manu Ginobili")>                                                       |
       | <("Tony Parker")-[:teammate]->("Manu Ginobili")>                                                   |
       | <("Tony Parker")-[:serve]->("Spurs")>                                                              |
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM hash("Yao Ming") TO hash("Tim Duncan"), hash("Spurs"), hash("Lakers") OVER * UPTO 2 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path                                                                |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:like]->("Tim Duncan")> |
+      | <("Yao Ming")-[:like]->("Tracy McGrady")-[:serve]->("Spurs")>       |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:serve]->("Lakers")>    |
 
   Scenario: Integer Vid [5] MultiPair Shortest Path
     When executing query:
@@ -390,11 +414,13 @@ Feature: Integer Vid Shortest Path
       FIND SHORTEST PATH FROM hash("Tony Parker"), hash("Yao Ming") TO hash("Manu Ginobili"), hash("Spurs"), hash("Lakers") OVER * BIDIRECT UPTO 2 STEPS
       """
     Then the result should be, in any order, with relax comparison:
-      | path                                             |
-      | <("Tony Parker")-[:serve]->("Spurs")>            |
-      | <("Tony Parker")<-[:teammate]-("Manu Ginobili")> |
-      | <("Tony Parker")-[:like]->("Manu Ginobili")>     |
-      | <("Tony Parker")-[:teammate]->("Manu Ginobili")> |
+      | path                                                             |
+      | <("Yao Ming")-[:like]->("Tracy McGrady")-[:serve]->("Spurs")>    |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:serve]->("Lakers")> |
+      | <("Tony Parker")-[:serve]->("Spurs")>                            |
+      | <("Tony Parker")<-[:teammate]-("Manu Ginobili")>                 |
+      | <("Tony Parker")-[:like]->("Manu Ginobili")>                     |
+      | <("Tony Parker")-[:teammate]->("Manu Ginobili")>                 |
 
   Scenario: Integer Vid [3] Shortest Path BIDIRECT
     When executing query:
diff --git a/tests/tck/features/path/ShortestPath.feature b/tests/tck/features/path/ShortestPath.feature
index 35892b1c..db87d135 100644
--- a/tests/tck/features/path/ShortestPath.feature
+++ b/tests/tck/features/path/ShortestPath.feature
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 vesoft inc. All rights reserved.
+# Copyright (c) 2021 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.
@@ -52,6 +52,21 @@ Feature: Shortest Path
       | path                                                                            |
       | <("Tiago Splitter")-[:like]->("Tim Duncan")-[:teammate]->("LaMarcus Aldridge")> |
 
+  Scenario: [6] SinglePair Shortest Path limit steps
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM "Tiago Splitter" TO "Tony Parker" OVER * UPTO 1 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path |
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM "Tiago Splitter" TO "Tim Duncan" OVER * UPTO 1 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path                                         |
+      | <("Tiago Splitter")-[:like]->("Tim Duncan")> |
+
   Scenario: [1] MultiPair Shortest Path
     When executing query:
       """
@@ -104,6 +119,15 @@ Feature: Shortest Path
       | <("Tony Parker")-[:like]->("Manu Ginobili")>                                                       |
       | <("Tony Parker")-[:teammate]->("Manu Ginobili")>                                                   |
       | <("Tony Parker")-[:serve]->("Spurs")>                                                              |
+    When executing query:
+      """
+      FIND SHORTEST PATH FROM "Yao Ming" TO "Tim Duncan", "Spurs", "Lakers" OVER * UPTO 2 STEPS
+      """
+    Then the result should be, in any order, with relax comparison:
+      | path                                                                |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:like]->("Tim Duncan")> |
+      | <("Yao Ming")-[:like]->("Tracy McGrady")-[:serve]->("Spurs")>       |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:serve]->("Lakers")>    |
 
   Scenario: [5] MultiPair Shortest Path
     When executing query:
@@ -390,11 +414,13 @@ Feature: Shortest Path
       FIND SHORTEST PATH FROM "Tony Parker", "Yao Ming" TO "Manu Ginobili", "Spurs", "Lakers" OVER * BIDIRECT UPTO 2 STEPS
       """
     Then the result should be, in any order, with relax comparison:
-      | path                                             |
-      | <("Tony Parker")-[:serve]->("Spurs")>            |
-      | <("Tony Parker")<-[:teammate]-("Manu Ginobili")> |
-      | <("Tony Parker")-[:like]->("Manu Ginobili")>     |
-      | <("Tony Parker")-[:teammate]->("Manu Ginobili")> |
+      | path                                                             |
+      | <("Yao Ming")-[:like]->("Tracy McGrady")-[:serve]->("Spurs")>    |
+      | <("Yao Ming")-[:like]->("Shaquile O'Neal")-[:serve]->("Lakers")> |
+      | <("Tony Parker")-[:serve]->("Spurs")>                            |
+      | <("Tony Parker")<-[:teammate]-("Manu Ginobili")>                 |
+      | <("Tony Parker")-[:like]->("Manu Ginobili")>                     |
+      | <("Tony Parker")-[:teammate]->("Manu Ginobili")>                 |
 
   Scenario: [3] Shortest Path BIDIRECT
     When executing query:
-- 
GitLab