diff --git a/src/context/Iterator.cpp b/src/context/Iterator.cpp
index 272a56d646c69f809245416b02541e0d676930e2..03f59ae9cac498fcce92443e5774f31b224f4801 100644
--- a/src/context/Iterator.cpp
+++ b/src/context/Iterator.cpp
@@ -170,11 +170,9 @@ Status GetNeighborsIter::buildPropIndex(const std::string& props,
     std::string name = pieces[1];
     if (isEdge) {
         // The first character of the tag/edge name is +/-.
-        // It's not used for now.
         if (UNLIKELY(name.find("+") != 0 && name.find("-") != 0)) {
             return Status::Error("Bad edge name: %s", name.c_str());
         }
-        name = name.substr(1, name.size());
         dsIndex->tagEdgeNameIndices.emplace(columnId, name);
         dsIndex->edgePropsMap.emplace(name, std::move(propIdx));
     } else {
@@ -231,7 +229,8 @@ const Value& GetNeighborsIter::getEdgeProp(const std::string& edge,
     }
 
     auto currentEdge = currentEdgeName();
-    if (edge != "*" && currentEdge != edge) {
+    if (edge != "*" &&
+            (currentEdge.compare(1, std::string::npos, edge) != 0)) {
         VLOG(1) << "Current edge: " << currentEdgeName() << " Wanted: " << edge;
         return Value::kNullValue;
     }
@@ -239,6 +238,7 @@ const Value& GetNeighborsIter::getEdgeProp(const std::string& edge,
     auto index = dsIndices_[segment].edgePropsMap.find(currentEdge);
     if (index == dsIndices_[segment].edgePropsMap.end()) {
         VLOG(1) << "No edge found: " << edge;
+        VLOG(1) << "Current edge: " << currentEdge;
         return Value::kNullValue;
     }
     auto propIndex = index->second.propIndices.find(prop);
@@ -291,7 +291,7 @@ Value GetNeighborsIter::getEdge() const {
 
     auto segment = currentSeg();
     Edge edge;
-    auto& edgeName = currentEdgeName();
+    auto edgeName = currentEdgeName().substr(1, std::string::npos);
     edge.name = edgeName;
     auto& src = getColumn(kVid);
     if (!src.isStr()) {
@@ -313,7 +313,7 @@ Value GetNeighborsIter::getEdge() const {
     edge.type = 0;
 
     auto& edgePropMap = dsIndices_[segment].edgePropsMap;
-    auto edgeProp = edgePropMap.find(edgeName);
+    auto edgeProp = edgePropMap.find(currentEdgeName());
     if (edgeProp == edgePropMap.end()) {
         return Value::kNullValue;
     }
diff --git a/src/context/Iterator.h b/src/context/Iterator.h
index f316d371d9bc017cd4ccdf97f6bfd54bddc0ada7..7fc4666108944de48c4e973deb7ee382bbf21857 100644
--- a/src/context/Iterator.h
+++ b/src/context/Iterator.h
@@ -510,6 +510,13 @@ public:
         }
     }
 
+    // TODO: We should build new iter for get props, the seq iter will
+    // not meet the requirements of match any more.
+    const Value& getTagProp(const std::string& tag,
+                            const std::string& prop) const override {
+        return getColumn(tag+ "." + prop);
+    }
+
     const Value& getEdgeProp(const std::string& edge,
                              const std::string& prop) const override {
         return getColumn(edge + "." + prop);
diff --git a/src/exec/query/GetNeighborsExecutor.cpp b/src/exec/query/GetNeighborsExecutor.cpp
index 4f894fe50e4874c7d13630179f2a5017cf815235..8c96feacc83387c0d60169db5a5a463bd08bb771 100644
--- a/src/exec/query/GetNeighborsExecutor.cpp
+++ b/src/exec/query/GetNeighborsExecutor.cpp
@@ -43,14 +43,16 @@ Status GetNeighborsExecutor::buildRequestDataSet() {
     reqDs_.colNames = {kVid};
     reqDs_.rows.reserve(iter->size());
     auto* src = gn_->src();
+    std::unordered_set<Value> uniqueVid;
     for (; iter->valid(); iter->next()) {
         auto val = Expression::eval(src, ctx);
         if (!val.isStr()) {
             continue;
         }
-        Row row;
-        row.values.emplace_back(std::move(val));
-        reqDs_.rows.emplace_back(std::move(row));
+        auto ret = uniqueVid.emplace(val);
+        if (ret.second) {
+            reqDs_.rows.emplace_back(Row({std::move(val)}));
+        }
     }
 
     return Status::OK();
diff --git a/src/exec/query/GetPropExecutor.h b/src/exec/query/GetPropExecutor.h
index da670a64499133fe7362b01f9497b74d55b7281f..0aa6f5d86793669e4c606d59d6c334dbb1741c0f 100644
--- a/src/exec/query/GetPropExecutor.h
+++ b/src/exec/query/GetPropExecutor.h
@@ -39,6 +39,7 @@ protected:
         for (auto &colName : v.colNames) {
             std::replace(colName.begin(), colName.end(), ':', '.');
         }
+        VLOG(1) << "Resp: " << v;
         return finish(ResultBuilder()
                       .value(std::move(v))
                       .iter(Iterator::Kind::kSequential)
diff --git a/src/exec/query/GetVerticesExecutor.cpp b/src/exec/query/GetVerticesExecutor.cpp
index 76d56e621a440a3dd47f4edc1e3145c72296024c..7fe67a87a6bf918aa625d4b23e08c3a95709fcf2 100644
--- a/src/exec/query/GetVerticesExecutor.cpp
+++ b/src/exec/query/GetVerticesExecutor.cpp
@@ -29,7 +29,14 @@ folly::Future<Status> GetVerticesExecutor::getVertices() {
 
     GraphStorageClient *storageClient = qctx()->getStorageClient();
     nebula::DataSet vertices({kVid});
+    std::unordered_set<Value> uniqueVid;
     if (!gv->vertices().empty()) {
+        for (auto& v : gv->vertices()) {
+            auto ret = uniqueVid.emplace(v.values.front());
+            if (ret.second) {
+                vertices.emplace_back(std::move(v));
+            }
+        }
         vertices.rows.insert(vertices.rows.end(),
                              std::make_move_iterator(gv->vertices().begin()),
                              std::make_move_iterator(gv->vertices().end()));
@@ -37,18 +44,27 @@ folly::Future<Status> GetVerticesExecutor::getVertices() {
     if (gv->src() != nullptr) {
         // Accept Table such as | $a | $b | $c |... as input which one column indicate src
         auto valueIter = ectx_->getResult(gv->inputVar()).iter();
+        VLOG(1) << "GV input var: " << gv->inputVar() << " iter kind: " << valueIter->kind();
         auto expCtx = QueryExpressionContext(qctx()->ectx(), valueIter.get());
         for (; valueIter->valid(); valueIter->next()) {
             auto src = gv->src()->eval(expCtx);
+            VLOG(1) << "src vid: " << src;
             if (!src.isStr()) {
-                LOG(WARNING) << "Mismatched vid type.";
+                LOG(WARNING) << "Mismatched vid type: " << src.type();
                 continue;
             }
-            vertices.emplace_back(Row({
-                std::move(src)
-            }));
+            auto ret = uniqueVid.emplace(src);
+            if (ret.second) {
+                vertices.emplace_back(Row({std::move(src)}));
+            }
         }
     }
+
+    if (vertices.rows.empty()) {
+        // TODO: add test for empty input.
+        return finish(ResultBuilder().value(Value(DataSet())).finish());
+    }
+
     return DCHECK_NOTNULL(storageClient)
         ->getProps(gv->space(),
                    std::move(vertices),
diff --git a/src/planner/PlanNode.h b/src/planner/PlanNode.h
index 48f3430ca9cfae44e4eaec60be20976b06577b21..29fcc9c9b7684255ae24c1dfa47f280c3da0cb6e 100644
--- a/src/planner/PlanNode.h
+++ b/src/planner/PlanNode.h
@@ -107,7 +107,7 @@ public:
 
     void setId(int64_t id) {
         id_ = id;
-        outputVar_ = folly::stringPrintf("%s_%ld", toString(kind_), id_);
+        outputVar_ = folly::stringPrintf("__%s_%ld", toString(kind_), id_);
     }
 
     void setPlan(ExecutionPlan* plan) {
diff --git a/src/validator/GoValidator.cpp b/src/validator/GoValidator.cpp
index 18f89ec5aae277c14c9749cd9856567fcef6e7c2..1c689f68ac4906ff0f1eeb35a0ed02fcfbca1120 100644
--- a/src/validator/GoValidator.cpp
+++ b/src/validator/GoValidator.cpp
@@ -36,11 +36,6 @@ Status GoValidator::validateImpl() {
 
     NG_RETURN_IF_ERROR(buildColumns());
 
-    if (!dstTagProps_.empty()) {
-        // TODO: inplement get vertex props.
-        return Status::Error("Not support get dst yet.");
-    }
-
     if (isOverAll_) {
         // TODO: implement over all.
         return Status::Error("Not support over all yet.");
@@ -85,7 +80,11 @@ Status GoValidator::validateFrom(const FromClause* from) {
                 return Status::Error(ss.str());
             }
             srcRef_ = src;
-            firstBeginningSrcVidColName_ = *(static_cast<SymbolPropertyExpression*>(src)->prop());
+            auto* symPropExpr = static_cast<SymbolPropertyExpression*>(src);
+            if (fromType_ == kVariable) {
+                userDefinedVarName_ = *(symPropExpr->sym());
+            }
+            firstBeginningSrcVidColName_ = *(symPropExpr->prop());
         }
     } else {
         auto vidList = from->vidList();
@@ -204,28 +203,42 @@ Status GoValidator::oneStep(PlanNode* dependencyForGn,
     gn->setInputVar(inputVarNameForGN);
     VLOG(1) << gn->varName();
 
-    if (!dstTagProps_.empty()) {
-        // TODO: inplement get vertex props.
-        return Status::Error("Not support get dst yet.");
+    PlanNode* dependencyForProjectResult = gn;
+
+    PlanNode* projectSrcEdgeProps = nullptr;
+    if (!inputProps_.empty() || !varProps_.empty() || !dstTagProps_.empty()) {
+        projectSrcEdgeProps = buildProjectSrcEdgePropsForGN(gn);
+    }
+
+    PlanNode* joinDstProps = nullptr;
+    if (!dstTagProps_.empty() && projectSrcEdgeProps != nullptr) {
+        joinDstProps = buildJoinDstProps(projectSrcEdgeProps);
+    }
+    if (joinDstProps != nullptr) {
+        dependencyForProjectResult = joinDstProps;
     }
 
-    PlanNode* inputNodeForProjectResult = gn;
-    auto* joinInput = ifBuildJoinPipeInput(gn, projectFromJoin);
+    PlanNode* joinInput = nullptr;
+    if (!inputProps_.empty() || !varProps_.empty()) {
+        joinInput = buildJoinPipeOrVariableInput(
+            projectFromJoin,
+            joinDstProps == nullptr ? projectSrcEdgeProps : joinDstProps);
+    }
     if (joinInput != nullptr) {
-        inputNodeForProjectResult = joinInput;
+        dependencyForProjectResult = joinInput;
     }
 
     if (filter_ != nullptr) {
-        auto* filterNode = Filter::make(plan, inputNodeForProjectResult,
+        auto* filterNode = Filter::make(plan, dependencyForProjectResult,
                     newFilter_ != nullptr ? newFilter_ : filter_);
-        filterNode->setInputVar(inputNodeForProjectResult->varName());
-        filterNode->setColNames(inputNodeForProjectResult->colNames());
-        inputNodeForProjectResult = filterNode;
+        filterNode->setInputVar(dependencyForProjectResult->varName());
+        filterNode->setColNames(dependencyForProjectResult->colNames());
+        dependencyForProjectResult = filterNode;
     }
     auto* projectResult =
-        Project::make(plan, inputNodeForProjectResult,
+        Project::make(plan, dependencyForProjectResult,
         newYieldCols_ != nullptr ? newYieldCols_ : yields_);
-    projectResult->setInputVar(inputNodeForProjectResult->varName());
+    projectResult->setInputVar(dependencyForProjectResult->varName());
     projectResult->setColNames(std::vector<std::string>(colNames_));
     if (distinct_) {
         Dedup* dedupNode = Dedup::make(plan, projectResult);
@@ -253,17 +266,24 @@ Status GoValidator::buildNStepsPlan() {
         startVidsVar = projectStartVid->varName();
     }
 
-    Project* projectLeftVarForJoin = ifBuildLeftVarForTraceJoin(projectStartVid);
+    Project* projectLeftVarForJoin = nullptr;
+    if (!inputProps_.empty() || !varProps_.empty()) {
+        projectLeftVarForJoin = buildLeftVarForTraceJoin(projectStartVid);
+    }
 
     auto* gn = GetNeighbors::make(plan, bodyStart, space_.id);
     gn->setSrc(src_);
-    gn->setEdgeProps(buildNStepLoopEdgeProps());
+    gn->setEdgeProps(buildEdgeDst());
     gn->setInputVar(startVidsVar);
     VLOG(1) << gn->varName();
 
     Project* projectDstFromGN = projectDstVidsFromGN(gn, startVidsVar);
 
-    Project* projectFromJoin = ifTraceToStartVid(projectLeftVarForJoin, projectDstFromGN);
+    Project* projectFromJoin = nullptr;
+    if ((!inputProps_.empty() || !varProps_.empty()) &&
+        projectLeftVarForJoin != nullptr && projectDstFromGN != nullptr) {
+        projectFromJoin = traceToStartVid(projectLeftVarForJoin, projectDstFromGN);
+    }
 
     auto* loop = Loop::make(
         plan,
@@ -290,105 +310,167 @@ Status GoValidator::buildNStepsPlan() {
     return Status::OK();
 }
 
-PlanNode* GoValidator::ifBuildJoinPipeInput(
-    PlanNode* gn, PlanNode* projectFromJoin) {
-    PlanNode* inputNodeForProjectResult = nullptr;
+PlanNode* GoValidator::buildProjectSrcEdgePropsForGN(PlanNode* gn) {
+    DCHECK(gn != nullptr);
+
     auto* plan = qctx_->plan();
-    if (!inputProps_.empty()) {
+    if (!inputProps_.empty() || !varProps_.empty()) {
         auto* srcVidCol = new YieldColumn(
             new VariablePropertyExpression(new std::string(gn->varName()),
                                         new std::string(kVid)),
             new std::string(kVid));
         srcAndEdgePropCols_->addColumn(srcVidCol);
-        auto* project = Project::make(plan, gn, srcAndEdgePropCols_);
-        project->setInputVar(gn->varName());
-        project->setColNames(deduceColNames(srcAndEdgePropCols_));
-        VLOG(1) << project->varName();
-
-        PlanNode* joinInputHashKeyDependency = project;
-        if (steps_ > 1) {
-            auto* joinHashKey = new VariablePropertyExpression(
-                    new std::string(project->varName()), new std::string(kVid));
-            plan->saveObject(joinHashKey);
-            auto* probeKey = new VariablePropertyExpression(
-                    new std::string(projectFromJoin->varName()), new std::string(dstVidColName_));
-            plan->saveObject(probeKey);
-            auto* join = DataJoin::make(
-                plan, project,
-                {project->varName(), ExecutionContext::kLatestVersion},
-                {projectFromJoin->varName(), ExecutionContext::kLatestVersion},
-                {joinHashKey}, {probeKey});
-            std::vector<std::string> colNames = project->colNames();
-            for (auto& col : projectFromJoin->colNames()) {
-                colNames.emplace_back(col);
-            }
-            join->setColNames(std::move(colNames));
-            VLOG(1) << join->varName();
-            joinInputHashKeyDependency = join;
-        }
+    }
 
-        auto* joinHashKey = new VariablePropertyExpression(
-            new std::string(joinInputHashKeyDependency->varName()),
-            new std::string(steps_ > 1 ? firstBeginningSrcVidColName_ : kVid));
-        plan->saveObject(joinHashKey);
-        auto* joinInput =
-            DataJoin::make(plan, joinInputHashKeyDependency,
-                           {joinInputHashKeyDependency->varName(),
-                            ExecutionContext::kLatestVersion},
-                           {inputVarName_, ExecutionContext::kLatestVersion},
-                           {joinHashKey}, {steps_ > 1 ? srcRef_ : src_});
-        std::vector<std::string> colNames = joinInputHashKeyDependency->colNames();
-        for (auto& col : outputs_) {
-            colNames.emplace_back(col.first);
-        }
-        joinInput->setColNames(std::move(colNames));
-        VLOG(1) << joinInput->varName();
-        inputNodeForProjectResult = joinInput;
+    VLOG(1) << "build dst cols";
+    if (!dstTagProps_.empty()) {
+        joinDstVidColName_ = vctx_->anonColGen()->getCol();
+        auto* dstVidCol = new YieldColumn(
+            new EdgePropertyExpression(new std::string("*"),
+                                        new std::string(kDst)),
+            new std::string(joinDstVidColName_));
+        srcAndEdgePropCols_->addColumn(dstVidCol);
     }
 
-    return inputNodeForProjectResult;
+    auto* project = Project::make(plan, gn, srcAndEdgePropCols_);
+    project->setInputVar(gn->varName());
+    project->setColNames(deduceColNames(srcAndEdgePropCols_));
+    VLOG(1) << project->varName();
+
+    return project;
 }
 
-Project* GoValidator::ifTraceToStartVid(Project* projectLeftVarForJoin,
-                                         Project* projectDstFromGN) {
-    Project* projectJoin = nullptr;
+PlanNode* GoValidator::buildJoinDstProps(PlanNode* projectSrcDstProps) {
+    DCHECK(dstPropCols_ != nullptr);
+    DCHECK(projectSrcDstProps != nullptr);
+
     auto* plan = qctx_->plan();
-    if (!inputProps_.empty() && projectLeftVarForJoin != nullptr) {
-        auto hashKey = new VariablePropertyExpression(
-                new std::string(projectLeftVarForJoin->varName()),
-                new std::string(dstVidColName_));
-        plan->saveObject(hashKey);
-        auto probeKey = new VariablePropertyExpression(
-            new std::string(projectDstFromGN->varName()), new std::string(srcVidColName_));
+
+    auto* vids = new VariablePropertyExpression(
+        new std::string(projectSrcDstProps->varName()),
+        new std::string(joinDstVidColName_));
+    plan->saveObject(vids);
+    auto* getDstVertices =
+        GetVertices::make(plan, projectSrcDstProps, space_.id, {}, vids,
+                            buildDstVertexProps(), {});
+    getDstVertices->setInputVar(projectSrcDstProps->varName());
+
+    auto vidColName = vctx_->anonColGen()->getCol();
+    auto* vidCol = new YieldColumn(
+        new VariablePropertyExpression(new std::string(getDstVertices->varName()),
+                                    new std::string(kVid)),
+        new std::string(vidColName));
+    dstPropCols_->addColumn(vidCol);
+    auto* project = Project::make(plan, getDstVertices, dstPropCols_);
+    project->setInputVar(getDstVertices->varName());
+    project->setColNames(deduceColNames(dstPropCols_));
+
+    auto* joinHashKey = new VariablePropertyExpression(
+        new std::string(projectSrcDstProps->varName()),
+        new std::string(joinDstVidColName_));
+    plan->saveObject(joinHashKey);
+    auto* probeKey = new VariablePropertyExpression(
+        new std::string(project->varName()), new std::string(vidColName));
+    plan->saveObject(probeKey);
+    auto joinDst = DataJoin::make(plan, project,
+            {projectSrcDstProps->varName(), ExecutionContext::kLatestVersion},
+            {project->varName(), ExecutionContext::kLatestVersion},
+            {joinHashKey}, {probeKey});
+    VLOG(1) << joinDst->varName() << " hash key: " << joinHashKey->toString()
+        << " probe key: " << probeKey->toString();
+
+    return joinDst;
+}
+
+PlanNode* GoValidator::buildJoinPipeOrVariableInput(
+    PlanNode* projectFromJoin, PlanNode* dependencyForJoinInput) {
+    auto* plan = qctx_->plan();
+
+    if (steps_ > 1) {
+        DCHECK(projectFromJoin != nullptr);
+        auto* joinHashKey = new VariablePropertyExpression(
+                new std::string(dependencyForJoinInput->varName()), new std::string(kVid));
+        plan->saveObject(joinHashKey);
+        auto* probeKey = new VariablePropertyExpression(
+                new std::string(projectFromJoin->varName()), new std::string(dstVidColName_));
         plan->saveObject(probeKey);
         auto* join = DataJoin::make(
-            plan, projectDstFromGN,
-            {projectLeftVarForJoin->varName(),
-             ExecutionContext::kLatestVersion},
-            {projectDstFromGN->varName(), ExecutionContext::kLatestVersion},
-            {hashKey}, {probeKey});
-        std::vector<std::string> colNames = projectLeftVarForJoin->colNames();
-        for (auto& col : projectDstFromGN->colNames()) {
+            plan, dependencyForJoinInput,
+            {dependencyForJoinInput->varName(), ExecutionContext::kLatestVersion},
+            {projectFromJoin->varName(), ExecutionContext::kLatestVersion},
+            {joinHashKey}, {probeKey});
+        std::vector<std::string> colNames = dependencyForJoinInput->colNames();
+        for (auto& col : projectFromJoin->colNames()) {
             colNames.emplace_back(col);
         }
         join->setColNames(std::move(colNames));
         VLOG(1) << join->varName();
+        dependencyForJoinInput = join;
+    }
+
+    DCHECK(dependencyForJoinInput != nullptr);
+    auto* joinHashKey = new VariablePropertyExpression(
+        new std::string(dependencyForJoinInput->varName()),
+        new std::string(steps_ > 1 ? firstBeginningSrcVidColName_ : kVid));
+    plan->saveObject(joinHashKey);
+    auto* joinInput =
+        DataJoin::make(plan, dependencyForJoinInput,
+                        {dependencyForJoinInput->varName(),
+                        ExecutionContext::kLatestVersion},
+                        {fromType_ == kPipe ? inputVarName_ : userDefinedVarName_,
+                        ExecutionContext::kLatestVersion},
+                        {joinHashKey}, {steps_ > 1 ? srcRef_ : src_});
+    std::vector<std::string> colNames = dependencyForJoinInput->colNames();
+    for (auto& col : outputs_) {
+        colNames.emplace_back(col.first);
+    }
+    joinInput->setColNames(std::move(colNames));
+    VLOG(1) << joinInput->varName();
+
+    return joinInput;
+}
+
+Project* GoValidator::traceToStartVid(Project* projectLeftVarForJoin,
+                                      Project* projectDstFromGN) {
+    DCHECK(projectLeftVarForJoin != nullptr);
+    DCHECK(projectDstFromGN != nullptr);
+
+    auto* plan = qctx_->plan();
+    auto hashKey = new VariablePropertyExpression(
+            new std::string(projectLeftVarForJoin->varName()),
+            new std::string(dstVidColName_));
+    plan->saveObject(hashKey);
+    auto probeKey = new VariablePropertyExpression(
+        new std::string(projectDstFromGN->varName()), new std::string(srcVidColName_));
+    plan->saveObject(probeKey);
+    auto* join = DataJoin::make(
+        plan, projectDstFromGN,
+        {projectLeftVarForJoin->varName(),
+            ExecutionContext::kLatestVersion},
+        {projectDstFromGN->varName(), ExecutionContext::kLatestVersion},
+        {hashKey}, {probeKey});
+    std::vector<std::string> colNames = projectLeftVarForJoin->colNames();
+    for (auto& col : projectDstFromGN->colNames()) {
+        colNames.emplace_back(col);
+    }
+    join->setColNames(std::move(colNames));
+    VLOG(1) << join->varName();
+
+    auto* columns = new YieldColumns();
+    auto* column =
+        new YieldColumn(new InputPropertyExpression(
+                            new std::string(firstBeginningSrcVidColName_)),
+                        new std::string(firstBeginningSrcVidColName_));
+    columns->addColumn(column);
+    column =
+        new YieldColumn(new InputPropertyExpression(new std::string(kVid)),
+                        new std::string(dstVidColName_));
+    columns->addColumn(column);
+    auto* projectJoin = Project::make(plan, join, plan->saveObject(columns));
+    projectJoin->setInputVar(join->varName());
+    projectJoin->setColNames(deduceColNames(columns));
+    VLOG(1) << projectJoin->varName();
 
-        auto* columns = new YieldColumns();
-        auto* column =
-            new YieldColumn(new InputPropertyExpression(
-                                new std::string(firstBeginningSrcVidColName_)),
-                            new std::string(firstBeginningSrcVidColName_));
-        columns->addColumn(column);
-        column =
-            new YieldColumn(new InputPropertyExpression(new std::string(kVid)),
-                            new std::string(dstVidColName_));
-        columns->addColumn(column);
-        projectJoin = Project::make(plan, join, plan->saveObject(columns));
-        projectJoin->setInputVar(join->varName());
-        projectJoin->setColNames(deduceColNames(columns));
-        VLOG(1) << projectJoin->varName();
-    }
     return projectJoin;
 }
 
@@ -402,7 +484,7 @@ Project* GoValidator::projectDstVidsFromGN(PlanNode* gn, const std::string& outp
     columns->addColumn(column);
 
     srcVidColName_ = vctx_->anonColGen()->getCol();
-    if (!inputProps_.empty()) {
+    if (!inputProps_.empty() || !varProps_.empty()) {
         column =
             new YieldColumn(new InputPropertyExpression(new std::string(kVid)),
                             new std::string(srcVidColName_));
@@ -418,35 +500,39 @@ Project* GoValidator::projectDstVidsFromGN(PlanNode* gn, const std::string& outp
     return project;
 }
 
-Project* GoValidator::ifBuildLeftVarForTraceJoin(PlanNode* projectStartVid) {
-    Project* projectLeftVarForJoin = nullptr;
+Project* GoValidator::buildLeftVarForTraceJoin(PlanNode* projectStartVid) {
     auto* plan = qctx_->plan();
     dstVidColName_ = vctx_->anonColGen()->getCol();
-    if (!inputProps_.empty()) {
-        auto* columns = new YieldColumns();
-        auto* column =
-            new YieldColumn(Expression::decode(srcRef_->encode()).release(),
-                            new std::string(firstBeginningSrcVidColName_));
-        columns->addColumn(column);
-        column =
-            new YieldColumn(Expression::decode(srcRef_->encode()).release(),
-                            new std::string(dstVidColName_));
-        columns->addColumn(column);
-        plan->saveObject(columns);
-        projectLeftVarForJoin = Project::make(plan, projectStartVid, columns);
-        projectLeftVarForJoin->setInputVar(inputVarName_);
-        projectLeftVarForJoin->setColNames(deduceColNames(columns));
-    }
+    auto* columns = new YieldColumns();
+    auto* column =
+        new YieldColumn(Expression::decode(srcRef_->encode()).release(),
+                        new std::string(firstBeginningSrcVidColName_));
+    columns->addColumn(column);
+    column =
+        new YieldColumn(Expression::decode(srcRef_->encode()).release(),
+                        new std::string(dstVidColName_));
+    columns->addColumn(column);
+    plan->saveObject(columns);
+    // projectStartVid could be nullptr, that means no input for this project.
+    auto* projectLeftVarForJoin = Project::make(plan, projectStartVid, columns);
+    projectLeftVarForJoin->setInputVar(
+        fromType_ == kPipe ? inputVarName_ : userDefinedVarName_);
+    projectLeftVarForJoin->setColNames(deduceColNames(columns));
 
     return projectLeftVarForJoin;
 }
 
 Status GoValidator::buildOneStepPlan() {
-    std::string inputVarNameForGN = inputVarName_;
+    std::string inputVarNameForGN;
     if (!starts_.empty() && srcRef_ == nullptr) {
         inputVarNameForGN = buildConstantInput();
     } else {
         src_ = srcRef_;
+        if (fromType_ == kVariable) {
+            inputVarNameForGN = userDefinedVarName_;
+        } else {
+            inputVarNameForGN = inputVarName_;
+        }
     }
 
     auto status = oneStep(nullptr, inputVarNameForGN, nullptr);
@@ -484,7 +570,11 @@ PlanNode* GoValidator::buildRuntimeInput() {
     columns->addColumn(column);
     auto plan = qctx_->plan();
     auto* project = Project::make(plan, nullptr, plan->saveObject(columns));
+    if (fromType_ == kVariable) {
+        project->setInputVar(userDefinedVarName_);
+    }
     project->setColNames({ kVid });
+    VLOG(1) << project->varName() << " input: " << project->inputVar();
     src_ = plan->saveObject(new InputPropertyExpression(new std::string(kVid)));
     return project;
 }
@@ -505,13 +595,11 @@ GetNeighbors::VertexProps GoValidator::buildSrcVertexProps() {
     return vertexProps;
 }
 
-GetNeighbors::VertexProps GoValidator::buildDstVertexProps() {
-    GetNeighbors::VertexProps vertexProps;
+std::vector<storage::cpp2::VertexProp> GoValidator::buildDstVertexProps() {
+    std::vector<storage::cpp2::VertexProp> vertexProps(dstTagProps_.size());
     if (!dstTagProps_.empty()) {
-        vertexProps = std::make_unique<std::vector<storage::cpp2::VertexProp>>(
-            dstTagProps_.size());
         std::transform(dstTagProps_.begin(), dstTagProps_.end(),
-                       vertexProps->begin(), [](auto& tag) {
+                       vertexProps.begin(), [](auto& tag) {
                            storage::cpp2::VertexProp vp;
                            vp.tag = tag.first;
                            vp.props = std::move(tag.second);
@@ -525,55 +613,109 @@ GetNeighbors::EdgeProps GoValidator::buildEdgeProps() {
     GetNeighbors::EdgeProps edgeProps;
     if (!edgeProps_.empty()) {
         if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) {
-            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeProps_.size());
-            std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(),
-                    [] (auto& edge) {
-                storage::cpp2::EdgeProp ep;
-                ep.type = -edge.first;
-                ep.props = std::move(edge.second);
-                return ep;
-            });
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                edgeProps_.size());
+            std::transform(edgeProps_.begin(), edgeProps_.end(),
+                           edgeProps->begin(), [this](auto& edge) {
+                               storage::cpp2::EdgeProp ep;
+                               ep.type = -edge.first;
+                               ep.props = std::move(edge.second);
+                               if (!dstTagProps_.empty()) {
+                                   ep.props.emplace_back(kDst);
+                               }
+                               return ep;
+                           });
         } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) {
             auto size = edgeProps_.size();
-            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(size * 2);
-            std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(),
-                    [] (auto& edge) {
-                storage::cpp2::EdgeProp ep;
-                ep.type = edge.first;
-                ep.props = std::move(edge.second);
-                return ep;
-            });
-            std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin() + size,
-                    [] (auto& edge) {
-                storage::cpp2::EdgeProp ep;
-                ep.type = -edge.first;
-                ep.props = std::move(edge.second);
-                return ep;
-            });
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                size * 2);
+            std::transform(edgeProps_.begin(), edgeProps_.end(),
+                           edgeProps->begin(), [this](auto& edge) {
+                               storage::cpp2::EdgeProp ep;
+                               ep.type = edge.first;
+                               ep.props = edge.second;
+                               if (!dstTagProps_.empty()) {
+                                   ep.props.emplace_back(kDst);
+                               }
+                               return ep;
+                           });
+            std::transform(edgeProps_.begin(), edgeProps_.end(),
+                           edgeProps->begin() + size, [this](auto& edge) {
+                               storage::cpp2::EdgeProp ep;
+                               ep.type = -edge.first;
+                               ep.props = std::move(edge.second);
+                               if (!dstTagProps_.empty()) {
+                                   ep.props.emplace_back(kDst);
+                               }
+                               return ep;
+                           });
         } else {
-            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeProps_.size());
-            std::transform(edgeProps_.begin(), edgeProps_.end(), edgeProps->begin(),
-                    [] (auto& edge) {
-                storage::cpp2::EdgeProp ep;
-                ep.type = edge.first;
-                ep.props = std::move(edge.second);
-                return ep;
-            });
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                edgeProps_.size());
+            std::transform(edgeProps_.begin(), edgeProps_.end(),
+                           edgeProps->begin(), [this](auto& edge) {
+                               storage::cpp2::EdgeProp ep;
+                               ep.type = edge.first;
+                               ep.props = std::move(edge.second);
+                               if (!dstTagProps_.empty()) {
+                                   ep.props.emplace_back(kDst);
+                               }
+                               return ep;
+                           });
         }
+    } else if (!dstTagProps_.empty()) {
+        return buildEdgeDst();
     }
+
     return edgeProps;
 }
 
-GetNeighbors::EdgeProps GoValidator::buildNStepLoopEdgeProps() {
+GetNeighbors::EdgeProps GoValidator::buildEdgeDst() {
     GetNeighbors::EdgeProps edgeProps;
-    if (!edgeProps_.empty()) {
-        edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(edgeTypes_.size());
-        std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(), [] (auto& type) {
-            storage::cpp2::EdgeProp ep;
-            ep.type = type;
-            ep.props = {kDst};
-            return ep;
-        });
+    if (!edgeProps_.empty() || !dstTagProps_.empty()) {
+        if (direction_ == storage::cpp2::EdgeDirection::IN_EDGE) {
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                edgeTypes_.size());
+            std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(),
+                        [](auto& type) {
+                            storage::cpp2::EdgeProp ep;
+                            ep.type = -type;
+                            VLOG(1) << "edge type: " << ep.type;
+                            ep.props = {kDst};
+                            return ep;
+                        });
+        } else if (direction_ == storage::cpp2::EdgeDirection::BOTH) {
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                edgeTypes_.size() * 2);
+            std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(),
+                        [](auto& type) {
+                            storage::cpp2::EdgeProp ep;
+                            ep.type = type;
+                            VLOG(1) << "edge type: " << ep.type;
+                            ep.props = {kDst};
+                            return ep;
+                        });
+            std::transform(edgeTypes_.begin(), edgeTypes_.end(),
+                           edgeProps->begin() + edgeTypes_.size(),
+                           [](auto& type) {
+                               storage::cpp2::EdgeProp ep;
+                               ep.type = -type;
+                               VLOG(1) << "edge type: " << ep.type;
+                               ep.props = {kDst};
+                               return ep;
+                           });
+        } else {
+            edgeProps = std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
+                edgeTypes_.size());
+            std::transform(edgeTypes_.begin(), edgeTypes_.end(), edgeProps->begin(),
+                        [](auto& type) {
+                            storage::cpp2::EdgeProp ep;
+                            ep.type = type;
+                            VLOG(1) << "edge type: " << ep.type;
+                            ep.props = {kDst};
+                            return ep;
+                        });
+        }
     }
     return edgeProps;
 }
@@ -781,7 +923,7 @@ Status GoValidator::buildColumns() {
         return Status::OK();
     }
 
-    if (!srcTagProps_.empty() || !edgeProps_.empty()) {
+    if (!srcTagProps_.empty() || !edgeProps_.empty() || !dstTagProps_.empty()) {
         srcAndEdgePropCols_ = qctx_->plan()->saveObject(new YieldColumns());
     }
 
diff --git a/src/validator/GoValidator.h b/src/validator/GoValidator.h
index 4361a53ce711451d8eb28d5dff4865cfdd569cc2..9006ad6b1c5ef2c7995a77c63f9386798f92602c 100644
--- a/src/validator/GoValidator.h
+++ b/src/validator/GoValidator.h
@@ -52,23 +52,27 @@ private:
 
     GetNeighbors::VertexProps buildSrcVertexProps();
 
-    GetNeighbors::VertexProps buildDstVertexProps();
+    std::vector<storage::cpp2::VertexProp> buildDstVertexProps();
 
     GetNeighbors::EdgeProps buildEdgeProps();
 
-    GetNeighbors::EdgeProps buildNStepLoopEdgeProps();
+    GetNeighbors::EdgeProps buildEdgeDst();
 
     Expression* buildNStepLoopCondition(int64_t steps) const;
 
-    Project* ifBuildLeftVarForTraceJoin(PlanNode* projectStartVid);
+    Project* buildLeftVarForTraceJoin(PlanNode* projectStartVid);
 
     Project* projectDstVidsFromGN(PlanNode* gn, const std::string& outputVar);
 
-    Project* ifTraceToStartVid(Project* projectLeftVarForJoin,
-                               Project* projectDstFromGN);
+    Project* traceToStartVid(Project* projectLeftVarForJoin,
+                             Project* projectDstFromGN);
 
-    PlanNode* ifBuildJoinPipeInput(PlanNode* gn,
-                                   PlanNode* projectFromJoin);
+    PlanNode* buildJoinPipeOrVariableInput(PlanNode* gn,
+                                           PlanNode* projectFromJoin);
+
+    PlanNode* buildProjectSrcEdgePropsForGN(PlanNode* gn);
+
+    PlanNode* buildJoinDstProps(PlanNode* projectSrcDstProps);
 
     enum FromType {
         kConstantExpr,
@@ -89,6 +93,7 @@ private:
     std::vector<std::string>                                colNames_;
     YieldColumns*                                           yields_{nullptr};
     bool                                                    distinct_{false};
+    std::string                                             userDefinedVarName_;
 
     // Generated by validator if needed, and the lifecycle of raw pinters would
     // be managed by object pool
@@ -98,9 +103,12 @@ private:
     std::unordered_map<std::string, YieldColumn*>           propExprColMap_;
     Expression*                                             newFilter_{nullptr};
     YieldColumns*                                           newYieldCols_{nullptr};
+    // Used for n steps to trace the path
     std::string                                             srcVidColName_;
     std::string                                             dstVidColName_;
     std::string                                             firstBeginningSrcVidColName_;
+    // Used for get dst props
+    std::string                                             joinDstVidColName_;
 };
 }  // namespace graph
 }  // namespace nebula
diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp
index fd41f24d1332858a1160cef96a5e8e06de9ad457..548a8966a743ef987bc11d8a2d6b53fd9aa656c6 100644
--- a/src/validator/test/QueryValidatorTest.cpp
+++ b/src/validator/test/QueryValidatorTest.cpp
@@ -122,6 +122,9 @@ TEST_F(QueryValidatorTest, GoNSteps) {
         };
         EXPECT_TRUE(checkResult(query, expected));
     }
+}
+
+TEST_F(QueryValidatorTest, GoWithPipe) {
     {
         std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS "
                             "id | GO 2 STEPS FROM $-.id OVER like";
@@ -288,6 +291,222 @@ TEST_F(QueryValidatorTest, GoNSteps) {
         };
         EXPECT_TRUE(checkResult(query, expected));
     }
+    {
+        std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS "
+                            "id, $$.person.name as name | GO 1 STEPS FROM $-.id OVER like "
+                            "YIELD $-.name, like.likeness + 1, $-.id, like._dst, "
+                            "$$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS "
+                            "id, $$.person.name as name | GO 1 STEPS FROM $-.id OVER like "
+                            "YIELD DISTINCT $-.name, like.likeness + 1, $-.id, like._dst, "
+                            "$$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kDataCollect,
+            PK::kDedup,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO FROM \"1\" OVER like "
+                            "YIELD $^.person.name AS name, like._dst AS id "
+                            "| GO FROM $-.id OVER like "
+                            "YIELD $-.name, $^.person.name, $$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS "
+                            "id, $$.person.name as name | GO 2 STEPS FROM $-.id OVER like "
+                            "YIELD $-.name, like.likeness + 1, $-.id, like._dst, "
+                            "$$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kLoop,
+            PK::kProject,
+            PK::kProject,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kStart,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO 1 STEPS FROM \"1\" OVER like YIELD like._dst AS "
+                            "id, $$.person.name as name | GO 2 STEPS FROM $-.id OVER like "
+                            "YIELD DISTINCT $-.name, like.likeness + 1, $-.id, like._dst, "
+                            "$$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kDataCollect,
+            PK::kDedup,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kLoop,
+            PK::kProject,
+            PK::kProject,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kStart,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+}
+
+TEST_F(QueryValidatorTest, GoWithVariable) {
+    {
+        std::string query = "$var = GO FROM \"1\" OVER like "
+                            "YIELD $^.person.name AS name, like._dst AS id;"
+                            "GO FROM $var.id OVER like "
+                            "YIELD $var.name, $^.person.name, $$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+}
+
+TEST_F(QueryValidatorTest, GoReversely) {
+    {
+        std::string query = "GO FROM \"1\" OVER like REVERSELY "
+                            "YIELD $$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO 2 STEPS FROM \"1\" OVER like REVERSELY "
+                            "YIELD $$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kLoop,
+            PK::kStart,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+}
+
+TEST_F(QueryValidatorTest, GoBidirectly) {
+    {
+        std::string query = "GO FROM \"1\" OVER like BIDIRECT";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO FROM \"1\" OVER like BIDIRECT "
+                            "YIELD $$.person.name";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
 }
 
 TEST_F(QueryValidatorTest, GoOneStep) {
@@ -340,8 +559,67 @@ TEST_F(QueryValidatorTest, GoOneStep) {
     {
         std::string query = "GO FROM \"1\" OVER like "
                             "YIELD $$.person.name,$$.person.age";
-        // TODO: implement get dst props and test plan.
-        EXPECT_FALSE(checkResult(query));
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO FROM \"1\" OVER like "
+                            "YIELD $^.person.name, like._dst, "
+                            "$$.person.name, $$.person.age + 1";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO FROM \"1\" OVER like "
+                            "WHERE like._dst == \"2\""
+                            "YIELD $^.person.name, like._dst, "
+                            "$$.person.name, $$.person.age + 1";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kFilter,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
+    {
+        std::string query = "GO FROM \"1\" OVER like "
+                            "WHERE like._dst == \"2\""
+                            "YIELD DISTINCT $^.person.name, like._dst, "
+                            "$$.person.name, $$.person.age + 1";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kDataCollect,
+            PK::kDedup,
+            PK::kProject,
+            PK::kFilter,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart,
+        };
+        EXPECT_TRUE(checkResult(query, expected));
     }
     {
         std::string query = "GO FROM \"1\",\"2\",\"3\" OVER like";
diff --git a/src/validator/test/ValidatorTestBase.h b/src/validator/test/ValidatorTestBase.h
index 7ca4723c2e2f6fd92c733d3a7b3c70ded66a5820..7245c2fe585bddb54a560bc4dfab52cadda63d2b 100644
--- a/src/validator/test/ValidatorTestBase.h
+++ b/src/validator/test/ValidatorTestBase.h
@@ -51,6 +51,7 @@ protected:
     ::testing::AssertionResult checkResult(const std::string& query,
                                            const std::vector<PlanNode::Kind>& expected = {},
                                            const std::vector<std::string> &rootColumns = {}) {
+        VLOG(1) << "query: " << query;
         auto result = GQLParser().parse(query);
         if (!result.ok()) {
             return ::testing::AssertionFailure() << result.status();