From 69ad4027ef9ef6ef9e297bdbe1d25149789bb9d9 Mon Sep 17 00:00:00 2001 From: "mingquan.ji" Date: Fri, 25 Mar 2022 16:19:56 +0800 Subject: [PATCH] concurrent execute --- .../executor/algo/ShortestPathExecutor.cpp | 192 +++++++------ .../executor/algo/ShortestPathExecutor.h | 38 ++- src/graph/executor/query/TraverseExecutor.cpp | 4 +- src/graph/planner/plan/Query.h | 2 +- src/parser/MatchPath.h | 15 + src/parser/MatchSentence.h | 259 ------------------ 6 files changed, 150 insertions(+), 360 deletions(-) diff --git a/src/graph/executor/algo/ShortestPathExecutor.cpp b/src/graph/executor/algo/ShortestPathExecutor.cpp index e90bab0b005..4f2371a2b64 100644 --- a/src/graph/executor/algo/ShortestPathExecutor.cpp +++ b/src/graph/executor/algo/ShortestPathExecutor.cpp @@ -12,72 +12,104 @@ folly::Future ShortestPathExecutor::execute() { SCOPED_TIMER(&execTime_); single_ = pathNode_->single(); range_ = {pathNode_->stepRange()->min(), pathNode_->stepRange()->max()}; - NG_RETURN_IF_ERROR(buildRequestDataSet()); + auto& colNames = pathNode_->colNames(); + auto rowSize = buildRequestDataSet(); std::vector> futures; - for (size_t i = 0; i < cartesianProduct_.size(); ++i) { - futures.emplace_back(shortestPath(i)); + for (size_t rowNum = 0; rowNum < rowSize; ++rowNum) { + resultDs_[rowNum].colNames = colNames; + futures.emplace_back(shortestPath(rowNum, 0)); } - return folly::collect(futures).via(runner()).thenValue([this](auto&& resps) { + return folly::collect(futures).via(runner()).thenValue([this, &colNames](auto&& resps) { for (auto& resp : resps) { NG_RETURN_IF_ERROR(resp); } - return finish(ResultBuilder().value(Value(std::move(resultDs_))).build()); + // append dataset + DataSet result; + result.colNames = colNames; + for (auto& ds : resultDs_) { + result.append(std::move(ds)); + } + return finish(ResultBuilder().value(Value(std::move(result))).build()); }); } -Status ShortestPathExecutor::buildRequestDataSet() { +size_t ShortestPathExecutor::buildRequestDataSet() { auto iter = ectx_->getResult(pathNode_->inputVar()).iter(); - cartesianProduct_.reserve(iter->size()); - const auto& vidType = *(qctx()->rctx()->session()->space().spaceDesc.vid_type_ref()); + auto rowSize = iter->size(); + leftVids_.reserve(rowSize); + rightVids_.reserve(rowSize); + leftVisitedVids_.resize(rowSize); + rightVisitedVids_.resize(rowSize); + allLeftSteps_.resize(rowSize); + allRightSteps_.reserve(rowSize); + resultDs_.resize(rowSize); + const auto& vidType = *(qctx()->rctx()->session()->space().spaceDesc.vid_type_ref()); std::unordered_set> uniqueSet; - uniqueSet.reserve(iter->size()); + uniqueSet.reserve(rowSize); for (; iter->valid(); iter->next()) { auto start = iter->getColumn(0); auto end = iter->getColumn(1); if (!SchemaUtil::isValidVid(start, vidType) || !SchemaUtil::isValidVid(end, vidType)) { - return Status::Error("Mismatched vid type, space vid type: %s", - SchemaUtil::typeToString(vidType).c_str()); + LOG(ERROR) << "Mismatched vid type. start type : " << start.type() + << ", end type: " << end.type() + << ", space vid type: " << SchemaUtil::typeToString(vidType); + continue; } if (start == end) { // continue or return error continue; } if (uniqueSet.emplace(std::pair{start, end}).second) { - cartesianProduct_.emplace_back(std::move(start), std::move(end)); + // set origin rightStep_; + std::unordered_map> steps; + std::vector dummy; + steps.emplace(end, std::move(dummy)); + std::vector>> originRightStep({std::move(steps)}); + allRightSteps_.emplace_back(std::move(originRightStep)); + + DataSet startDs, endDs; + startDs.rows.emplace_back(Row({std::move(start)})); + endDs.rows.emplace_back(Row({std::move(end)})); + leftVids_.emplace_back(std::move(startDs)); + rightVids_.emplace_back(std::move(endDs)); } } - if (!cartesianProduct_.empty()) { - leftVids_.rows.emplace_back(Row({std::move(cartesianProduct_.front().first)})); - rightVids_.rows.emplace_back(Row({std::move(cartesianProduct_.front().second)})); - } - return Status::OK(); + return rowSize; } -folly::Future ShortestPathExecutor::shortestPath(size_t i) { +folly::Future ShortestPathExecutor::shortestPath(size_t rowNum, size_t stepNum) { + VLOG(1) << "current rowNum is : " << rowNum << " step is :" << stepNum; std::vector> futures; - futures.emplace_back(getNeighbors(false)); - futures.emplace_back(getNeighbors(true)); - return folly::collect(futures).via(runner()).thenValue([this, i](auto&& resps) { + futures.emplace_back(getNeighbors(rowNum, false)); + futures.emplace_back(getNeighbors(rowNum, true)); + return folly::collect(futures).via(runner()).thenValue([this, rowNum, stepNum](auto&& resps) { for (auto& resp : resps) { if (!resp.ok()) { return folly::makeFuture(std::move(resp)); } } - return handleResponse(i); + return handleResponse(rowNum, stepNum); }); } -folly::Future ShortestPathExecutor::getNeighbors(bool reverse) { +folly::Future ShortestPathExecutor::getNeighbors(size_t rowNum, bool reverse) { + if (reverse) { + VLOG(1) << "reverse GetNeightbor input : " << rightVids_[rowNum].toString(); + } else { + VLOG(1) << "GetNeightbor input : " << leftVids_[rowNum].toString(); + } StorageClient* storageClient = qctx_->getStorageClient(); + time::Duration getNbrTime; storage::StorageClient::CommonRequestParam param(pathNode_->space(), qctx()->rctx()->session()->id(), qctx()->plan()->id(), qctx()->plan()->isProfileEnabled()); + auto& inputRows = reverse ? rightVids_[rowNum].rows : leftVids_[rowNum].rows; return storageClient ->getNeighbors(param, {nebula::kVid}, - reverse ? std::move(rightVids_.rows) : std::move(leftVids_.rows), + std::move(inputRows), {}, pathNode_->edgeDirection(), nullptr, @@ -90,31 +122,32 @@ folly::Future ShortestPathExecutor::getNeighbors(bool reverse) { -1, nullptr) .via(runner()) - .thenValue([this, reverse](auto&& resp) { return buildPath(std::move(resp), reverse); }); + .ensure([this, rowNum, getNbrTime]() { + SCOPED_TIMER(&execTime_); + // otherStats_.emplace("rowNum", rowNum); + otherStats_.emplace("total_rpc_time", folly::sformat("{}(us)", getNbrTime.elapsedInUSec())); + }) + .thenValue([this, rowNum, reverse](auto&& resp) { + SCOPED_TIMER(&execTime_); + addStats(resp, otherStats_); + return buildPath(rowNum, std::move(resp), reverse); + }); } -folly::Future ShortestPathExecutor::handleResponse(size_t i) { - if (conjunctPath()) { - if (i < cartesianProduct_.size() - 1) { - leftVids_.rows = {Row({std::move(cartesianProduct_[i + 1].first)})}; - rightVids_.rows = {Row({std::move(cartesianProduct_[i + 1].second)})}; - } - leftVisitedVids_.clear(); - rightVisitedVids_.clear(); - allLeftSteps_.clear(); - allRightSteps_.clear(); +folly::Future ShortestPathExecutor::handleResponse(size_t rowNum, size_t stepNum) { + if (conjunctPath(rowNum, stepNum)) { return Status::OK(); } - step_++; - if (step_ > 10) { + stepNum++; + if (stepNum * 2 >= range_.second) { return Status::OK(); } - return shortestPath(i); + return shortestPath(rowNum, stepNum); } -bool ShortestPathExecutor::conjunctPath() { - const auto& leftStep = allLeftSteps_.back(); - const auto& prevRightStep = allRightSteps_[step_ - 1]; +bool ShortestPathExecutor::conjunctPath(size_t rowNum, size_t stepNum) { + const auto& leftStep = allLeftSteps_[rowNum].back(); + const auto& prevRightStep = allRightSteps_[rowNum][stepNum]; std::vector meetVids; for (const auto& step : leftStep) { if (prevRightStep.find(step.first) != prevRightStep.end()) { @@ -122,11 +155,14 @@ bool ShortestPathExecutor::conjunctPath() { } } if (!meetVids.empty()) { - buildOddPath(meetVids); + buildOddPath(rowNum, meetVids); return true; } + if (stepNum * 2 >= range_.second) { + return false; + } - const auto& rightStep = allRightSteps_.back(); + const auto& rightStep = allRightSteps_[rowNum].back(); for (const auto& step : leftStep) { if (rightStep.find(step.first) != rightStep.end()) { meetVids.push_back(step.first); @@ -135,10 +171,10 @@ bool ShortestPathExecutor::conjunctPath() { if (meetVids.empty()) { return false; } - return buildEvenPath(meetVids); + return buildEvenPath(rowNum, meetVids); } -Status ShortestPathExecutor::buildPath(RpcResponse&& resps, bool reverse) { +Status ShortestPathExecutor::buildPath(size_t rowNum, RpcResponse&& resps, bool reverse) { auto result = handleCompleteness(resps, FLAGS_accept_partial_success); NG_RETURN_IF_ERROR(result); auto& responses = std::move(resps).responses(); @@ -151,26 +187,21 @@ Status ShortestPathExecutor::buildPath(RpcResponse&& resps, bool reverse) { } list.values.emplace_back(std::move(*dataset)); } + if (reverse) { + VLOG(1) << "reverse GetNeightbor output: " << list.toString().c_str(); + } else { + VLOG(1) << "GetNeightbor output: " << list.toString().c_str(); + } auto listVal = std::make_shared(std::move(list)); auto iter = std::make_unique(listVal); - return doBuildPath(iter.get(), reverse); + return doBuildPath(rowNum, iter.get(), reverse); } -Status ShortestPathExecutor::doBuildPath(GetNeighborsIter* iter, bool reverse) { +Status ShortestPathExecutor::doBuildPath(size_t rowNum, GetNeighborsIter* iter, bool reverse) { auto iterSize = iter->size(); - auto& visitedVids = reverse ? rightVisitedVids_ : leftVisitedVids_; + auto& visitedVids = reverse ? rightVisitedVids_[rowNum] : leftVisitedVids_[rowNum]; visitedVids.reserve(visitedVids.size() + iterSize); - auto& allSteps = reverse ? allRightSteps_ : allLeftSteps_; - if (reverse && step_ == 0) { - std::unordered_map> steps; - for (; iter->valid(); iter->next()) { - // set oright rightPath_; - auto& vid = iter->getColumn(0); - std::vector dummpy; - steps.emplace(vid, std::move(dummpy)); - } - allSteps.emplace_back(std::move(steps)); - } + auto& allSteps = reverse ? allRightSteps_[rowNum] : allLeftSteps_[rowNum]; allSteps.emplace_back(); auto& currentStep = allSteps.back(); @@ -224,29 +255,31 @@ Status ShortestPathExecutor::doBuildPath(GetNeighborsIter* iter, bool reverse) { visitedVids.insert(std::make_move_iterator(uniqueDst.begin()), std::make_move_iterator(uniqueDst.end())); if (reverse) { - rightVids_.rows.swap(nextStepVids); + rightVids_[rowNum].rows.swap(nextStepVids); + VLOG(1) << "reverse next Vid : " << rightVids_[rowNum].toString() << " rowNum" << rowNum; } else { - leftVids_.rows.swap(nextStepVids); + leftVids_[rowNum].rows.swap(nextStepVids); + VLOG(1) << "current next Vid : " << leftVids_[rowNum].toString() << " rowNum " << rowNum; } return Status::OK(); } -void ShortestPathExecutor::buildOddPath(const std::vector& meetVids) { +void ShortestPathExecutor::buildOddPath(size_t rowNum, const std::vector& meetVids) { for (auto& meetVid : meetVids) { - auto leftPaths = createLeftPath(meetVid); - auto rightPaths = createRightPath(meetVid, true); + auto leftPaths = createLeftPath(rowNum, meetVid); + auto rightPaths = createRightPath(rowNum, meetVid, true); for (auto& leftPath : leftPaths) { for (auto& rightPath : rightPaths) { Row path = leftPath; auto& steps = path.values.back().mutableList().values; steps.insert(steps.end(), rightPath.values.begin(), rightPath.values.end()); - resultDs_.rows.emplace_back(std::move(path)); + resultDs_[rowNum].rows.emplace_back(std::move(path)); } } } } -bool ShortestPathExecutor::buildEvenPath(const std::vector& meetVids) { +bool ShortestPathExecutor::buildEvenPath(size_t rowNum, const std::vector& meetVids) { std::vector meetVertices; auto status = getMeetVidsProps(meetVids, meetVertices).get(); if (!status.ok() || meetVertices.empty()) { @@ -254,33 +287,34 @@ bool ShortestPathExecutor::buildEvenPath(const std::vector& meetVids) { } for (auto& meetVertex : meetVertices) { auto meetVid = meetVertex.getVertex().vid; - auto leftPaths = createLeftPath(meetVid); - auto rightPaths = createRightPath(meetVid, false); + auto leftPaths = createLeftPath(rowNum, meetVid); + auto rightPaths = createRightPath(rowNum, meetVid, false); for (auto& leftPath : leftPaths) { for (auto& rightPath : rightPaths) { Row path = leftPath; auto& steps = path.values.back().mutableList().values; steps.emplace_back(meetVertex); steps.insert(steps.end(), rightPath.values.begin(), rightPath.values.end()); - resultDs_.rows.emplace_back(std::move(path)); + resultDs_[rowNum].rows.emplace_back(std::move(path)); } } } return true; } -std::vector ShortestPathExecutor::createLeftPath(const Value& meetVid) { - auto& lastSteps = allLeftSteps_.back(); +std::vector ShortestPathExecutor::createLeftPath(size_t rowNum, const Value& meetVid) { + auto& allSteps = allLeftSteps_[rowNum]; + auto& lastSteps = allSteps.back(); auto findMeetVid = lastSteps.find(meetVid); std::vector leftPaths(findMeetVid->second); - for (auto stepIter = allLeftSteps_.rbegin() + 1; stepIter != allLeftSteps_.rend(); ++stepIter) { + for (auto stepIter = allSteps.rbegin() + 1; stepIter != allSteps.rend(); ++stepIter) { std::vector temp; for (auto& leftPath : leftPaths) { Value id = leftPath.values.front().getVertex().vid; auto findId = stepIter->find(id); for (auto& step : findId->second) { auto newPath = leftPath; - newPath.values.insert(leftPath.values.begin(), step.values.begin(), step.values.end()); + newPath.values.insert(newPath.values.begin(), step.values.begin(), step.values.end()); temp.emplace_back(std::move(newPath)); } } @@ -299,9 +333,12 @@ std::vector ShortestPathExecutor::createLeftPath(const Value& meetVid) { return result; } -std::vector ShortestPathExecutor::createRightPath(const Value& meetVid, bool oddStep) { +std::vector ShortestPathExecutor::createRightPath(size_t rowNum, + const Value& meetVid, + bool oddStep) { + auto& allSteps = allRightSteps_[rowNum]; std::vector rightPaths; - auto& lastSteps = allRightSteps_.back(); + auto& lastSteps = allSteps.back(); if (oddStep) { for (auto& steps : lastSteps) { bool flag = false; @@ -321,8 +358,7 @@ std::vector ShortestPathExecutor::createRightPath(const Value& meetVid, boo auto findMeetVid = lastSteps.find(meetVid); rightPaths = findMeetVid->second; } - for (auto stepIter = allRightSteps_.rbegin() + 1; stepIter != allRightSteps_.rend() - 1; - ++stepIter) { + for (auto stepIter = allSteps.rbegin() + 1; stepIter != allSteps.rend() - 1; ++stepIter) { std::vector temp; for (auto& rightPath : rightPaths) { Value id = rightPath.values.front().getVertex().vid; @@ -375,7 +411,7 @@ folly::Future ShortestPathExecutor::getMeetVidsProps(const std::vector execute() override; private: - Status buildRequestDataSet(); + size_t buildRequestDataSet(); - folly::Future getNeighbors(bool reverse); + folly::Future getNeighbors(size_t rowNum, bool reverse); - folly::Future shortestPath(size_t i); + folly::Future shortestPath(size_t rowNum, size_t stepNum); - folly::Future handleResponse(size_t i); + folly::Future handleResponse(size_t rowNum, size_t stepNum); Status handlePropResp(PropRpcResponse&& resps, std::vector& vertices); - Status buildPath(RpcResponse&& resp, bool reverse); + Status buildPath(size_t rowNum, RpcResponse&& resp, bool reverse); folly::Future getMeetVidsProps(const std::vector& meetVids, std::vector& meetVertices); - Status doBuildPath(GetNeighborsIter* iter, bool reverse); + Status doBuildPath(size_t rowNum, GetNeighborsIter* iter, bool reverse); - bool conjunctPath(); + bool conjunctPath(size_t rowNum, size_t stepNum); - bool buildEvenPath(const std::vector& meetVids); + bool buildEvenPath(size_t rowNum, const std::vector& meetVids); - void buildOddPath(const std::vector& meetVids); + void buildOddPath(size_t rowNum, const std::vector& meetVids); - std::vector createRightPath(const Value& meetVid, bool evenStep); + std::vector createRightPath(size_t rowNum, const Value& meetVid, bool evenStep); - std::vector createLeftPath(const Value& meetVid); + std::vector createLeftPath(size_t rowNum, const Value& meetVid); private: const ShortestPath* pathNode_{nullptr}; - size_t step_{0}; std::pair range_; bool single_{true}; - std::vector> cartesianProduct_; - DataSet resultDs_; - DataSet leftVids_; - DataSet rightVids_; - std::unordered_set leftVisitedVids_; - std::unordered_set rightVisitedVids_; - std::vector>> allLeftSteps_; - std::vector>> allRightSteps_; + std::vector resultDs_; + std::vector leftVids_; + std::vector rightVids_; + std::vector> leftVisitedVids_; + std::vector> rightVisitedVids_; + std::vector>>> allLeftSteps_; + std::vector>>> allRightSteps_; }; } // namespace graph diff --git a/src/graph/executor/query/TraverseExecutor.cpp b/src/graph/executor/query/TraverseExecutor.cpp index 2a1ba9e3f35..78e76699697 100644 --- a/src/graph/executor/query/TraverseExecutor.cpp +++ b/src/graph/executor/query/TraverseExecutor.cpp @@ -58,8 +58,8 @@ Status TraverseExecutor::buildRequestDataSet() { for (; iter->valid(); iter->next()) { auto vid = src->eval(ctx(iter)); if (!SchemaUtil::isValidVid(vid, vidType)) { - LOG(WARNING) << "Mismatched vid type: " << vid.type() - << ", space vid type: " << SchemaUtil::typeToString(vidType); + LOG(ERROR) << "Mismatched vid type: " << vid.type() + << ", space vid type: " << SchemaUtil::typeToString(vidType); continue; } // Need copy here, Argument executor may depends on this variable. diff --git a/src/graph/planner/plan/Query.h b/src/graph/planner/plan/Query.h index 6d35fa9080d..6bc36c02f14 100644 --- a/src/graph/planner/plan/Query.h +++ b/src/graph/planner/plan/Query.h @@ -1681,7 +1681,7 @@ class RollUpApply : public BinaryInputNode { std::vector compareCols_; // Collect column to List InputPropertyExpression* collectCol_; -} +}; class ShortestPath final : public SingleInputNode { public: diff --git a/src/parser/MatchPath.h b/src/parser/MatchPath.h index 01d05cd2340..c48eb1c37b3 100644 --- a/src/parser/MatchPath.h +++ b/src/parser/MatchPath.h @@ -301,6 +301,19 @@ class MatchPath final { return edges_[i].get(); } + void setShortestPath() { + shortestPath_.first = true; + shortestPath_.second = true; + } + + void setAllShortestPaths() { + shortestPath_.first = true; + } + + std::pair shortestPath() const { + return shortestPath_; + } + std::string toString() const; MatchPath clone() const { @@ -318,6 +331,8 @@ class MatchPath final { std::unique_ptr alias_; std::vector> nodes_; std::vector> edges_; + // First is `shortest` flag, second is `single` flag + std::pair shortestPath_{false, false}; }; } // namespace nebula diff --git a/src/parser/MatchSentence.h b/src/parser/MatchSentence.h index 23731cd5020..239c52c991e 100644 --- a/src/parser/MatchSentence.h +++ b/src/parser/MatchSentence.h @@ -14,265 +14,6 @@ namespace nebula { -class MatchEdgeTypeList final { - public: - MatchEdgeTypeList() = default; - - void add(std::string* item) { - items_.emplace_back(item); - } - - auto items() && { - return std::move(items_); - } - - private: - std::vector> items_; -}; - -class MatchStepRange final { - public: - explicit MatchStepRange(size_t min = 0, size_t max = std::numeric_limits::max()) { - min_ = min; - max_ = max; - } - - auto min() const { - return min_; - } - - auto max() const { - return max_; - } - - std::string toString() const; - - private: - size_t min_{1}; - size_t max_{1}; -}; - -class MatchEdgeProp final { - public: - MatchEdgeProp(const std::string& alias, - MatchEdgeTypeList* types, - MatchStepRange* range, - Expression* props = nullptr) { - alias_ = alias; - range_.reset(range); - props_ = static_cast(props); - if (types != nullptr) { - types_ = std::move(*types).items(); - delete types; - } - } - - auto get() && { - return std::make_tuple( - std::move(alias_), std::move(types_), std::move(range_), std::move(props_)); - } - - private: - std::string alias_; - std::vector> types_; - MapExpression* props_{nullptr}; - std::unique_ptr range_; -}; - -class MatchEdge final { - public: - using Direction = nebula::storage::cpp2::EdgeDirection; - MatchEdge(MatchEdgeProp* prop, Direction direction) { - if (prop != nullptr) { - auto tuple = std::move(*prop).get(); - alias_ = std::move(std::get<0>(tuple)); - types_ = std::move(std::get<1>(tuple)); - range_ = std::move(std::get<2>(tuple)); - props_ = std::move(std::get<3>(tuple)); - delete prop; - } - direction_ = direction; - } - - auto direction() const { - return direction_; - } - - const std::string& alias() const { - return alias_; - } - - auto& types() const { - return types_; - } - - const MapExpression* props() const { - return props_; - } - - auto* range() const { - return range_.get(); - } - - std::string toString() const; - - private: - Direction direction_; - std::string alias_; - std::vector> types_; - std::unique_ptr range_; - MapExpression* props_{nullptr}; -}; - -class MatchNodeLabel final { - public: - explicit MatchNodeLabel(std::string* label, Expression* props = nullptr) - : label_(label), props_(static_cast(props)) { - DCHECK(props == nullptr || props->kind() == Expression::Kind::kMap); - } - - const std::string* label() const { - return label_.get(); - } - - const MapExpression* props() const { - return props_; - } - - MapExpression* props() { - return props_; - } - - std::string toString() const { - std::stringstream ss; - ss << ":" << *label_; - if (props_ != nullptr) { - ss << props_->toString(); - } - return ss.str(); - } - - private: - std::unique_ptr label_; - MapExpression* props_{nullptr}; -}; - -class MatchNodeLabelList final { - public: - void add(MatchNodeLabel* label) { - labels_.emplace_back(label); - } - - const auto& labels() const { - return labels_; - } - - std::string toString() const { - std::stringstream ss; - for (const auto& label : labels_) { - ss << label->toString(); - } - return ss.str(); - } - - private: - std::vector> labels_; -}; - -class MatchNode final { - public: - MatchNode(const std::string& alias, MatchNodeLabelList* labels, Expression* props = nullptr) { - alias_ = alias; - labels_.reset(labels); - props_ = static_cast(props); - } - - const std::string& alias() const { - return alias_; - } - - const auto* labels() const { - return labels_.get(); - } - - const MapExpression* props() const { - return props_; - } - - MapExpression* props() { - return props_; - } - - std::string toString() const; - - private: - std::string alias_; - std::unique_ptr labels_; - MapExpression* props_{nullptr}; -}; - -class MatchPath final { - public: - explicit MatchPath(MatchNode* node) { - nodes_.emplace_back(node); - } - - void add(MatchEdge* edge, MatchNode* node) { - edges_.emplace_back(edge); - nodes_.emplace_back(node); - } - - void setAlias(std::string* alias) { - alias_.reset(alias); - } - - const std::string* alias() const { - return alias_.get(); - } - - const auto& nodes() const { - return nodes_; - } - - const auto& edges() const { - return edges_; - } - - size_t steps() const { - return edges_.size(); - } - - const MatchNode* node(size_t i) const { - return nodes_[i].get(); - } - - const MatchEdge* edge(size_t i) const { - return edges_[i].get(); - } - - std::string toString() const; - - void setShortestPath() { - shortestPath_.first = true; - shortestPath_.second = true; - } - - void setAllShortestPaths() { - shortestPath_.first = true; - } - - std::pair shortestPath() const { - return shortestPath_; - } - - private: - std::unique_ptr alias_; - std::vector> nodes_; - std::vector> edges_; - // First is `shortest` flag, second is `single` flag - std::pair shortestPath_{false, false}; -}; - class MatchPathList final { public: explicit MatchPathList(MatchPath* path);