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

All path. (#360)

* Add produce all paths executor.

* Implement produce all paths executor.

* Add all path executor test.

* Conjunct all paths and test.

* Refactor conjunct all paths.

* Implement find all paths plan.

* Fix one step path.

* Add it.

* Fix mem leak.

* Remove useless code in conjunct path.

* Address comment.
parent 63edabd4
No related branches found
No related tags found
No related merge requests found
Showing
with 1458 additions and 165 deletions
......@@ -31,6 +31,7 @@ nebula_add_library(
algo/ConjunctPathExecutor.cpp
algo/BFSShortestPathExecutor.cpp
algo/ProduceSemiShortestPathExecutor.cpp
algo/ProduceAllPathsExecutor.cpp
admin/SwitchSpaceExecutor.cpp
admin/CreateUserExecutor.cpp
admin/DropUserExecutor.cpp
......
......@@ -37,6 +37,7 @@
#include "executor/algo/BFSShortestPathExecutor.h"
#include "executor/algo/ProduceSemiShortestPathExecutor.h"
#include "executor/algo/ConjunctPathExecutor.h"
#include "executor/algo/ProduceAllPathsExecutor.h"
#include "executor/logic/LoopExecutor.h"
#include "executor/logic/PassThroughExecutor.h"
#include "executor/logic/SelectExecutor.h"
......@@ -371,6 +372,9 @@ Executor *Executor::makeExecutor(QueryContext *qctx, const PlanNode *node) {
case PlanNode::Kind::kConjunctPath: {
return pool->add(new ConjunctPathExecutor(node, qctx));
}
case PlanNode::Kind::kProduceAllPaths: {
return pool->add(new ProduceAllPathsExecutor(node, qctx));
}
case PlanNode::Kind::kUnknown: {
break;
}
......
......@@ -16,6 +16,8 @@ folly::Future<Status> ConjunctPathExecutor::execute() {
switch (conjunct->pathKind()) {
case ConjunctPath::PathKind::kBiBFS:
return bfsShortestPath();
case ConjunctPath::PathKind::kAllPaths:
return allPaths();
default:
LOG(FATAL) << "Not implement.";
}
......@@ -231,5 +233,88 @@ bool ConjunctPathExecutor::findPath(Iterator* iter,
}
return found;
}
folly::Future<Status> ConjunctPathExecutor::allPaths() {
auto* conjunct = asNode<ConjunctPath>(node());
auto lIter = ectx_->getResult(conjunct->leftInputVar()).iter();
const auto& rHist = ectx_->getHistory(conjunct->rightInputVar());
VLOG(1) << "current: " << node()->outputVar();
VLOG(1) << "left input: " << conjunct->leftInputVar()
<< " right input: " << conjunct->rightInputVar();
VLOG(1) << "right hist size: " << rHist.size();
DCHECK(!!lIter);
auto steps = conjunct->steps();
count_++;
DataSet ds;
ds.colNames = conjunct->colNames();
std::unordered_map<Value, const List&> table;
for (; lIter->valid(); lIter->next()) {
auto& dst = lIter->getColumn(kVid);
auto& path = lIter->getColumn("path");
if (path.isList()) {
VLOG(1) << "Forward dst: " << dst;
table.emplace(dst, path.getList());
}
}
if (rHist.size() >= 2) {
VLOG(1) << "Find odd length path.";
auto previous = rHist[rHist.size() - 2].iter();
findAllPaths(previous.get(), table, ds);
}
if (count_ * 2 <= steps) {
VLOG(1) << "Find even length path.";
auto latest = rHist.back().iter();
findAllPaths(latest.get(), table, ds);
}
return finish(ResultBuilder().value(Value(std::move(ds))).finish());
}
bool ConjunctPathExecutor::findAllPaths(Iterator* backwardPathsIter,
std::unordered_map<Value, const List&>& forwardPathsTable,
DataSet& ds) {
bool found = false;
for (; backwardPathsIter->valid(); backwardPathsIter->next()) {
auto& dst = backwardPathsIter->getColumn(kVid);
VLOG(1) << "Backward dst: " << dst;
auto& pathList = backwardPathsIter->getColumn("path");
if (!pathList.isList()) {
continue;
}
for (const auto& path : pathList.getList().values) {
if (!path.isPath()) {
continue;
}
auto forwardPaths = forwardPathsTable.find(dst);
if (forwardPaths == forwardPathsTable.end()) {
continue;
}
for (const auto& i : forwardPaths->second.values) {
if (!i.isPath()) {
continue;
}
Row row;
auto forward = i.getPath();
Path backward = path.getPath();
VLOG(1) << "Forward path:" << forward;
VLOG(1) << "Backward path:" << backward;
backward.reverse();
VLOG(1) << "Backward reverse path:" << backward;
forward.append(std::move(backward));
VLOG(1) << "Found path: " << forward;
row.values.emplace_back(std::move(forward));
ds.rows.emplace_back(std::move(row));
} // `i'
found = true;
} // `path'
} // `backwardPathsIter'
return found;
}
} // namespace graph
} // namespace nebula
......@@ -21,6 +21,8 @@ public:
private:
folly::Future<Status> bfsShortestPath();
folly::Future<Status> allPaths();
std::vector<Row> findBfsShortestPath(Iterator* iter,
bool isLatest,
std::multimap<Value, const Edge*>& table);
......@@ -33,8 +35,13 @@ private:
bool findPath(Iterator* iter, std::multimap<Value, const Path*>& table, DataSet& ds);
bool findAllPaths(Iterator* backwardPathsIter,
std::unordered_map<Value, const List&>& forwardPathsTable,
DataSet& ds);
std::vector<std::multimap<Value, const Edge*>> forward_;
std::vector<std::multimap<Value, const Edge*>> backward_;
size_t count_{0};
};
} // namespace graph
} // namespace nebula
......
/* 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.
*/
#include "executor/algo/ProduceAllPathsExecutor.h"
#include "planner/Algo.h"
namespace nebula {
namespace graph {
folly::Future<Status> ProduceAllPathsExecutor::execute() {
SCOPED_TIMER(&execTime_);
auto* allPaths = asNode<ProduceAllPaths>(node());
auto iter = ectx_->getResult(allPaths->inputVar()).iter();
DCHECK(!!iter);
DataSet ds;
ds.colNames = node()->colNames();
Interims interims;
if (!iter->isGetNeighborsIter()) {
return Status::Error("Only accept GetNeighbotsIter.");
}
VLOG(1) << "Edge size: " << iter->size();
for (; iter->valid(); iter->next()) {
auto edgeVal = iter->getEdge();
if (!edgeVal.isEdge()) {
continue;
}
auto& edge = edgeVal.getEdge();
auto histPaths = historyPaths_.find(edge.src);
if (histPaths == historyPaths_.end()) {
createPaths(edge, interims);
} else {
buildPaths(histPaths->second, edge, interims);
}
}
for (auto& interim : interims) {
Row row;
auto& dst = interim.first;
auto& paths = interim.second;
row.values.emplace_back(std::move(dst));
row.values.emplace_back(List(std::move(paths)));
ds.rows.emplace_back(std::move(row));
const auto& dstInDs = ds.rows.back().values.front();
for (auto& path : ds.rows.back().values.back().getList().values) {
historyPaths_[dstInDs].emplace_back(&path.getPath());
}
}
count_++;
return finish(ResultBuilder().value(Value(std::move(ds))).finish());
}
void ProduceAllPathsExecutor::createPaths(const Edge& edge, Interims& interims) {
Path path;
path.src = Vertex(edge.src, {});
path.steps.emplace_back(Step(Vertex(edge.dst, {}), edge.type, edge.name, edge.ranking, {}));
VLOG(1) << "Create path: " << path;
interims[edge.dst].emplace_back(std::move(path));
}
void ProduceAllPathsExecutor::buildPaths(const std::vector<const Path*>& history,
const Edge& edge,
Interims& interims) {
for (auto* histPath : history) {
if (histPath->steps.size() < count_) {
continue;
} else {
Path path = *histPath;
path.steps.emplace_back(
Step(Vertex(edge.dst, {}), edge.type, edge.name, edge.ranking, {}));
VLOG(1) << "Build path: " << path;
interims[edge.dst].emplace_back(std::move(path));
}
}
}
} // namespace graph
} // namespace nebula
/* 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 EXECUTOR_ALGO_PRODUCEALLPATHSEXECUTOR_H_
#define EXECUTOR_ALGO_PRODUCEALLPATHSEXECUTOR_H_
#include "executor/Executor.h"
namespace nebula {
namespace graph {
class ProduceAllPathsExecutor final : public Executor {
public:
ProduceAllPathsExecutor(const PlanNode* node, QueryContext* qctx)
: Executor("ProduceAllPaths", node, qctx) {}
folly::Future<Status> execute() override;
private:
// k: dst, v: paths to dst
using HistoryPaths = std::unordered_map<Value, std::vector<const Path*>>;
// k: dst, v: paths to dst
using Interims = std::unordered_map<Value, std::vector<Value>>;
void createPaths(const Edge& edge, Interims& interims);
void buildPaths(const std::vector<const Path*>& history, const Edge& edge, Interims& interims);
size_t count_{0};
HistoryPaths historyPaths_;
};
} // namespace graph
} // namespace nebula
#endif
......@@ -41,6 +41,10 @@ folly::Future<Status> DataCollectExecutor::doCollect() {
NG_RETURN_IF_ERROR(collectBFSShortest(vars));
break;
}
case DataCollect::CollectKind::kAllPaths: {
NG_RETURN_IF_ERROR(collectAllPaths(vars));
break;
}
default:
LOG(FATAL) << "Unknown data collect type: " << static_cast<int64_t>(dc->collectKind());
}
......@@ -155,5 +159,29 @@ Status DataCollectExecutor::collectBFSShortest(const std::vector<std::string>& v
// Will rewrite this method once we implement returning the props for the path.
return rowBasedMove(vars);
}
Status DataCollectExecutor::collectAllPaths(const std::vector<std::string>& vars) {
DataSet ds;
ds.colNames = std::move(colNames_);
DCHECK(!ds.colNames.empty());
for (auto& var : vars) {
auto& hist = ectx_->getHistory(var);
for (auto& result : hist) {
auto iter = result.iter();
if (iter->isSequentialIter()) {
auto* seqIter = static_cast<SequentialIter*>(iter.get());
for (; seqIter->valid(); seqIter->next()) {
ds.rows.emplace_back(seqIter->moveRow());
}
} else {
std::stringstream msg;
msg << "Iterator should be kind of SequentialIter, but was: " << iter->kind();
return Status::Error(msg.str());
}
}
}
result_.setDataSet(std::move(ds));
return Status::OK();
}
} // namespace graph
} // namespace nebula
......@@ -29,6 +29,8 @@ private:
Status collectBFSShortest(const std::vector<std::string>& vars);
Status collectAllPaths(const std::vector<std::string>& vars);
std::vector<std::string> colNames_;
Value result_;
};
......
......@@ -74,6 +74,7 @@ nebula_add_test(
BFSShortestTest.cpp
ConjunctPathTest.cpp
ProduceSemiShortestPathTest.cpp
ProduceAllPathsTest.cpp
OBJECTS
${EXEC_QUERY_TEST_OBJS}
LIBRARIES
......
......@@ -118,6 +118,117 @@ protected:
}
qctx_->ectx()->setResult("backward4", ResultBuilder().value(ds2).finish());
}
qctx_->symTable()->newVariable("all_paths_forward1");
qctx_->symTable()->newVariable("all_paths_backward1");
qctx_->symTable()->newVariable("all_paths_backward2");
qctx_->symTable()->newVariable("all_paths_backward3");
qctx_->symTable()->newVariable("all_paths_backward4");
{
// 1->2
// 1->3
DataSet ds;
ds.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"2", std::move(paths)};
ds.rows.emplace_back(std::move(row));
}
{
Row row;
List paths;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("3", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"3", std::move(paths)};
ds.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_forward1", ResultBuilder().value(ds).finish());
}
{
// 4->7
DataSet ds2;
ds2.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("4", {});
path.steps.emplace_back(Step(Vertex("7", {}), -1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"7", std::move(paths)};
ds2.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_backward1", ResultBuilder().value(ds2).finish());
}
{
// 2
DataSet ds1;
ds1.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("2", {});
paths.values.emplace_back(std::move(path));
row.values = {"2", std::move(paths)};
ds1.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_backward2", ResultBuilder().value(ds1).finish());
// 2->7
DataSet ds2;
ds2.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("2", {});
path.steps.emplace_back(Step(Vertex("7", {}), -1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"7", std::move(paths)};
ds2.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_backward2", ResultBuilder().value(ds2).finish());
}
{
// 4->3
DataSet ds2;
ds2.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("4", {});
path.steps.emplace_back(Step(Vertex("3", {}), -1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"3", std::move(paths)};
ds2.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_backward3", ResultBuilder().value(ds2).finish());
}
{
// 5->4
DataSet ds;
ds.colNames = {kVid, "path"};
{
Row row;
List paths;
Path path;
path.src = Vertex("5", {});
path.steps.emplace_back(Step(Vertex("4", {}), -1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
row.values = {"4", std::move(paths)};
ds.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult("all_paths_backward4", ResultBuilder().value(ds).finish());
}
}
protected:
......@@ -128,7 +239,8 @@ TEST_F(ConjunctPathTest, BiBFSNoPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kBiBFS);
ConjunctPath::PathKind::kBiBFS,
5);
conjunct->setLeftVar("forward1");
conjunct->setRightVar("backward1");
conjunct->setColNames({"_path"});
......@@ -149,7 +261,8 @@ TEST_F(ConjunctPathTest, BiBFSOneStepPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kBiBFS);
ConjunctPath::PathKind::kBiBFS,
5);
conjunct->setLeftVar("forward1");
conjunct->setRightVar("backward2");
conjunct->setColNames({"_path"});
......@@ -177,7 +290,8 @@ TEST_F(ConjunctPathTest, BiBFSTwoStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kBiBFS);
ConjunctPath::PathKind::kBiBFS,
5);
conjunct->setLeftVar("forward1");
conjunct->setRightVar("backward3");
conjunct->setColNames({"_path"});
......@@ -206,7 +320,8 @@ TEST_F(ConjunctPathTest, BiBFSThreeStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kBiBFS);
ConjunctPath::PathKind::kBiBFS,
5);
conjunct->setLeftVar("forward1");
conjunct->setRightVar("backward4");
conjunct->setColNames({"_path"});
......@@ -291,7 +406,8 @@ TEST_F(ConjunctPathTest, BiBFSFourStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kBiBFS);
ConjunctPath::PathKind::kBiBFS,
5);
conjunct->setLeftVar("forward1");
conjunct->setRightVar("backward4");
conjunct->setColNames({"_path"});
......@@ -373,5 +489,274 @@ TEST_F(ConjunctPathTest, BiBFSFourStepsPath) {
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
}
TEST_F(ConjunctPathTest, AllPathsNoPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kAllPaths,
5);
conjunct->setLeftVar("all_paths_forward1");
conjunct->setRightVar("all_paths_backward1");
conjunct->setColNames({"_path"});
auto conjunctExe = std::make_unique<ConjunctPathExecutor>(conjunct, qctx_.get());
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
TEST_F(ConjunctPathTest, AllPathsOneStepPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kAllPaths,
5);
conjunct->setLeftVar("all_paths_forward1");
conjunct->setRightVar("all_paths_backward2");
conjunct->setColNames({"_path"});
auto conjunctExe = std::make_unique<ConjunctPathExecutor>(conjunct, qctx_.get());
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
TEST_F(ConjunctPathTest, AllPathsTwoStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kAllPaths,
5);
conjunct->setLeftVar("all_paths_forward1");
conjunct->setRightVar("all_paths_backward3");
conjunct->setColNames({"_path"});
auto conjunctExe = std::make_unique<ConjunctPathExecutor>(conjunct, qctx_.get());
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("3", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
TEST_F(ConjunctPathTest, AllPathsThreeStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kAllPaths,
5);
conjunct->setLeftVar("all_paths_forward1");
conjunct->setRightVar("all_paths_backward4");
conjunct->setColNames({"_path"});
auto conjunctExe = std::make_unique<ConjunctPathExecutor>(conjunct, qctx_.get());
{
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
{
{
// 1->2->4@0
// 1->2->4@1
DataSet ds1;
ds1.colNames = {kVid, "path"};
Row row;
List paths;
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 1, {}));
paths.values.emplace_back(std::move(path));
}
row.values = {"4", std::move(paths)};
ds1.rows.emplace_back(std::move(row));
qctx_->ectx()->setResult("all_paths_forward1", ResultBuilder().value(ds1).finish());
}
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
{
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
}
{
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 1, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
}
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
}
TEST_F(ConjunctPathTest, AllPathsFourStepsPath) {
auto* conjunct = ConjunctPath::make(qctx_.get(),
StartNode::make(qctx_.get()),
StartNode::make(qctx_.get()),
ConjunctPath::PathKind::kAllPaths,
5);
conjunct->setLeftVar("all_paths_forward1");
conjunct->setRightVar("all_paths_backward4");
conjunct->setColNames({"_path"});
auto conjunctExe = std::make_unique<ConjunctPathExecutor>(conjunct, qctx_.get());
{
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
{
{
// 1->2->6@0
// 1->2->6@1
DataSet ds1;
ds1.colNames = {kVid, "path"};
Row row;
List paths;
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 1, {}));
paths.values.emplace_back(std::move(path));
}
row.values = {"6", std::move(paths)};
ds1.rows.emplace_back(std::move(row));
qctx_->ectx()->setResult("all_paths_forward1", ResultBuilder().value(ds1).finish());
}
{
// 5->4->6@0
DataSet ds1;
ds1.colNames = {kVid, "path"};
Row row;
List paths;
{
Path path;
path.src = Vertex("5", {});
path.steps.emplace_back(Step(Vertex("4", {}), -1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), -1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
row.values = {"6", std::move(paths)};
ds1.rows.emplace_back(std::move(row));
qctx_->ectx()->setResult("all_paths_backward4", ResultBuilder().value(ds1).finish());
}
auto future = conjunctExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(conjunct->outputVar());
DataSet expected;
expected.colNames = {"_path"};
{
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
}
{
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("2", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 1, {}));
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
row.values.emplace_back(std::move(path));
expected.rows.emplace_back(std::move(row));
}
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
}
} // namespace graph
} // namespace nebula
/* 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.
*/
#include <gtest/gtest.h>
#include "context/QueryContext.h"
#include "planner/Algo.h"
#include "executor/algo/ProduceAllPathsExecutor.h"
namespace nebula {
namespace graph {
class ProduceAllPathsTest : public testing::Test {
protected:
static bool compareAllPathRow(Row& lhs, Row& rhs) {
auto& lDst = lhs.values[0];
auto& rDst = rhs.values[0];
if (lDst != rDst) {
return lDst < rDst;
}
auto& lEdges = lhs.values[1].getList();
auto& rEdges = rhs.values[1].getList();
if (lEdges != rEdges) {
return lEdges < rEdges;
}
return false;
}
static ::testing::AssertionResult verifyAllPaths(DataSet& result, DataSet& expected) {
std::sort(expected.rows.begin(), expected.rows.end(), compareAllPathRow);
for (auto& row : expected.rows) {
auto& edges = row.values[1].mutableList().values;
std::sort(edges.begin(), edges.end());
}
std::sort(result.rows.begin(), result.rows.end(), compareAllPathRow);
for (auto& row : result.rows) {
auto& edges = row.values[1].mutableList().values;
std::sort(edges.begin(), edges.end());
}
EXPECT_EQ(result, expected);
return result == expected
? ::testing::AssertionSuccess()
: (::testing::AssertionFailure() << result << " vs. " << expected);
}
void SetUp() override {
qctx_ = std::make_unique<QueryContext>();
/*
* 0->1->5->7;
* 1->6->7
* 2->6->7
* 3->4->7
* startVids {0, 1, 2, 3}
*/
{
DataSet ds1;
ds1.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 0->1
Row row;
row.values.emplace_back("0");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("1");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
{
// 2->6
Row row;
row.values.emplace_back("2");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("6");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
{
// 3->4
Row row;
row.values.emplace_back("3");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("4");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
firstStepResult_ = std::move(ds1);
DataSet ds2;
ds2.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
}
{
// 4->7, 5->7, 6->7
for (auto i = 4; i < 7; i++) {
Row row;
row.values.emplace_back(folly::to<std::string>(i));
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
}
}
secondStepResult_ = std::move(ds2);
DataSet ds3;
ds3.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 5->7, 6->7
for (auto i = 5; i < 7; i++) {
Row row;
row.values.emplace_back(folly::to<std::string>(i));
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds3.rows.emplace_back(std::move(row));
}
}
thridStepResult_ = std::move(ds3);
{
DataSet ds;
ds.colNames = {kVid,
"_stats",
"_tag:tag1:prop1:prop2",
"_edge:+edge1:prop1:prop2:_dst:_rank",
"_expr"};
qctx_->symTable()->newVariable("empty_get_neighbors");
qctx_->ectx()->setResult("empty_get_neighbors",
ResultBuilder()
.value(Value(std::move(ds)))
.iter(Iterator::Kind::kGetNeighbors)
.finish());
}
}
}
protected:
std::unique_ptr<QueryContext> qctx_;
DataSet firstStepResult_;
DataSet secondStepResult_;
DataSet thridStepResult_;
};
TEST_F(ProduceAllPathsTest, AllPath) {
qctx_->symTable()->newVariable("input");
auto* allPathsNode = ProduceAllPaths::make(qctx_.get(), nullptr);
allPathsNode->setInputVar("input");
allPathsNode->setColNames({kDst, "_paths"});
auto allPathsExe = std::make_unique<ProduceAllPathsExecutor>(allPathsNode, qctx_.get());
// Step1
{
ResultBuilder builder;
List datasets;
datasets.values.emplace_back(std::move(firstStepResult_));
builder.value(std::move(datasets)).iter(Iterator::Kind::kGetNeighbors);
qctx_->ectx()->setResult("input", builder.finish());
auto future = allPathsExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(allPathsNode->outputVar());
DataSet expected;
expected.colNames = {kDst, "_paths"};
{
// 0->1
Row row;
Path path;
path.src = Vertex("0", {});
path.steps.emplace_back(Step(Vertex("1", {}), 1, "edge1", 0, {}));
List paths;
paths.values.emplace_back(std::move(path));
row.values.emplace_back("1");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
{
// 1->5
Row row;
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
List paths;
paths.values.emplace_back(std::move(path));
row.values.emplace_back("5");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
{
// 1->6
Row row;
List paths;
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
{
Path path;
path.src = Vertex("2", {});
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
row.values.emplace_back("6");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
{
// 3->4
Row row;
Path path;
path.src = Vertex("3", {});
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
List paths;
paths.values.emplace_back(std::move(path));
row.values.emplace_back("4");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
auto resultDs = result.value().getDataSet();
EXPECT_TRUE(verifyAllPaths(resultDs, expected));
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
// Step 2
{
ResultBuilder builder;
List datasets;
datasets.values.emplace_back(std::move(secondStepResult_));
builder.value(std::move(datasets)).iter(Iterator::Kind::kGetNeighbors);
qctx_->ectx()->setResult("input", builder.finish());
auto future = allPathsExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(allPathsNode->outputVar());
DataSet expected;
expected.colNames = {kDst, "_paths"};
{
// 0->1->5
Row row;
Path path;
path.src = Vertex("0", {});
path.steps.emplace_back(Step(Vertex("1", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
List paths;
paths.values.emplace_back(std::move(path));
row.values.emplace_back("5");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
{
// 0->1->6
Row row;
Path path;
path.src = Vertex("0", {});
path.steps.emplace_back(Step(Vertex("1", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
List paths;
paths.values.emplace_back(std::move(path));
row.values.emplace_back("6");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
{
Row row;
List paths;
// 2->6->7
{
Path path;
path.src = Vertex("2", {});
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
// 3->4->7
{
Path path;
path.src = Vertex("3", {});
path.steps.emplace_back(Step(Vertex("4", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
// 1->5->7
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
// 1->6->7
{
Path path;
path.src = Vertex("1", {});
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
row.values.emplace_back("7");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
auto resultDs = result.value().getDataSet();
EXPECT_TRUE(verifyAllPaths(resultDs, expected));
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
// Step3
{
ResultBuilder builder;
List datasets;
datasets.values.emplace_back(std::move(thridStepResult_));
builder.value(std::move(datasets)).iter(Iterator::Kind::kGetNeighbors);
qctx_->ectx()->setResult("input", builder.finish());
auto future = allPathsExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(allPathsNode->outputVar());
DataSet expected;
expected.colNames = {"_dst", "_paths"};
{
// 0->1->5->7, 0->1->6->7
List paths;
{
Path path;
path.src = Vertex("0", {});
path.steps.emplace_back(Step(Vertex("1", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("5", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
{
Path path;
path.src = Vertex("0", {});
path.steps.emplace_back(Step(Vertex("1", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("6", {}), 1, "edge1", 0, {}));
path.steps.emplace_back(Step(Vertex("7", {}), 1, "edge1", 0, {}));
paths.values.emplace_back(std::move(path));
}
Row row;
row.values.emplace_back("7");
row.values.emplace_back(std::move(paths));
expected.rows.emplace_back(std::move(row));
}
auto resultDs = result.value().getDataSet();
EXPECT_TRUE(verifyAllPaths(resultDs, expected));
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
}
TEST_F(ProduceAllPathsTest, EmptyInput) {
auto* allPathsNode = ProduceAllPaths::make(qctx_.get(), nullptr);
allPathsNode->setInputVar("empty_get_neighbors");
allPathsNode->setColNames({kDst, "_paths"});
auto allPathsExe = std::make_unique<ProduceAllPathsExecutor>(allPathsNode, qctx_.get());
auto future = allPathsExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(allPathsNode->outputVar());
DataSet expected;
expected.colNames = {"_dst", "_paths"};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}
} // namespace graph
} // namespace nebula
......@@ -15,199 +15,199 @@ namespace graph {
class ProduceSemiShortestPathTest : public testing::Test {
protected:
static bool compareShortestPath(Row& row1, Row& row2) {
// row : dst | cost | {paths}
if (row1.values[0] != row2.values[0]) {
return row1.values[0] < row2.values[0];
}
if (row1.values[1] != row2.values[1]) {
return row1.values[1] < row2.values[1];
}
auto& pathList1 = row1.values[2].getList();
auto& pathList2 = row2.values[2].getList();
if (pathList1.size() != pathList2.size()) {
return pathList1.size() < pathList2.size();
}
for (size_t i = 0; i < pathList1.size(); i++) {
if (pathList1.values[i] != pathList2.values[i]) {
return pathList1.values[i] < pathList2.values[i];
static bool compareShortestPath(Row& row1, Row& row2) {
// row : dst | cost | {paths}
if (row1.values[0] != row2.values[0]) {
return row1.values[0] < row2.values[0];
}
}
return false;
}
void SetUp() override {
qctx_ = std::make_unique<QueryContext>();
/*
* 0->1->5->7;
* 1->6->7
* 2->6->7
* 3->4->7
* startVids {0, 1, 2, 3}
*/
{
DataSet ds1;
ds1.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 0->1
Row row;
row.values.emplace_back("0");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("1");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
if (row1.values[1] != row2.values[1]) {
return row1.values[1] < row2.values[1];
}
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
auto& pathList1 = row1.values[2].getList();
auto& pathList2 = row2.values[2].getList();
if (pathList1.size() != pathList2.size()) {
return pathList1.size() < pathList2.size();
}
{
// 2->6
Row row;
row.values.emplace_back("2");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("6");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
{
// 3->4
Row row;
row.values.emplace_back("3");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("4");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
for (size_t i = 0; i < pathList1.size(); i++) {
if (pathList1.values[i] != pathList2.values[i]) {
return pathList1.values[i] < pathList2.values[i];
}
}
firstStepResult_ = std::move(ds1);
return false;
}
DataSet ds2;
ds2.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
void SetUp() override {
qctx_ = std::make_unique<QueryContext>();
/*
* 0->1->5->7;
* 1->6->7
* 2->6->7
* 3->4->7
* startVids {0, 1, 2, 3}
*/
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
DataSet ds1;
ds1.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 0->1
Row row;
row.values.emplace_back("0");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back("1");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
}
{
// 4->7, 5->7, 6->7
for (auto i = 4; i < 7; i++) {
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds1.rows.emplace_back(std::move(row));
}
{
// 2->6
Row row;
row.values.emplace_back(folly::to<std::string>(i));
row.values.emplace_back("2");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back("6");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
ds1.rows.emplace_back(std::move(row));
}
}
secondStepResult_ = std::move(ds2);
DataSet ds3;
ds3.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 5->7, 6->7
for (auto i = 5; i < 7; i++) {
{
// 3->4
Row row;
row.values.emplace_back(folly::to<std::string>(i));
row.values.emplace_back("3");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back("4");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds3.rows.emplace_back(std::move(row));
ds1.rows.emplace_back(std::move(row));
}
}
thridStepResult_ = std::move(ds3);
firstStepResult_ = std::move(ds1);
{
DataSet ds;
ds.colNames = {kVid,
"_stats",
"_tag:tag1:prop1:prop2",
"_edge:+edge1:prop1:prop2:_dst:_rank",
"_expr"};
qctx_->symTable()->newVariable("empty_get_neighbors");
qctx_->ectx()->setResult("empty_get_neighbors",
ResultBuilder()
.value(Value(std::move(ds)))
.iter(Iterator::Kind::kGetNeighbors)
.finish());
DataSet ds2;
ds2.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 1->5, 1->6;
Row row;
row.values.emplace_back("1");
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
for (auto i = 5; i < 7; i++) {
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back(folly::to<std::string>(i));
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
}
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
}
{
// 4->7, 5->7, 6->7
for (auto i = 4; i < 7; i++) {
Row row;
row.values.emplace_back(folly::to<std::string>(i));
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds2.rows.emplace_back(std::move(row));
}
}
secondStepResult_ = std::move(ds2);
DataSet ds3;
ds3.colNames = {kVid, "_stats", "_edge:+edge1:_type:_dst:_rank", "_expr"};
{
// 5->7, 6->7
for (auto i = 5; i < 7; i++) {
Row row;
row.values.emplace_back(folly::to<std::string>(i));
// _stats = empty
row.values.emplace_back(Value());
// edges
List edges;
List edge;
edge.values.emplace_back(1);
edge.values.emplace_back("7");
edge.values.emplace_back(0);
edges.values.emplace_back(std::move(edge));
row.values.emplace_back(edges);
// _expr = empty
row.values.emplace_back(Value());
ds3.rows.emplace_back(std::move(row));
}
}
thridStepResult_ = std::move(ds3);
{
DataSet ds;
ds.colNames = {kVid,
"_stats",
"_tag:tag1:prop1:prop2",
"_edge:+edge1:prop1:prop2:_dst:_rank",
"_expr"};
qctx_->symTable()->newVariable("empty_get_neighbors");
qctx_->ectx()->setResult("empty_get_neighbors",
ResultBuilder()
.value(Value(std::move(ds)))
.iter(Iterator::Kind::kGetNeighbors)
.finish());
}
}
}
}
protected:
std::unique_ptr<QueryContext> qctx_;
......
......@@ -48,29 +48,50 @@ public:
kBiBFS,
kBiDijkstra,
kFloyd,
kAllPath,
kAllPaths,
};
static ConjunctPath* make(QueryContext* qctx,
PlanNode* left,
PlanNode* right,
PathKind pathKind) {
return qctx->objPool()->add(new ConjunctPath(qctx, left, right, pathKind));
PathKind pathKind,
size_t steps) {
return qctx->objPool()->add(new ConjunctPath(qctx, left, right, pathKind, steps));
}
PathKind pathKind() const {
return pathKind_;
}
size_t steps() const {
return steps_;
}
private:
ConjunctPath(QueryContext* qctx, PlanNode* left, PlanNode* right, PathKind pathKind)
ConjunctPath(QueryContext* qctx,
PlanNode* left,
PlanNode* right,
PathKind pathKind,
size_t steps)
: BiInputNode(qctx, Kind::kConjunctPath, left, right) {
pathKind_ = pathKind;
steps_ = steps;
}
PathKind pathKind_;
size_t steps_{0};
};
class ProduceAllPaths final : public SingleInputNode {
public:
static ProduceAllPaths* make(QueryContext* qctx, PlanNode* input) {
return qctx->objPool()->add(new ProduceAllPaths(qctx, input));
}
private:
ProduceAllPaths(QueryContext* qctx, PlanNode* input)
: SingleInputNode(qctx, Kind::kProduceAllPaths, input) {}
};
} // namespace graph
} // namespace nebula
#endif // PLANNER_ALGO_H_
......@@ -183,6 +183,8 @@ const char* PlanNode::toString(PlanNode::Kind kind) {
return "ProduceSemiShortestPath";
case Kind::kConjunctPath:
return "ConjunctPath";
case Kind::kProduceAllPaths:
return "ProduceAllPaths";
// no default so the compiler will warning when lack
}
LOG(FATAL) << "Impossible kind plan node " << static_cast<int>(kind);
......
......@@ -107,6 +107,7 @@ public:
kBFSShortest,
kProduceSemiShortestPath,
kConjunctPath,
kProduceAllPaths,
};
PlanNode(QueryContext* qctx, Kind kind);
......
......@@ -209,6 +209,10 @@ std::unique_ptr<cpp2::PlanNodeDescription> DataCollect::explain() const {
addDescription("kind", "BFS SHORTEST", desc.get());
break;
}
case CollectKind::kAllPaths: {
addDescription("kind", "ALL PATHS", desc.get());
break;
}
}
return desc;
}
......
......@@ -842,6 +842,7 @@ public:
kRowBasedMove,
kMToN,
kBFSShortest,
kAllPaths,
};
static DataCollect* make(QueryContext* qctx,
......
......@@ -25,13 +25,16 @@ Status FindPathValidator::validateImpl() {
Status FindPathValidator::toPlan() {
// TODO: Implement the path plan.
if (from_.vids.size() == 1 && to_.vids.size() == 1) {
if (isShortest_ && from_.vids.size() == 1 && to_.vids.size() == 1) {
return singlePairPlan();
} else {
} else if (isShortest_) {
auto* passThrough = PassThroughNode::make(qctx_, nullptr);
tail_ = passThrough;
root_ = tail_;
} else {
return allPairPaths();
}
return Status::OK();
}
......@@ -46,7 +49,7 @@ Status FindPathValidator::singlePairPlan() {
VLOG(1) << "backward: " << backward->outputVar();
auto* conjunct =
ConjunctPath::make(qctx_, forward, backward, ConjunctPath::PathKind::kBiBFS);
ConjunctPath::make(qctx_, forward, backward, ConjunctPath::PathKind::kBiBFS, steps_.steps);
conjunct->setLeftVar(forward->outputVar());
conjunct->setRightVar(backward->outputVar());
conjunct->setColNames({"_path"});
......@@ -142,5 +145,82 @@ GetNeighbors::EdgeProps FindPathValidator::buildEdgeKey(bool reverse) {
});
return edgeProps;
}
Status FindPathValidator::allPairPaths() {
auto* bodyStart = StartNode::make(qctx_);
auto* passThrough = PassThroughNode::make(qctx_, bodyStart);
auto* forward = allPaths(passThrough, from_, false);
VLOG(1) << "forward: " << forward->outputVar();
auto* backward = allPaths(passThrough, to_, true);
VLOG(1) << "backward: " << backward->outputVar();
auto* conjunct = ConjunctPath::make(
qctx_, forward, backward, ConjunctPath::PathKind::kAllPaths, steps_.steps);
conjunct->setLeftVar(forward->outputVar());
conjunct->setRightVar(backward->outputVar());
conjunct->setColNames({"_path"});
auto* loop = Loop::make(
qctx_, nullptr, conjunct, buildAllPathsLoopCondition(steps_.steps));
auto* dataCollect = DataCollect::make(
qctx_, loop, DataCollect::CollectKind::kAllPaths, {conjunct->outputVar()});
dataCollect->setColNames({"_path"});
root_ = dataCollect;
tail_ = loop;
return Status::OK();
}
PlanNode* FindPathValidator::allPaths(PlanNode* dep, const Starts& starts, bool reverse) {
std::string startVidsVar;
Expression* vids = nullptr;
buildConstantInput(starts, startVidsVar, vids);
DCHECK(!!vids);
auto* gn = GetNeighbors::make(qctx_, dep, space_.id);
gn->setSrc(vids);
gn->setEdgeProps(buildEdgeKey(reverse));
gn->setInputVar(startVidsVar);
auto* allPaths = ProduceAllPaths::make(qctx_, gn);
allPaths->setInputVar(gn->outputVar());
allPaths->setColNames({kVid, "path"});
allPaths->setOutputVar(startVidsVar);
DataSet ds;
ds.colNames = {kVid, "path"};
for (auto& vid : starts.vids) {
Row row;
row.values.emplace_back(vid);
List paths;
Path path;
path.src = Vertex(vid.getStr(), {});
paths.values.emplace_back(std::move(path));
row.values.emplace_back(std::move(paths));
ds.rows.emplace_back(std::move(row));
}
qctx_->ectx()->setResult(startVidsVar, ResultBuilder().value(Value(std::move(ds))).finish());
return allPaths;
}
Expression* FindPathValidator::buildAllPathsLoopCondition(uint32_t steps) {
// ++loopSteps{0} <= (steps/2+steps%2) && size(pathVar) == 0
auto loopSteps = vctx_->anonVarGen()->getVar();
qctx_->ectx()->setValue(loopSteps, 0);
auto* nSteps = new RelationalExpression(
Expression::Kind::kRelLE,
new UnaryExpression(
Expression::Kind::kUnaryIncr,
new VersionedVariableExpression(new std::string(loopSteps), new ConstantExpression(0))),
new ConstantExpression(static_cast<int32_t>(steps / 2 + steps % 2)));
return qctx_->objPool()->add(nSteps);
}
} // namespace graph
} // namespace nebula
......@@ -36,6 +36,12 @@ private:
GetNeighbors::EdgeProps buildEdgeKey(bool reverse);
Status allPairPaths();
PlanNode* allPaths(PlanNode* dep, const Starts& starts, bool reverse);
Expression* buildAllPathsLoopCondition(uint32_t steps);
private:
bool isShortest_{false};
Starts to_;
......
......@@ -76,3 +76,77 @@ class TestFindPath(NebulaTestSuite):
}
self.check_column_names(resp, expected_data["column_names"])
self.check_path_result_without_prop(resp.data.rows, expected_data["rows"])
def test_all_pairs_all_paths_constant_input(self):
stmt = 'FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker" OVER like UPTO 3 STEPS'
resp = self.execute_query(stmt)
self.check_resp_succeeded(resp)
expected_data = {
"column_names": ["_path"],
"rows": [
[b"Tim Duncan", (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge"), (b"like", 0, b"Tony Parker")]
]
}
self.check_column_names(resp, expected_data["column_names"])
self.check_path_result_without_prop(resp.data.rows, expected_data["rows"])
stmt = 'FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","Manu Ginobili" OVER like UPTO 3 STEPS'
resp = self.execute_query(stmt)
self.check_resp_succeeded(resp)
expected_data = {
"column_names": ["_path"],
"rows": [
[b"Tim Duncan", (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Manu Ginobili")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Manu Ginobili")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Manu Ginobili")]
]
}
self.check_column_names(resp, expected_data["column_names"])
self.check_path_result_without_prop(resp.data.rows, expected_data["rows"])
stmt = 'FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","LaMarcus Aldridge" OVER like UPTO 3 STEPS'
resp = self.execute_query(stmt)
self.check_resp_succeeded(resp)
expected_data = {
"column_names": ["_path"],
"rows": [
[b"Tim Duncan", (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge"), (b"like", 0, b"Tony Parker")]
]
}
self.check_column_names(resp, expected_data["column_names"])
self.check_path_result_without_prop(resp.data.rows, expected_data["rows"])
stmt = 'FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","Spurs" OVER like,serve UPTO 3 STEPS'
resp = self.execute_query(stmt)
self.check_resp_succeeded(resp)
expected_data = {
"column_names": ["_path"],
"rows": [
[b"Tim Duncan", (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Tim Duncan"), (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Manu Ginobili"), (b"like", 0, b"Tim Duncan"), (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge"), (b"like", 0, b"Tony Parker")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"LaMarcus Aldridge"), (b"serve", 0, b"Spurs")],
[b"Tim Duncan", (b"like", 0, b"Tony Parker"), (b"like", 0, b"Manu Ginobili"), (b"serve", 0, b"Spurs")]
]
}
self.check_column_names(resp, expected_data["column_names"])
self.check_path_result_without_prop(resp.data.rows, expected_data["rows"])
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