Skip to content
Snippets Groups Projects
Unverified Commit 1b554864 authored by cpw's avatar cpw Committed by GitHub
Browse files

Match start from any node. (#505)


* Fix the find start node logic.

* Expand from node or edge.

* Left expand and right expand.

* Add dependecy and inputVar for Expand.

* Fix all edge expand.

* Join the left expand and right expand.

* Fix project wrong vid column.

* Project final columns.

* Fix inputvar of left expand.

* Fix reversely expand.

* Fix mem leak and double free.

* Test from middle.

* Fix start from end node.

* Fix return edges.

* Fix return edges and add tests.

* Rebase and fix.

* Fix ut.

* Fix mem leak.

* Fix typo.

* Fix match by id test and add int vid test for start from any node.

* Move some test to bugfix.

Co-authored-by: default avatarShylock Hg <33566796+Shylock-Hg@users.noreply.github.com>
parent 6df1d1f6
No related branches found
No related tags found
No related merge requests found
Showing
with 536 additions and 193 deletions
......@@ -686,28 +686,20 @@ std::ostream& operator<<(std::ostream& os, LogicalRow::Kind kind) {
}
std::ostream& operator<<(std::ostream& os, const LogicalRow& row) {
switch (row.kind()) {
case nebula::graph::LogicalRow::Kind::kSequential:
case nebula::graph::LogicalRow::Kind::kJoin: {
std::stringstream ss;
size_t cnt = 0;
for (auto* seg : row.segments()) {
if (seg == nullptr) {
ss << "nullptr";
} else {
ss << *seg;
}
if (cnt < row.size() - 1) {
ss << ",";
}
++cnt;
}
os << ss.str();
break;
std::stringstream ss;
size_t cnt = 0;
for (auto* seg : row.segments()) {
if (seg == nullptr) {
ss << "nullptr";
} else {
ss << *seg;
}
default:
LOG(FATAL) << "Not support streaming for " << row.kind();
if (cnt < row.size() - 1) {
ss << ",";
}
++cnt;
}
os << ss.str();
return os;
}
} // namespace graph
......
......@@ -150,11 +150,10 @@ struct NodeContext final : PatternContext {
NodeInfo* info{nullptr};
// Output fields
ScanInfo scanInfo;
const Expression* ids{nullptr};
ScanInfo scanInfo;
const Expression* ids{nullptr};
// initialize start expression in project node
// initialExpr will be used by YieldColumn, so no need to handle the lifecycle by object pool.
Expression* initialExpr{nullptr};
std::unique_ptr<Expression> initialExpr;
};
struct EdgeContext final : PatternContext {
......
......@@ -26,6 +26,7 @@ folly::Future<Status> ProjectExecutor::execute() {
DataSet ds;
ds.colNames = project->colNames();
for (; iter->valid(); iter->next()) {
VLOG(1) << "row: " << *iter->row();
Row row;
for (auto& col : columns) {
Value val = col->expr()->eval(ctx(iter.get()));
......
......@@ -8,7 +8,7 @@
#define PLANNER_MATCH_ADDDEPENDENCYSTRATEGY_H_
#include "planner/PlanNode.h"
#include "planner/match/SegmentsConnector.h"
#include "planner/match/SegmentsConnectStrategy.h"
namespace nebula {
namespace graph {
......
......@@ -8,7 +8,7 @@
#define PLANNER_MATCH_ADDINPUTSTRATEGY_H_
#include "planner/PlanNode.h"
#include "planner/match/SegmentsConnector.h"
#include "planner/match/SegmentsConnectStrategy.h"
namespace nebula {
namespace graph {
......
......@@ -26,26 +26,35 @@ static std::unique_ptr<std::vector<VertexProp>> genVertexProps() {
}
std::unique_ptr<std::vector<storage::cpp2::EdgeProp>> Expand::genEdgeProps(const EdgeInfo &edge) {
if (edge.edgeTypes.empty()) {
return std::make_unique<std::vector<storage::cpp2::EdgeProp>>(
buildAllEdgeProp().value());
}
auto edgeProps = std::make_unique<std::vector<EdgeProp>>();
for (auto edgeType : edge.edgeTypes) {
auto edgeSchema = matchCtx_->qctx->schemaMng()->getEdgeSchema(
matchCtx_->space.id, edgeType);
if (edge.direction == Direction::IN_EDGE) {
edgeType = -edgeType;
} else if (edge.direction == Direction::BOTH) {
EdgeProp edgeProp;
edgeProp.set_type(-edgeType);
std::vector<std::string> props{kSrc, kType, kRank, kDst};
for (std::size_t i = 0; i < edgeSchema->getNumFields(); ++i) {
props.emplace_back(edgeSchema->getFieldName(i));
switch (edge.direction) {
case Direction::OUT_EDGE: {
if (reversely_) {
edgeType = -edgeType;
}
break;
}
case Direction::IN_EDGE: {
if (!reversely_) {
edgeType = -edgeType;
}
break;
}
case Direction::BOTH: {
EdgeProp edgeProp;
edgeProp.set_type(-edgeType);
std::vector<std::string> props{kSrc, kType, kRank, kDst};
for (std::size_t i = 0; i < edgeSchema->getNumFields(); ++i) {
props.emplace_back(edgeSchema->getFieldName(i));
}
edgeProp.set_props(std::move(props));
edgeProps->emplace_back(std::move(edgeProp));
break;
}
edgeProp.set_props(std::move(props));
edgeProps->emplace_back(std::move(edgeProp));
}
EdgeProp edgeProp;
edgeProp.set_type(edgeType);
......@@ -75,25 +84,24 @@ static Expression* buildPathExpr() {
Status Expand::doExpand(const NodeInfo& node,
const EdgeInfo& edge,
const PlanNode* input,
SubPlan*plan) {
NG_RETURN_IF_ERROR(expandSteps(node, edge, input, plan));
SubPlan* plan) {
NG_RETURN_IF_ERROR(expandSteps(node, edge, plan));
NG_RETURN_IF_ERROR(filterDatasetByPathLength(edge, plan->root, plan));
return Status::OK();
}
Status Expand::expandSteps(const NodeInfo& node,
const EdgeInfo& edge,
const PlanNode* input,
SubPlan* plan) {
SubPlan subplan;
NG_RETURN_IF_ERROR(expandStep(edge, input, node.filter, true, &subplan));
NG_RETURN_IF_ERROR(expandStep(edge, dependency_, inputVar_, node.filter, true, &subplan));
// plan->tail = subplan.tail;
PlanNode* passThrough = subplan.root;
auto maxHop = edge.range ? edge.range->max() : 1;
for (int64_t i = 1; i < maxHop; ++i) {
SubPlan curr;
NG_RETURN_IF_ERROR(expandStep(edge, passThrough, nullptr, false, &curr));
NG_RETURN_IF_ERROR(
expandStep(edge, passThrough, passThrough->outputVar(), nullptr, false, &curr));
auto rNode = subplan.root;
DCHECK(rNode->kind() == PNKind::kUnion || rNode->kind() == PNKind::kPassThrough);
NG_RETURN_IF_ERROR(collectData(passThrough, curr.root, rNode, &passThrough, &subplan));
......@@ -104,17 +112,17 @@ Status Expand::expandSteps(const NodeInfo& node,
// build subplan: Project->Dedup->GetNeighbors->[Filter]->Project
Status Expand::expandStep(const EdgeInfo& edge,
const PlanNode* input,
PlanNode* dep,
const std::string& inputVar,
const Expression* nodeFilter,
bool needPassThrough,
SubPlan* plan) {
DCHECK(input != nullptr);
auto qctx = matchCtx_->qctx;
// Extract dst vid from input project node which output dataset format is: [v1,e1,...,vn,en]
SubPlan curr;
curr.root = const_cast<PlanNode*>(input);
MatchSolver::extractAndDedupVidColumn(qctx, initialExpr_, &curr);
curr.root = dep;
MatchSolver::extractAndDedupVidColumn(qctx, initialExpr_.release(), dep, inputVar, curr);
auto gn = GetNeighbors::make(qctx, curr.root, matchCtx_->space.id);
auto srcExpr = ExpressionUtils::inputPropExpr(kVid);
......@@ -152,7 +160,7 @@ Status Expand::expandStep(const EdgeInfo& edge,
auto la = static_cast<const LabelAttributeExpression*>(expr);
return new AttributeExpression(new EdgeExpression(), la->right()->clone().release());
});
auto filter = edge.filter->clone().release();
auto filter = saveObject(edge.filter->clone().release());
filter->accept(&visitor);
auto filterNode = Filter::make(qctx, root, filter);
filterNode->setColNames(root->colNames());
......@@ -230,30 +238,5 @@ Status Expand::filterDatasetByPathLength(const EdgeInfo& edge,
// plan->tail = curr.tail;
return Status::OK();
}
StatusOr<std::vector<storage::cpp2::EdgeProp>> Expand::buildAllEdgeProp() {
// list all edge properties
std::map<TagID, std::shared_ptr<const meta::SchemaProviderIf>> edgesSchema;
const auto allEdgesResult = matchCtx_->qctx->schemaMng()->getAllVerEdgeSchema(
matchCtx_->space.id);
NG_RETURN_IF_ERROR(allEdgesResult);
const auto allEdges = std::move(allEdgesResult).value();
for (const auto &edge : allEdges) {
edgesSchema.emplace(edge.first, edge.second.back());
}
std::vector<storage::cpp2::EdgeProp> eProps;
for (const auto &edgeSchema : edgesSchema) {
storage::cpp2::EdgeProp eProp;
eProp.set_type(edgeSchema.first);
std::vector<std::string> props{kSrc, kType, kRank, kDst};
for (std::size_t i = 0; i < edgeSchema.second->getNumFields(); ++i) {
props.emplace_back(edgeSchema.second->getFieldName(i));
}
eProp.set_props(std::move(props));
eProps.emplace_back(std::move(eProp));
}
return eProps;
}
} // namespace graph
} // namespace nebula
......@@ -19,21 +19,36 @@ namespace graph {
*/
class Expand final {
public:
Expand(MatchClauseContext* matchCtx, Expression** initialExpr)
: matchCtx_(matchCtx), initialExpr_(initialExpr) {}
Expand(MatchClauseContext* matchCtx, std::unique_ptr<Expression> initialExpr)
: matchCtx_(matchCtx), initialExpr_(std::move(initialExpr)) {}
Expand* reversely() {
reversely_ = true;
return this;
}
Expand* depends(PlanNode* dep) {
dependency_ = dep;
return this;
}
Expand* inputVar(const std::string& inputVar) {
inputVar_ = inputVar;
return this;
}
Status doExpand(const NodeInfo& node,
const EdgeInfo& edge,
const PlanNode* input,
SubPlan* plan);
private:
Status expandSteps(const NodeInfo& node,
const EdgeInfo& edge,
const PlanNode* input,
SubPlan* plan);
Status expandStep(const EdgeInfo& edge,
const PlanNode* input,
PlanNode* dep,
const std::string& inputVar,
const Expression* nodeFilter,
bool needPassThrough,
SubPlan* plan);
......@@ -46,22 +61,18 @@ public:
Status filterDatasetByPathLength(const EdgeInfo& edge, PlanNode* input, SubPlan* plan);
Expression* initialExprOrEdgeDstExpr(const PlanNode* node);
PlanNode* joinDataSet(const PlanNode* right, const PlanNode* left);
template <typename T>
T* saveObject(T* obj) const {
return matchCtx_->qctx->objPool()->add(obj);
}
private:
std::unique_ptr<std::vector<storage::cpp2::EdgeProp>> genEdgeProps(const EdgeInfo &edge);
StatusOr<std::vector<storage::cpp2::EdgeProp>> buildAllEdgeProp();
MatchClauseContext* matchCtx_;
Expression** initialExpr_;
MatchClauseContext* matchCtx_;
std::unique_ptr<Expression> initialExpr_;
bool reversely_{false};
PlanNode* dependency_{nullptr};
std::string inputVar_;
};
} // namespace graph
} // namespace nebula
......
......@@ -18,10 +18,24 @@ PlanNode* InnerJoinStrategy::connect(const PlanNode* left, const PlanNode* right
}
PlanNode* InnerJoinStrategy::joinDataSet(const PlanNode* left, const PlanNode* right) {
auto& leftKey = left->colNamesRef().back();
auto& rightKey = right->colNamesRef().front();
auto buildExpr = MatchSolver::getLastEdgeDstExprInLastPath(leftKey);
auto probeExpr = MatchSolver::getFirstVertexVidInFistPath(rightKey);
Expression* buildExpr = nullptr;
if (leftPos_ == JoinPos::kStart) {
auto& leftKey = left->colNamesRef().front();
buildExpr = MatchSolver::getStartVidInPath(leftKey);
} else {
auto& leftKey = left->colNamesRef().back();
buildExpr = MatchSolver::getEndVidInPath(leftKey);
}
Expression* probeExpr = nullptr;
if (rightPos_ == JoinPos::kStart) {
auto& rightKey = right->colNamesRef().front();
probeExpr = MatchSolver::getStartVidInPath(rightKey);
} else {
auto& rightKey = right->colNamesRef().back();
probeExpr = MatchSolver::getEndVidInPath(rightKey);
}
qctx_->objPool()->add(buildExpr);
qctx_->objPool()->add(probeExpr);
auto join = DataJoin::make(qctx_,
......
......@@ -7,8 +7,8 @@
#ifndef PLANNER_PLANNERS_MATCH_INNERJOINSTRATEGY_H_
#define PLANNER_PLANNERS_MATCH_INNERJOINSTRATEGY_H_
#include "planner/match/SegmentsConnector.h"
#include "planner/PlanNode.h"
#include "planner/match/SegmentsConnectStrategy.h"
namespace nebula {
namespace graph {
......@@ -17,12 +17,30 @@ namespace graph {
*/
class InnerJoinStrategy final : public SegmentsConnectStrategy {
public:
enum class JoinPos : int8_t {
kStart,
kEnd
};
explicit InnerJoinStrategy(QueryContext* qctx) : SegmentsConnectStrategy(qctx) {}
InnerJoinStrategy* leftPos(JoinPos pos) {
leftPos_ = pos;
return this;
}
InnerJoinStrategy* rightPos(JoinPos pos) {
rightPos_ = pos;
return this;
}
PlanNode* connect(const PlanNode* left, const PlanNode* right) override;
private:
PlanNode* joinDataSet(const PlanNode* left, const PlanNode* right);
JoinPos leftPos_{JoinPos::kEnd};
JoinPos rightPos_{JoinPos::kStart};
};
} // namespace graph
} // namespace nebula
......
......@@ -18,6 +18,7 @@
namespace nebula {
namespace graph {
StatusOr<SubPlan> MatchClausePlanner::transform(CypherClauseContextBase* clauseCtx) {
if (clauseCtx->kind != CypherClauseKind::kMatch) {
return Status::Error("Not a valid context for MatchClausePlanner.");
......@@ -27,34 +28,39 @@ StatusOr<SubPlan> MatchClausePlanner::transform(CypherClauseContextBase* clauseC
auto& nodeInfos = matchClauseCtx->nodeInfos;
auto& edgeInfos = matchClauseCtx->edgeInfos;
SubPlan matchClausePlan;
int64_t startIndex = -1;
size_t startIndex = 0;
bool startFromEdge = false;
NG_RETURN_IF_ERROR(findStarts(matchClauseCtx, startIndex, matchClausePlan));
NG_RETURN_IF_ERROR(expand(nodeInfos, edgeInfos, matchClauseCtx, startIndex, matchClausePlan));
NG_RETURN_IF_ERROR(projectColumnsBySymbols(matchClauseCtx, &matchClausePlan));
NG_RETURN_IF_ERROR(findStarts(matchClauseCtx, startFromEdge, startIndex, matchClausePlan));
NG_RETURN_IF_ERROR(
expand(nodeInfos, edgeInfos, matchClauseCtx, startFromEdge, startIndex, matchClausePlan));
NG_RETURN_IF_ERROR(projectColumnsBySymbols(matchClauseCtx, startIndex, matchClausePlan));
NG_RETURN_IF_ERROR(appendFilterPlan(matchClauseCtx, matchClausePlan));
return matchClausePlan;
}
Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx,
int64_t& startIndex,
bool& startFromEdge,
size_t& startIndex,
SubPlan& matchClausePlan) {
auto& nodeInfos = matchClauseCtx->nodeInfos;
auto& edgeInfos = matchClauseCtx->edgeInfos;
auto& startVidFinders = StartVidFinder::finders();
bool foundStart = false;
// Find the start plan node
for (size_t i = 0; i < nodeInfos.size(); ++i) {
for (auto& finder : startVidFinders) {
for (auto& finder : startVidFinders) {
for (size_t i = 0; i < nodeInfos.size() && !foundStart; ++i) {
auto nodeCtx = NodeContext(matchClauseCtx, &nodeInfos[i]);
auto finderObj = finder();
if (finderObj->match(&nodeCtx)) {
auto plan = finderObj->transform(&nodeCtx);
auto nodeFinder = finder();
if (nodeFinder->match(&nodeCtx)) {
auto plan = nodeFinder->transform(&nodeCtx);
if (!plan.ok()) {
return plan.status();
}
matchClausePlan = std::move(plan).value();
startIndex = i;
initialExpr_ = nodeCtx.initialExpr;
foundStart = true;
initialExpr_ = nodeCtx.initialExpr->clone();
VLOG(1) << "Find starts: " << startIndex
<< " node: " << matchClausePlan.root->outputVar()
<< " colNames: " << folly::join(",", matchClausePlan.root->colNames());
......@@ -63,22 +69,25 @@ Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx,
if (i != nodeInfos.size() - 1) {
auto edgeCtx = EdgeContext(matchClauseCtx, &edgeInfos[i]);
if (finderObj->match(&edgeCtx)) {
auto plan = finderObj->transform(&edgeCtx);
auto edgeFinder = finder();
if (edgeFinder->match(&edgeCtx)) {
auto plan = edgeFinder->transform(&edgeCtx);
if (!plan.ok()) {
return plan.status();
}
matchClausePlan = std::move(plan).value();
startFromEdge = true;
startIndex = i;
foundStart = true;
break;
}
}
}
if (startIndex != -1) {
if (foundStart) {
break;
}
}
if (startIndex < 0) {
if (!foundStart) {
return Status::Error("Can't solve the start vids from the sentence: %s",
matchClauseCtx->sentence->toString().c_str());
}
......@@ -89,23 +98,121 @@ Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx,
Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
int64_t startIndex,
bool startFromEdge,
size_t startIndex,
SubPlan& subplan) {
// Do expand from startIndex and connect the the subplans.
// TODO: Only support start from the head node now.
if (startIndex != 0) {
return Status::Error("Only support start from the head node parttern.");
if (startFromEdge) {
return expandFromEdge(nodeInfos, edgeInfos, matchClauseCtx, startIndex, subplan);
} else {
return expandFromNode(nodeInfos, edgeInfos, matchClauseCtx, startIndex, subplan);
}
}
Status MatchClausePlanner::expandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan) {
SubPlan rightExpandPlan = subplan;
NG_RETURN_IF_ERROR(
rightExpandFromNode(nodeInfos, edgeInfos, matchClauseCtx, startIndex, rightExpandPlan));
std::vector<std::string> joinColNames = {folly::stringPrintf("%s_%d", kPathStr, 0)};
for (size_t i = 0; i < edgeInfos.size(); ++i) {
if (startIndex > 0) {
auto left = rightExpandPlan.root;
SubPlan leftExpandPlan = rightExpandPlan;
NG_RETURN_IF_ERROR(leftExpandFromNode(nodeInfos,
edgeInfos,
matchClauseCtx,
startIndex,
subplan.root->outputVar(),
leftExpandPlan));
rightExpandPlan.root = leftExpandPlan.root;
if (startIndex < nodeInfos.size() - 1) {
// Connect the left expand and right expand part.
auto right = leftExpandPlan.root;
rightExpandPlan.root =
SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx,
left,
right,
InnerJoinStrategy::JoinPos::kStart,
InnerJoinStrategy::JoinPos::kStart);
}
}
subplan = rightExpandPlan;
return Status::OK();
}
Status MatchClausePlanner::leftExpandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
std::string inputVar,
SubPlan& subplan) {
std::vector<std::string> joinColNames = {
folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size())};
for (size_t i = startIndex; i > 0; --i) {
auto left = subplan.root;
auto status = std::make_unique<Expand>(matchClauseCtx, &initialExpr_)
->doExpand(nodeInfos[i], edgeInfos[i], subplan.root, &subplan);
auto status = std::make_unique<Expand>(matchClauseCtx,
i == startIndex ? initialExpr_->clone() : nullptr)
->depends(subplan.root)
->inputVar(inputVar)
->reversely()
->doExpand(nodeInfos[i], edgeInfos[i - 1], &subplan);
if (!status.ok()) {
return status;
}
if (i > 0) {
if (i < startIndex) {
auto right = subplan.root;
VLOG(1) << "left: " << folly::join(",", left->colNames())
<< " right: " << folly::join(",", right->colNames());
subplan.root = SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx, left, right);
joinColNames.emplace_back(
folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size() + i));
subplan.root->setColNames(joinColNames);
}
inputVar = subplan.root->outputVar();
}
VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar();
auto left = subplan.root;
NG_RETURN_IF_ERROR(
appendFetchVertexPlan(nodeInfos.front().filter,
matchClauseCtx->space,
matchClauseCtx->qctx,
edgeInfos.empty() ? initialExpr_->clone().release() : nullptr,
subplan));
if (!edgeInfos.empty()) {
auto right = subplan.root;
VLOG(1) << "left: " << folly::join(",", left->colNames())
<< " right: " << folly::join(",", right->colNames());
subplan.root = SegmentsConnector::innerJoinSegments(matchClauseCtx->qctx, left, right);
joinColNames.emplace_back(
folly::stringPrintf("%s_%lu", kPathStr, nodeInfos.size() + startIndex));
subplan.root->setColNames(joinColNames);
}
VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar();
return Status::OK();
}
Status MatchClausePlanner::rightExpandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan) {
std::vector<std::string> joinColNames = {folly::stringPrintf("%s_%lu", kPathStr, startIndex)};
for (size_t i = startIndex; i < edgeInfos.size(); ++i) {
auto left = subplan.root;
auto status =
std::make_unique<Expand>(matchClauseCtx,
i == startIndex ? initialExpr_->clone() : nullptr)
->depends(subplan.root)
->inputVar(subplan.root->outputVar())
->doExpand(nodeInfos[i], edgeInfos[i], &subplan);
if (!status.ok()) {
return status;
}
if (i > startIndex) {
auto right = subplan.root;
VLOG(1) << "left: " << folly::join(",", left->colNames())
<< " right: " << folly::join(",", right->colNames());
......@@ -117,8 +224,12 @@ Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos,
VLOG(1) << "root: " << subplan.root->outputVar() << " tail: " << subplan.tail->outputVar();
auto left = subplan.root;
NG_RETURN_IF_ERROR(appendFetchVertexPlan(
nodeInfos.back().filter, matchClauseCtx->qctx, matchClauseCtx->space, &subplan));
NG_RETURN_IF_ERROR(
appendFetchVertexPlan(nodeInfos.back().filter,
matchClauseCtx->space,
matchClauseCtx->qctx,
edgeInfos.empty() ? initialExpr_->clone().release() : nullptr,
subplan));
if (!edgeInfos.empty()) {
auto right = subplan.root;
VLOG(1) << "left: " << folly::join(",", left->colNames())
......@@ -132,14 +243,29 @@ Status MatchClausePlanner::expand(const std::vector<NodeInfo>& nodeInfos,
return Status::OK();
}
Status MatchClausePlanner::expandFromEdge(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan) {
UNUSED(nodeInfos);
UNUSED(edgeInfos);
UNUSED(matchClauseCtx);
UNUSED(startIndex);
UNUSED(subplan);
return Status::Error("Expand from edge has not been implemented yet.");
}
Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter,
const SpaceInfo& space,
QueryContext* qctx,
SpaceInfo& space,
SubPlan* plan) {
MatchSolver::extractAndDedupVidColumn(qctx, &initialExpr_, plan);
Expression* initialExpr,
SubPlan& plan) {
MatchSolver::extractAndDedupVidColumn(
qctx, initialExpr, plan.root, plan.root->outputVar(), plan);
auto srcExpr = ExpressionUtils::inputPropExpr(kVid);
auto gv = GetVertices::make(
qctx, plan->root, space.id, qctx->objPool()->add(srcExpr.release()), {}, {});
qctx, plan.root, space.id, qctx->objPool()->add(srcExpr.release()), {}, {});
PlanNode* root = gv;
if (nodeFilter != nullptr) {
......@@ -158,6 +284,7 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter,
return new VertexExpression();
});
filter->accept(&visitor);
qctx->objPool()->add(filter);
root = Filter::make(qctx, root, filter);
}
......@@ -166,35 +293,54 @@ Status MatchClausePlanner::appendFetchVertexPlan(const Expression* nodeFilter,
auto pathExpr = std::make_unique<PathBuildExpression>();
pathExpr->add(std::make_unique<VertexExpression>());
columns->addColumn(new YieldColumn(pathExpr.release()));
plan->root = Project::make(qctx, root, columns);
plan->root->setColNames({kPathStr});
plan.root = Project::make(qctx, root, columns);
plan.root->setColNames({kPathStr});
return Status::OK();
}
Status MatchClausePlanner::projectColumnsBySymbols(MatchClauseContext* matchClauseCtx,
SubPlan* plan) {
size_t startIndex,
SubPlan& plan) {
auto qctx = matchClauseCtx->qctx;
auto& nodeInfos = matchClauseCtx->nodeInfos;
auto& edgeInfos = matchClauseCtx->edgeInfos;
auto columns = qctx->objPool()->add(new YieldColumns);
auto input = plan->root;
auto input = plan.root;
const auto& inColNames = input->colNamesRef();
DCHECK_EQ(inColNames.size(), nodeInfos.size());
auto columns = qctx->objPool()->add(new YieldColumns);
std::vector<std::string> colNames;
auto addNode = [&, this](size_t i) {
auto& nodeInfo = nodeInfos[i];
if (nodeInfo.alias != nullptr && !nodeInfo.anonymous) {
columns->addColumn(buildVertexColumn(inColNames[i], *nodeInfo.alias));
if (i >= startIndex) {
columns->addColumn(
buildVertexColumn(inColNames[i - startIndex], *nodeInfo.alias));
} else if (startIndex == (nodeInfos.size() - 1)) {
columns->addColumn(buildVertexColumn(
inColNames[startIndex - i], *nodeInfo.alias));
} else {
columns->addColumn(buildVertexColumn(
inColNames[nodeInfos.size() - i], *nodeInfo.alias));
}
colNames.emplace_back(*nodeInfo.alias);
}
};
for (size_t i = 0; i < edgeInfos.size(); i++) {
VLOG(1) << "colSize: " << inColNames.size() << "i: " << i
<< " nodesize: " << nodeInfos.size() << " start: " << startIndex;
addNode(i);
auto& edgeInfo = edgeInfos[i];
if (edgeInfo.alias != nullptr && !edgeInfo.anonymous) {
columns->addColumn(buildEdgeColumn(inColNames[i], edgeInfo));
if (i >= startIndex) {
columns->addColumn(buildEdgeColumn(inColNames[i - startIndex], edgeInfo));
} else if (startIndex == (nodeInfos.size() - 1)) {
columns->addColumn(
buildEdgeColumn(inColNames[edgeInfos.size() - 1 - i], edgeInfo));
} else {
columns->addColumn(
buildEdgeColumn(inColNames[edgeInfos.size() - i], edgeInfo));
}
colNames.emplace_back(*edgeInfo.alias);
}
}
......@@ -207,15 +353,16 @@ Status MatchClausePlanner::projectColumnsBySymbols(MatchClauseContext* matchClau
auto iter = std::find_if(aliases.begin(), aliases.end(), [](const auto& alias) {
return alias.second == AliasType::kPath;
});
std::string alias = iter != aliases.end() ? iter->first : qctx->vctx()->anonColGen()->getCol();
columns->addColumn(buildPathColumn(alias, input));
std::string alias =
iter != aliases.end() ? iter->first : qctx->vctx()->anonColGen()->getCol();
columns->addColumn(buildPathColumn(alias, startIndex, inColNames, nodeInfos.size()));
colNames.emplace_back(alias);
auto project = Project::make(qctx, input, columns);
project->setColNames(std::move(colNames));
plan->root = MatchSolver::filtPathHasSameEdge(project, alias, qctx);
VLOG(1) << "root: " << plan->root->outputVar() << " tail: " << plan->tail->outputVar();
plan.root = MatchSolver::filtPathHasSameEdge(project, alias, qctx);
VLOG(1) << "root: " << plan.root->outputVar() << " tail: " << plan.tail->outputVar();
return Status::OK();
}
......@@ -250,12 +397,45 @@ YieldColumn* MatchClausePlanner::buildEdgeColumn(const std::string& colName, Edg
}
YieldColumn* MatchClausePlanner::buildPathColumn(const std::string& alias,
const PlanNode* input) const {
auto pathExpr = std::make_unique<PathBuildExpression>();
for (const auto& colName : input->colNamesRef()) {
pathExpr->add(ExpressionUtils::inputPropExpr(colName));
size_t startIndex,
const std::vector<std::string> colNames,
size_t nodeInfoSize) const {
auto colSize = colNames.size();
DCHECK((nodeInfoSize == colSize) || (nodeInfoSize + 1 == colSize));
size_t bound = 0;
if (colSize > nodeInfoSize) {
bound = colSize - startIndex - 1;
} else if (startIndex == (nodeInfoSize - 1)) {
bound = 0;
} else {
bound = colSize - startIndex;
}
auto rightExpandPath = std::make_unique<PathBuildExpression>();
for (size_t i = 0; i < bound; ++i) {
rightExpandPath->add(ExpressionUtils::inputPropExpr(colNames[i]));
}
auto leftExpandPath = std::make_unique<PathBuildExpression>();
for (size_t i = bound; i < colNames.size(); ++i) {
leftExpandPath->add(ExpressionUtils::inputPropExpr(colNames[i]));
}
auto finalPath = std::make_unique<PathBuildExpression>();
if (leftExpandPath->size() != 0) {
auto args = new ArgumentList();
args->addArgument(std::move(leftExpandPath));
auto reversePath =
std::make_unique<FunctionCallExpression>(new std::string("reversePath"), args);
if (rightExpandPath->size() == 0) {
return new YieldColumn(reversePath.release(), new std::string(alias));
}
finalPath->add(std::move(reversePath));
}
if (rightExpandPath->size() != 0) {
finalPath->add(std::move(rightExpandPath));
}
return new YieldColumn(pathExpr.release(), new std::string(alias));
return new YieldColumn(finalPath.release(), new std::string(alias));
}
Status MatchClausePlanner::appendFilterPlan(MatchClauseContext* matchClauseCtx, SubPlan& subplan) {
......
......@@ -22,32 +22,67 @@ public:
private:
Status findStarts(MatchClauseContext* matchClauseCtx,
int64_t& startIndex,
bool& startFromEdge,
size_t& startIndex,
SubPlan& matchClausePlan);
Status expand(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
int64_t startIndex,
bool startFromEdge,
size_t startIndex,
SubPlan& subplan);
Status expandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan);
PlanNode* joinLeftAndRightExpandPart(QueryContext* qctx, PlanNode* left, PlanNode* right);
Status leftExpandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
std::string inputVar,
SubPlan& subplan);
Status rightExpandFromNode(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan);
Status expandFromEdge(const std::vector<NodeInfo>& nodeInfos,
const std::vector<EdgeInfo>& edgeInfos,
MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& subplan);
Status appendFetchVertexPlan(const Expression* nodeFilter,
const SpaceInfo& space,
QueryContext* qctx,
SpaceInfo& space,
SubPlan* plan);
Expression* initialExpr,
SubPlan& plan);
Status projectColumnsBySymbols(MatchClauseContext* matchClauseCtx, SubPlan *plan);
Status projectColumnsBySymbols(MatchClauseContext* matchClauseCtx,
size_t startIndex,
SubPlan& plan);
YieldColumn* buildVertexColumn(const std::string& colName, const std::string& alias) const;
YieldColumn* buildEdgeColumn(const std::string& colName, EdgeInfo& edge) const;
YieldColumn* buildPathColumn(const std::string& alias, const PlanNode* input) const;
YieldColumn* buildPathColumn(const std::string& alias,
size_t startIndex,
const std::vector<std::string> colNames,
size_t nodeInfoSize) const;
Status appendFilterPlan(MatchClauseContext* matchClauseCtx, SubPlan& subplan);
private:
Expression* initialExpr_;
std::unique_ptr<Expression> initialExpr_;
};
} // namespace graph
} // namespace nebula
......
......@@ -162,33 +162,33 @@ Status MatchSolver::buildFilter(const MatchClauseContext* mctx, SubPlan* plan) {
}
void MatchSolver::extractAndDedupVidColumn(QueryContext* qctx,
Expression** initialExpr,
SubPlan* plan) {
Expression* initialExpr,
PlanNode* dep,
const std::string& inputVar,
SubPlan& plan) {
auto columns = qctx->objPool()->add(new YieldColumns);
auto input = plan->root;
Expression* vidExpr = initialExprOrEdgeDstExpr(input, initialExpr);
auto* var = qctx->symTable()->getVar(inputVar);
Expression* vidExpr = initialExprOrEdgeDstExpr(initialExpr, var->colNames.back());
columns->addColumn(new YieldColumn(vidExpr));
auto project = Project::make(qctx, input, columns);
auto project = Project::make(qctx, dep, columns);
project->setInputVar(inputVar);
project->setColNames({kVid});
auto dedup = Dedup::make(qctx, project);
dedup->setColNames({kVid});
plan->root = dedup;
// plan->tail = dedup;
plan.root = dedup;
}
Expression* MatchSolver::initialExprOrEdgeDstExpr(const PlanNode* node, Expression** initialExpr) {
Expression* vidExpr = *initialExpr;
if (vidExpr != nullptr) {
VLOG(1) << vidExpr->toString();
*initialExpr = nullptr;
Expression* MatchSolver::initialExprOrEdgeDstExpr(Expression* initialExpr,
const std::string& vidCol) {
if (initialExpr != nullptr) {
return initialExpr;
} else {
vidExpr = getLastEdgeDstExprInLastPath(node->colNamesRef().back());
return getEndVidInPath(vidCol);
}
return vidExpr;
}
Expression* MatchSolver::getLastEdgeDstExprInLastPath(const std::string& colName) {
Expression* MatchSolver::getEndVidInPath(const std::string& colName) {
// expr: __Project_2[-1] => path
auto columnExpr = ExpressionUtils::inputPropExpr(colName);
// expr: endNode(path) => vn
......@@ -201,7 +201,7 @@ Expression* MatchSolver::getLastEdgeDstExprInLastPath(const std::string& colName
return new AttributeExpression(endNode.release(), vidExpr.release());
}
Expression* MatchSolver::getFirstVertexVidInFistPath(const std::string& colName) {
Expression* MatchSolver::getStartVidInPath(const std::string& colName) {
// expr: __Project_2[0] => path
auto columnExpr = ExpressionUtils::inputPropExpr(colName);
// expr: startNode(path) => v1
......
......@@ -45,14 +45,17 @@ public:
static Status buildFilter(const MatchClauseContext* mctx, SubPlan* plan);
static void extractAndDedupVidColumn(QueryContext* qctx,
Expression** initialExpr,
SubPlan* plan);
Expression* initialExpr,
PlanNode* dep,
const std::string& inputVar,
SubPlan& plan);
static Expression* initialExprOrEdgeDstExpr(const PlanNode* node, Expression** initialExpr);
static Expression* initialExprOrEdgeDstExpr(Expression* initialExpr,
const std::string& vidCol);
static Expression* getLastEdgeDstExprInLastPath(const std::string& colName);
static Expression* getEndVidInPath(const std::string& colName);
static Expression* getFirstVertexVidInFistPath(const std::string& colName);
static Expression* getStartVidInPath(const std::string& colName);
static PlanNode* filtPathHasSameEdge(PlanNode* input,
const std::string& column,
......
......@@ -74,7 +74,7 @@ StatusOr<SubPlan> PropIndexSeek::transformNode(NodeContext* nodeCtx) {
plan.root = scan;
// initialize start expression in project node
nodeCtx->initialExpr = ExpressionUtils::newVarPropExpr(kVid);
nodeCtx->initialExpr = std::unique_ptr<Expression>(ExpressionUtils::newVarPropExpr(kVid));
return plan;
}
......
/* Copyright (c) 2020 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
#ifndef PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_
#define PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_
namespace nebula {
namespace graph {
class SegmentsConnectStrategy {
public:
explicit SegmentsConnectStrategy(QueryContext* qctx) : qctx_(qctx) {}
virtual ~SegmentsConnectStrategy() = default;
virtual PlanNode* connect(const PlanNode* left, const PlanNode* right) = 0;
protected:
QueryContext* qctx_;
};
} // namespace graph
} // namespace nebula
#endif // PLANNER_MATCH_SEGMENTSCONNECTSTRATEGY_H_
......@@ -7,7 +7,6 @@
#include "planner/match/SegmentsConnector.h"
#include "planner/match/AddDependencyStrategy.h"
#include "planner/match/AddInputStrategy.h"
#include "planner/match/InnerJoinStrategy.h"
#include "planner/match/CartesianProductStrategy.h"
namespace nebula {
......@@ -36,9 +35,14 @@ StatusOr<SubPlan> SegmentsConnector::connectSegments(CypherClauseContextBase* le
}
PlanNode* SegmentsConnector::innerJoinSegments(QueryContext* qctx,
const PlanNode* left,
const PlanNode* right) {
return std::make_unique<InnerJoinStrategy>(qctx)->connect(left, right);
const PlanNode* left,
const PlanNode* right,
InnerJoinStrategy::JoinPos leftPos,
InnerJoinStrategy::JoinPos rightPos) {
return std::make_unique<InnerJoinStrategy>(qctx)
->leftPos(leftPos)
->rightPos(rightPos)
->connect(left, right);
}
PlanNode* SegmentsConnector::cartesianProductSegments(QueryContext* qctx,
......
......@@ -11,6 +11,7 @@
#include "context/ast/QueryAstContext.h"
#include "planner/PlanNode.h"
#include "planner/Planner.h"
#include "planner/match/InnerJoinStrategy.h"
namespace nebula {
namespace graph {
......@@ -36,9 +37,12 @@ public:
SubPlan& left,
SubPlan& right);
static PlanNode* innerJoinSegments(QueryContext* qctx,
const PlanNode* left,
const PlanNode* right);
static PlanNode* innerJoinSegments(
QueryContext* qctx,
const PlanNode* left,
const PlanNode* right,
InnerJoinStrategy::JoinPos leftPos = InnerJoinStrategy::JoinPos::kEnd,
InnerJoinStrategy::JoinPos rightPos = InnerJoinStrategy::JoinPos::kStart);
static PlanNode* cartesianProductSegments(QueryContext* qctx,
const PlanNode* left,
......@@ -48,18 +52,6 @@ public:
static void addInput(const PlanNode* left, const PlanNode* right, bool copyColNames = false);
};
class SegmentsConnectStrategy {
public:
explicit SegmentsConnectStrategy(QueryContext* qctx) : qctx_(qctx) {}
virtual ~SegmentsConnectStrategy() = default;
virtual PlanNode* connect(const PlanNode* left, const PlanNode* right) = 0;
protected:
QueryContext* qctx_;
};
} // namespace graph
} // namespace nebula
#endif // PLANNER_MATCH_SEGMENTSCONNECTOR_H_
......@@ -122,7 +122,7 @@ StatusOr<SubPlan> VertexIdSeek::transformNode(NodeContext* nodeCtx) {
plan.root = passThrough;
plan.tail = passThrough;
nodeCtx->initialExpr = vidsResult.second;
nodeCtx->initialExpr = std::unique_ptr<Expression>(vidsResult.second);
VLOG(1) << "root: " << plan.root->kind() << " tail: " << plan.tail->kind();
return plan;
}
......
......@@ -231,6 +231,16 @@ Status MatchValidator::buildEdgeInfo(const MatchPath *path,
edgeInfos[i].edgeTypes.emplace_back(etype.value());
edgeInfos[i].types.emplace_back(*type);
}
} else {
const auto allEdgesResult =
matchCtx_->qctx->schemaMng()->getAllVerEdgeSchema(space_.id);
NG_RETURN_IF_ERROR(allEdgesResult);
const auto allEdges = std::move(allEdgesResult).value();
for (const auto &edgeSchema : allEdges) {
edgeInfos[i].edgeTypes.emplace_back(edgeSchema.first);
// TODO:
// edgeInfos[i].types.emplace_back(*type);
}
}
auto *stepRange = edge->range();
if (stepRange != nullptr) {
......
......@@ -1359,6 +1359,81 @@ TEST_F(QueryValidatorTest, TestMatch) {
};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "MATCH (v1)-[e:serve*2..3{start_year: 2000}]-(v2) "
"WHERE id(v1) == \"LeBron James\""
"RETURN v1, v2";
std::vector<PlanNode::Kind> expected = {
PK::kProject,
PK::kFilter,
PK::kFilter,
PK::kProject,
PK::kDataJoin,
PK::kProject,
PK::kGetVertices,
PK::kDedup,
PK::kProject,
PK::kFilter,
PK::kUnion,
PK::kPassThrough,
PK::kUnion,
PK::kFilter,
PK::kPassThrough,
PK::kPassThrough,
PK::kProject,
PK::kFilter,
PK::kProject,
PK::kDataJoin,
PK::kProject,
PK::kFilter,
PK::kProject,
PK::kDataJoin,
PK::kGetNeighbors,
PK::kFilter,
PK::kProject,
PK::kDedup,
PK::kGetNeighbors,
PK::kFilter,
PK::kProject,
PK::kDedup,
PK::kGetNeighbors,
PK::kPassThrough,
PK::kProject,
PK::kDedup,
PK::kStart,
PK::kProject,
};
EXPECT_TRUE(checkResult(query, expected));
}
{
std::string query = "MATCH p = (n)-[]-(m:person{name:\"LeBron James\"}) RETURN p";
std::vector<PlanNode::Kind> expected = {
PK::kProject,
PK::kFilter,
PK::kProject,
PK::kDataJoin,
PK::kProject,
PK::kGetVertices,
PK::kDedup,
PK::kProject,
PK::kFilter,
PK::kPassThrough,
PK::kProject,
PK::kFilter,
PK::kGetNeighbors,
PK::kDedup,
PK::kProject,
PK::kDataJoin,
PK::kProject,
PK::kFilter,
PK::kGetVertices,
PK::kDedup,
PK::kProject,
PK::kIndexScan,
PK::kStart,
};
EXPECT_TRUE(checkResult(query, expected));
}
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment