diff --git a/src/context/Iterator.cpp b/src/context/Iterator.cpp index 0d3726232..9fc92de07 100644 --- a/src/context/Iterator.cpp +++ b/src/context/Iterator.cpp @@ -748,9 +748,6 @@ std::ostream& operator<<(std::ostream& os, Iterator::Kind kind) { case Iterator::Kind::kGetNeighbors: os << "get neighbors"; break; - case Iterator::Kind::kJoin: - os << "join"; - break; case Iterator::Kind::kProp: os << "Prop"; break; diff --git a/src/context/Iterator.h b/src/context/Iterator.h index 9a4110d6f..a10e2c506 100644 --- a/src/context/Iterator.h +++ b/src/context/Iterator.h @@ -39,7 +39,6 @@ class Iterator { kDefault, kGetNeighbors, kSequential, - kJoin, kProp, }; @@ -103,10 +102,6 @@ class Iterator { return kind_ == Kind::kSequential; } - bool isJoinIter() const { - return kind_ == Kind::kJoin; - } - bool isPropIter() const { return kind_ == Kind::kProp; } @@ -270,9 +265,9 @@ class GetNeighborsIter final : public Iterator { // Its unique based on the GN interface dedup List getEdges(); + // only return currentEdge, not currentRow, for test const Row* row() const override { - DCHECK(false); - return nullptr; + return currentEdge_; } private: diff --git a/src/context/ast/QueryAstContext.h b/src/context/ast/QueryAstContext.h index 69e397b97..3f4f511a6 100644 --- a/src/context/ast/QueryAstContext.h +++ b/src/context/ast/QueryAstContext.h @@ -10,6 +10,7 @@ #include "common/base/Base.h" #include "common/expression/Expression.h" #include "context/ast/AstContext.h" +#include "visitor/DeducePropsVisitor.h" namespace nebula { namespace graph { @@ -42,6 +43,7 @@ struct PathContext final : AstContext { Starts to; StepClause steps; Over over; + Expression* filter{nullptr}; /* * find path from A to B OR find path from $-.src to $-.dst @@ -70,6 +72,7 @@ struct PathContext final : AstContext { // just for pipe sentence, // store the result of the previous sentence std::string inputVarName; + ExpressionProps exprProps; }; } // namespace graph diff --git a/src/parser/TraverseSentences.cpp b/src/parser/TraverseSentences.cpp index eefba70cd..12b39e584 100644 --- a/src/parser/TraverseSentences.cpp +++ b/src/parser/TraverseSentences.cpp @@ -197,15 +197,15 @@ std::string FindPathSentence::toString() const { buf += over_->toString(); buf += " "; } + if (where_ != nullptr) { + buf += where_->toString(); + buf += " "; + } if (step_ != nullptr) { buf += "UPTO "; buf += step_->toString(); buf += " "; } - if (where_ != nullptr) { - buf += where_->toString(); - buf += " "; - } return buf; } diff --git a/src/parser/parser.yy b/src/parser/parser.yy index c2cae66fe..0065d09fb 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -1929,34 +1929,31 @@ fetch_sentence ; find_path_sentence - : KW_FIND KW_ALL KW_PATH opt_with_properites from_clause to_clause over_clause find_path_upto_clause - /* where_clause */ { + : KW_FIND KW_ALL KW_PATH opt_with_properites from_clause to_clause over_clause where_clause find_path_upto_clause { auto *s = new FindPathSentence(false, $4, false); s->setFrom($5); s->setTo($6); s->setOver($7); - s->setStep($8); - /* s->setWhere($9); */ + s->setWhere($8); + s->setStep($9); $$ = s; } - | KW_FIND KW_SHORTEST KW_PATH opt_with_properites from_clause to_clause over_clause find_path_upto_clause - /* where_clause */ { + | KW_FIND KW_SHORTEST KW_PATH opt_with_properites from_clause to_clause over_clause where_clause find_path_upto_clause { auto *s = new FindPathSentence(true, $4, false); s->setFrom($5); s->setTo($6); s->setOver($7); - s->setStep($8); - /* s->setWhere($9); */ + s->setWhere($8); + s->setStep($9); $$ = s; } - | KW_FIND KW_NOLOOP KW_PATH opt_with_properites from_clause to_clause over_clause find_path_upto_clause - /* where_clause */ { + | KW_FIND KW_NOLOOP KW_PATH opt_with_properites from_clause to_clause over_clause where_clause find_path_upto_clause { auto *s = new FindPathSentence(false, $4, true); s->setFrom($5); s->setTo($6); s->setOver($7); - s->setStep($8); - /* s->setWhere($9) */ + s->setWhere($8); + s->setStep($9); $$ = s; } ; diff --git a/src/planner/ngql/PathPlanner.cpp b/src/planner/ngql/PathPlanner.cpp index 7a0e80382..49bb99a36 100644 --- a/src/planner/ngql/PathPlanner.cpp +++ b/src/planner/ngql/PathPlanner.cpp @@ -13,6 +13,24 @@ namespace nebula { namespace graph { +GetNeighbors::VertexProps PathPlanner::buildSrcVertexProps() { + GetNeighbors::VertexProps vertexProps; + auto& srcTagProps = pathCtx_->exprProps.srcTagProps(); + if (srcTagProps.empty()) { + return vertexProps; + } + vertexProps = std::make_unique>(srcTagProps.size()); + auto fun = [] (auto& tag) { + storage::cpp2::VertexProp vp; + vp.set_tag(tag.first); + std::vectorprops(tag.second.begin(), tag.second.end()); + vp.set_props(std::move(props)); + return vp; + }; + std::transform(srcTagProps.begin(), srcTagProps.end(), vertexProps->begin(), fun); + return vertexProps; +} + GetNeighbors::EdgeProps PathPlanner::buildEdgeProps(bool reverse) { auto edgeProps = std::make_unique>(); switch (pathCtx_->over.direction) { @@ -36,6 +54,7 @@ GetNeighbors::EdgeProps PathPlanner::buildEdgeProps(bool reverse) { void PathPlanner::doBuildEdgeProps(GetNeighbors::EdgeProps& edgeProps, bool reverse, bool isInEdge) { + const auto& exprProps = pathCtx_->exprProps; for (const auto& e : pathCtx_->over.edgeTypes) { storage::cpp2::EdgeProp ep; if (reverse == isInEdge) { @@ -43,7 +62,16 @@ void PathPlanner::doBuildEdgeProps(GetNeighbors::EdgeProps& edgeProps, } else { ep.set_type(-e); } - ep.set_props({kDst, kType, kRank}); + const auto& found = exprProps.edgeProps().find(e); + if (found == exprProps.edgeProps().end()) { + ep.set_props({kDst, kType, kRank}); + } else { + std::set props(found->second.begin(), found->second.end()); + props.emplace(kDst); + props.emplace(kType); + props.emplace(kRank); + ep.set_props(std::vector(props.begin(), props.end())); + } edgeProps->emplace_back(std::move(ep)); } } @@ -208,13 +236,22 @@ PlanNode* PathPlanner::singlePairPath(PlanNode* dep, bool reverse) { auto qctx = pathCtx_->qctx; auto* src = qctx->objPool()->add(new ColumnExpression(0)); + PlanNode* pathDep = nullptr; auto* gn = GetNeighbors::make(qctx, dep, pathCtx_->space.id); gn->setSrc(src); + gn->setVertexProps(buildSrcVertexProps()); gn->setEdgeProps(buildEdgeProps(reverse)); gn->setInputVar(vidsVar); gn->setDedup(); + pathDep = gn; - auto* path = BFSShortestPath::make(qctx, gn); + if (pathCtx_->filter != nullptr) { + auto* filterExpr = qctx->objPool()->add(pathCtx_->filter->clone().release()); + auto* filter = Filter::make(qctx, gn, filterExpr); + pathDep = filter; + } + + auto* path = BFSShortestPath::make(qctx, pathDep); path->setOutputVar(vidsVar); path->setColNames({kVid, kEdgeStr}); @@ -258,13 +295,22 @@ PlanNode* PathPlanner::allPairPath(PlanNode* dep, bool reverse) { auto qctx = pathCtx_->qctx; auto* src = qctx->objPool()->add(new ColumnExpression(0)); + PlanNode* pathDep = nullptr; auto* gn = GetNeighbors::make(qctx, dep, pathCtx_->space.id); gn->setSrc(src); + gn->setVertexProps(buildSrcVertexProps()); gn->setEdgeProps(buildEdgeProps(reverse)); gn->setInputVar(vidsVar); gn->setDedup(); + pathDep = gn; + + if (pathCtx_->filter != nullptr) { + auto* filterExpr = qctx->objPool()->add(pathCtx_->filter->clone().release()); + auto* filter = Filter::make(qctx, gn, filterExpr); + pathDep = filter; + } - auto* path = ProduceAllPaths::make(qctx, gn); + auto* path = ProduceAllPaths::make(qctx, pathDep); path->setOutputVar(vidsVar); path->setColNames({kVid, kPathStr}); return path; @@ -303,13 +349,22 @@ PlanNode* PathPlanner::multiPairPath(PlanNode* dep, bool reverse) { auto qctx = pathCtx_->qctx; auto* src = qctx->objPool()->add(new ColumnExpression(0)); + PlanNode* pathDep = nullptr; auto* gn = GetNeighbors::make(qctx, dep, pathCtx_->space.id); gn->setSrc(src); + gn->setVertexProps(buildSrcVertexProps()); gn->setEdgeProps(buildEdgeProps(reverse)); gn->setInputVar(vidsVar); gn->setDedup(); + pathDep = gn; + + if (pathCtx_->filter != nullptr) { + auto* filterExpr = qctx->objPool()->add(pathCtx_->filter->clone().release()); + auto* filter = Filter::make(qctx, gn, filterExpr); + pathDep = filter; + } - auto* path = ProduceSemiShortestPath::make(qctx, gn); + auto* path = ProduceSemiShortestPath::make(qctx, pathDep); path->setOutputVar(vidsVar); path->setColNames({kDst, kSrc, kCostStr, kPathStr}); diff --git a/src/planner/ngql/PathPlanner.h b/src/planner/ngql/PathPlanner.h index 584920e63..148da357c 100644 --- a/src/planner/ngql/PathPlanner.h +++ b/src/planner/ngql/PathPlanner.h @@ -49,6 +49,8 @@ class PathPlanner final : public Planner { PlanNode* buildEdgePlan(PlanNode* dep, const std::string& input); private: + GetNeighbors::VertexProps buildSrcVertexProps(); + GetNeighbors::EdgeProps buildEdgeProps(bool reverse); void doBuildEdgeProps(GetNeighbors::EdgeProps& edgeProps, bool reverse, bool isInEdge); diff --git a/src/validator/FindPathValidator.cpp b/src/validator/FindPathValidator.cpp index cd48c8337..5dc1ded04 100644 --- a/src/validator/FindPathValidator.cpp +++ b/src/validator/FindPathValidator.cpp @@ -19,13 +19,48 @@ Status FindPathValidator::validateImpl() { pathCtx_->noLoop = fpSentence->noLoop(); pathCtx_->withProp = fpSentence->withProperites(); pathCtx_->inputVarName = inputVarName_; + NG_RETURN_IF_ERROR(validateStarts(fpSentence->from(), pathCtx_->from)); NG_RETURN_IF_ERROR(validateStarts(fpSentence->to(), pathCtx_->to)); NG_RETURN_IF_ERROR(validateOver(fpSentence->over(), pathCtx_->over)); + NG_RETURN_IF_ERROR(validateWhere(fpSentence->where())); NG_RETURN_IF_ERROR(validateStep(fpSentence->step(), pathCtx_->steps)); outputs_.emplace_back("path", Value::Type::PATH); return Status::OK(); } + +Status FindPathValidator::validateWhere(WhereClause* where) { + if (where == nullptr) { + return Status::OK(); + } + // Not Support $-、$var、$$.tag.prop、agg + auto expr = where->filter(); + if (ExpressionUtils::findAny(expr, + {Expression::Kind::kAggregate, + Expression::Kind::kDstProperty, + Expression::Kind::kVarProperty, + Expression::Kind::kInputProperty})) { + return Status::SemanticError("Not support `%s' in where sentence.", + expr->toString().c_str()); + } + auto filter = ExpressionUtils::rewriteLabelAttr2EdgeProp(expr); + + auto typeStatus = deduceExprType(filter); + NG_RETURN_IF_ERROR(typeStatus); + auto type = typeStatus.value(); + if (type != Value::Type::BOOL && type != Value::Type::NULLVALUE && + type != Value::Type::__EMPTY__) { + std::stringstream ss; + ss << "`" << filter->toString() << "', expected Boolean, " + << "but was `" << type << "'"; + return Status::SemanticError(ss.str()); + } + + NG_RETURN_IF_ERROR(deduceProps(filter, pathCtx_->exprProps)); + pathCtx_->filter = filter; + return Status::OK(); +} + } // namespace graph } // namespace nebula diff --git a/src/validator/FindPathValidator.h b/src/validator/FindPathValidator.h index 1d299ea88..87aba5c95 100644 --- a/src/validator/FindPathValidator.h +++ b/src/validator/FindPathValidator.h @@ -26,6 +26,8 @@ class FindPathValidator final : public TraversalValidator { return pathCtx_.get(); } + Status validateWhere(WhereClause* where); + private: std::unique_ptr pathCtx_; }; diff --git a/src/validator/test/FindPathValidatorTest.cpp b/src/validator/test/FindPathValidatorTest.cpp index 7d6c9e34f..9fce45613 100644 --- a/src/validator/test/FindPathValidatorTest.cpp +++ b/src/validator/test/FindPathValidatorTest.cpp @@ -320,6 +320,71 @@ TEST_F(FindPathValidatorTest, RunTimePath) { } } +TEST_F(FindPathValidatorTest, PathWithFilter) { + { + std::string query = + "FIND ALL PATH FROM \"1\" TO \"2\" OVER like WHERE like.likeness > 30 UPTO 5 STEPS"; + std::vector expected = { + PK::kDataCollect, + PK::kLoop, + PK::kProject, + PK::kConjunctPath, + PK::kProject, + PK::kProduceAllPaths, + PK::kProduceAllPaths, + PK::kStart, + PK::kFilter, + PK::kFilter, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } + { + std::string query = "FIND SHORTEST PATH FROM \"1\" TO \"2\" OVER like WHERE like.likeness " + "> 30 UPTO 5 STEPS"; + std::vector expected = { + PK::kDataCollect, + PK::kLoop, + PK::kStart, + PK::kConjunctPath, + PK::kBFSShortest, + PK::kBFSShortest, + PK::kFilter, + PK::kFilter, + 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 like WHERE " + "like.likeness > 30 UPTO 5 STEPS"; + std::vector expected = { + PK::kDataCollect, + PK::kLoop, + PK::kCartesianProduct, + PK::kConjunctPath, + PK::kProject, + PK::kProduceSemiShortestPath, + PK::kProduceSemiShortestPath, + PK::kProject, + PK::kFilter, + PK::kFilter, + PK::kStart, + PK::kGetNeighbors, + PK::kGetNeighbors, + PK::kPassThrough, + PK::kStart, + }; + EXPECT_TRUE(checkResult(query, expected)); + } +} + } // namespace graph } // namespace nebula