Skip to content
This repository has been archived by the owner on Dec 1, 2022. It is now read-only.

Commit

Permalink
refactor unwind
Browse files Browse the repository at this point in the history
  • Loading branch information
czpmango committed May 11, 2021
1 parent 5a4f581 commit fbb89aa
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 365 deletions.
7 changes: 5 additions & 2 deletions src/context/ast/CypherAstContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,11 @@ struct MatchClauseContext final : CypherClauseContextBase {
struct UnwindClauseContext final : CypherClauseContextBase {
UnwindClauseContext() : CypherClauseContextBase(CypherClauseKind::kUnwind) {}

const YieldColumns* yieldColumns{nullptr};
std::unordered_map<std::string, AliasType>* aliasesUsed{nullptr};
Expression* unwindExpr{nullptr};
std::string alias;

// TODO: refactor alias
std::unordered_map<std::string, AliasType>* aliasesUsed{nullptr};
std::unordered_map<std::string, AliasType> aliasesGenerated;
};

Expand Down
24 changes: 12 additions & 12 deletions src/executor/query/UnwindExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ folly::Future<Status> UnwindExecutor::execute() {
SCOPED_TIMER(&execTime_);

auto *unwind = asNode<Unwind>(node());
auto columns = unwind->columns()->columns();
DCHECK_GT(columns.size(), 0);

auto iter = ectx_->getResult(unwind->inputVar()).iter();
DCHECK(!!iter);
auto &inputRes = ectx_->getResult(unwind->inputVar());
auto inputDsPtr = inputRes.valuePtr();
bool emptyInput = false;
if (inputDsPtr->type() != Value::Type::DATASET) {
emptyInput = true;
}
auto iter = inputRes.iter();
QueryExpressionContext ctx(ectx_);
auto *unwindExpr = unwind->unwindExpr();

DataSet ds;
ds.colNames = unwind->colNames();
for (; iter->valid(); iter->next()) {
auto &unwind_col = columns[0];
Value list = unwind_col->expr()->eval(ctx(iter.get()));
Value list = unwindExpr->eval(ctx(iter.get()));
std::vector<Value> vals = extractList(list);
for (auto &v : vals) {
Row row;
row.values.emplace_back(std::move(v));
for (size_t i = 1; i < columns.size(); ++i) {
auto &col = columns[i];
Value val = col->expr()->eval(ctx(iter.get()));
row.values.emplace_back(std::move(val));
if (!emptyInput) {
row = *(iter->row());
}
row.values.emplace_back(std::move(v));
ds.rows.emplace_back(std::move(row));
}
}
Expand Down
205 changes: 33 additions & 172 deletions src/executor/test/UnwindTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,185 +22,46 @@ class UnwindTest : public QueryTestBase {
start_ = StartNode::make(qctx_.get());
}

void testUnwind(std::vector<Value> l) {
auto list = std::make_unique<ConstantExpression>(List(l));
auto* unwind = Unwind::make(qctx_.get(), start_, list.get(), "items");
unwind->setColNames(std::vector<std::string>{"items"});

auto unwExe = Executor::create(unwind, qctx_.get());
auto future = unwExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(unwind->outputVar());

DataSet expected;
expected.colNames = {"items"};
for (auto v : l) {
Row row;
row.values.emplace_back(v);
expected.rows.emplace_back(std::move(row));
}
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}

protected:
std::unique_ptr<QueryContext> qctx_;
StartNode* start_;
};

TEST_F(UnwindTest, UnwindList) {
// UNWIND [1, 2, 3] as r
auto exprList = new ExpressionList(3);
for (auto i = 0; i < 3; ++i) {
exprList->add(new ConstantExpression(i));
}

auto *col = new YieldColumn(new ListExpression(exprList), new std::string("r"));
auto *columns = new YieldColumns();
qctx_->objPool()->add(columns);
columns->addColumn(col);

auto* unwind = Unwind::make(qctx_.get(), start_, columns);
unwind->setColNames(std::vector<std::string>{"r"});

auto unwExe = Executor::create(unwind, qctx_.get());
auto future = unwExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(unwind->outputVar());

DataSet expected;
expected.colNames = {"r"};
for (auto i = 0; i < 3; ++i) {
Row row;
row.values.emplace_back(i);
expected.rows.emplace_back(std::move(row));
}
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}

TEST_F(UnwindTest, UnwindNestedList) {
// UNWIND [1, [2, NULL, 3], 4, NULL] as r
auto *nestedList = new ExpressionList(3);
nestedList->add(new ConstantExpression(2));
nestedList->add(new ConstantExpression(Value::kNullValue));
nestedList->add(new ConstantExpression(3));
auto *nestedExpr = new ListExpression(nestedList);

auto *exprList = new ExpressionList(4);
exprList->add(new ConstantExpression(1));
exprList->add(nestedExpr);
exprList->add(new ConstantExpression(4));
exprList->add(new ConstantExpression(Value::kNullValue));
auto *col = new YieldColumn(new ListExpression(exprList), new std::string("r"));
auto *columns = new YieldColumns();
qctx_->objPool()->add(columns);
columns->addColumn(col);

auto* unwind = Unwind::make(qctx_.get(), start_, columns);
unwind->setColNames(std::vector<std::string>{"r"});

auto unwExe = Executor::create(unwind, qctx_.get());
auto future = unwExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(unwind->outputVar());

DataSet expected;
expected.colNames = {"r"};
expected.rows = {Row({Value(1)}),
Row({Value(List({Value(2), Value::kNullValue, Value(3)}))}),
Row({Value(4)}),
Row({Value::kNullValue})};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}

TEST_F(UnwindTest, UnwindLabel) {
// UNWIND [1, [2, NULL, 3], 4, NULL] as r1 UNWIND r as r2
auto *nestedList = new ExpressionList(3);
nestedList->add(new ConstantExpression(2));
nestedList->add(new ConstantExpression(Value::kNullValue));
nestedList->add(new ConstantExpression(3));
auto *nestedExpr = new ListExpression(nestedList);

auto *exprList = new ExpressionList(4);
exprList->add(new ConstantExpression(1));
exprList->add(nestedExpr);
exprList->add(new ConstantExpression(4));
exprList->add(new ConstantExpression(Value::kNullValue));
auto *col = new YieldColumn(new ListExpression(exprList), new std::string("r1"));
auto *columns = new YieldColumns();
qctx_->objPool()->add(columns);
columns->addColumn(col);

auto* unwind1 = Unwind::make(qctx_.get(), start_, columns);
unwind1->setColNames(std::vector<std::string>{"r1"});
auto unwExe = Executor::create(unwind1, qctx_.get());
auto future = unwExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());

auto col2 = new YieldColumn(
new VariablePropertyExpression(new std::string(), new std::string("r1")));
columns = new YieldColumns();
qctx_->objPool()->add(columns);
columns->addColumn(col2);
auto* unwind2 = Unwind::make(qctx_.get(), unwind1, columns);
unwind2->setInputVar(unwind1->outputVar());
unwind2->setColNames(std::vector<std::string>{"r2"});
unwExe = Executor::create(unwind2, qctx_.get());
future = unwExe->execute();
status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto& result = qctx_->ectx()->getResult(unwind2->outputVar());

DataSet expected;
expected.colNames = {"r2"};
expected.rows = {Row({Value(1)}),
Row({Value(2)}),
Row({Value::kNullValue}),
Row({Value(3)}),
Row({Value(4)})};
EXPECT_EQ(result.value().getDataSet(), expected);
EXPECT_EQ(result.state(), Result::State::kSuccess);
}

TEST_F(UnwindTest, ProjectUnwind) {
/*
| a | b |
| 1 | [1,2,3]|
#define TEST_UNWIND(list) \
do { \
testUnwind(list); \
} while (0)

UNWIND b as c
*/
auto *exprList = new ExpressionList(3);
exprList->add(new ConstantExpression(1));
exprList->add(new ConstantExpression(2));
exprList->add(new ConstantExpression(3));

auto *col1 = new YieldColumn(new ConstantExpression(1), new std::string("a"));
auto *col2 = new YieldColumn(new ListExpression(exprList), new std::string("b"));
auto *columns = qctx_->objPool()->add(new YieldColumns());
columns->addColumn(col1);
columns->addColumn(col2);
auto *project = Project::make(qctx_.get(), start_, columns);
project->setColNames({"a", "b"});
auto proExe = Executor::create(project, qctx_.get());
EXPECT_TRUE(proExe->execute().get().ok());
auto &proResult = qctx_->ectx()->getResult(project->outputVar());
DataSet expected;
expected.colNames = {"a", "b"};
Row row;
row.values.emplace_back(1);
row.values.emplace_back(List({1, 2, 3}));
expected.rows.emplace_back(std::move(row));
EXPECT_EQ(proResult.value().getDataSet(), expected);
EXPECT_EQ(proResult.state(), Result::State::kSuccess);

auto col =
new YieldColumn(new VariablePropertyExpression(new std::string(), new std::string("b")));
auto *columns2 = new YieldColumns();
qctx_->objPool()->add(columns2);
columns2->addColumn(col);

auto *unwind = Unwind::make(qctx_.get(), project, columns2);
unwind->setColNames(std::vector<std::string>{"c"});

auto unwExe = Executor::create(unwind, qctx_.get());
auto future = unwExe->execute();
auto status = std::move(future).get();
EXPECT_TRUE(status.ok());
auto &unwindResult = qctx_->ectx()->getResult(unwind->outputVar());
static std::unordered_map<std::string, std::vector<Value>> testSuite = {
{"case1", {1, 2, 3}},
{"case2", {1, List({2, Value((NullType::__NULL__)), 3}), 4, Value((NullType::__NULL__))}},
};

DataSet expected2;
expected2.colNames = {"c"};
for (auto i = 1; i <= 3; ++i) {
Row row2;
row2.values.emplace_back(i);
expected2.rows.emplace_back(std::move(row2));
}
EXPECT_EQ(unwindResult.value().getDataSet(), expected2);
EXPECT_EQ(unwindResult.state(), Result::State::kSuccess);
TEST_F(UnwindTest, UnwindList) {
TEST_UNWIND(testSuite["case1"]);
TEST_UNWIND(testSuite["case2"]);
}

} // namespace graph
Expand Down
8 changes: 4 additions & 4 deletions src/planner/match/LabelIndexSeek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ StatusOr<SubPlan> LabelIndexSeek::transformEdge(EdgeContext* edgeCtx) {
scan,
yieldColumns);
project->setColNames({kVid});
auto *unwindColumns = matchClauseCtx->qctx->objPool()->makeAndAdd<YieldColumns>();
unwindColumns->addColumn(new YieldColumn(new ColumnExpression(0)));
auto *unwind = Unwind::make(matchClauseCtx->qctx, project, unwindColumns);
unwind->setColNames({kVid});

auto *unwindExpr = matchClauseCtx->qctx->objPool()->add(new ColumnExpression(0));
auto* unwind = Unwind::make(matchClauseCtx->qctx, project, unwindExpr, kVid);
unwind->setColNames({"vidList", kVid});
plan.root = unwind;
}

Expand Down
11 changes: 11 additions & 0 deletions src/planner/match/MatchPlanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ StatusOr<SubPlan> MatchPlanner::transform(AstContext* astCtx) {
case CypherClauseKind::kUnwind: {
auto subplan = std::make_unique<UnwindClausePlanner>()->transform(clauseCtx.get());
NG_RETURN_IF_ERROR(subplan);
auto& unwind = subplan.value().root;
std::vector<std::string> inputCols;
if (!subplans.empty()) {
auto input = subplans.back().root;
auto cols = input->colNames();
for (auto col : cols) {
inputCols.emplace_back(col);
}
}
inputCols.emplace_back(unwind->colNames().front());
unwind->setColNames(inputCols);
subplans.emplace_back(std::move(subplan).value());
break;
}
Expand Down
8 changes: 4 additions & 4 deletions src/planner/match/PropIndexSeek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ StatusOr<SubPlan> PropIndexSeek::transformEdge(EdgeContext* edgeCtx) {
scan,
yieldColumns);
project->setColNames({kVid});
auto *unwindColumns = matchClauseCtx->qctx->objPool()->makeAndAdd<YieldColumns>();
unwindColumns->addColumn(new YieldColumn(new ColumnExpression(0)));
auto *unwind = Unwind::make(matchClauseCtx->qctx, project, unwindColumns);
unwind->setColNames({kVid});

auto *unwindExpr = matchClauseCtx->qctx->objPool()->add(new ColumnExpression(0));
auto* unwind = Unwind::make(matchClauseCtx->qctx, project, unwindExpr, kVid);
unwind->setColNames({"vidList", kVid});
plan.root = unwind;
}

Expand Down
23 changes: 4 additions & 19 deletions src/planner/match/UnwindClausePlanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,10 @@ StatusOr<SubPlan> UnwindClausePlanner::transform(CypherClauseContextBase* clause
}

Status UnwindClausePlanner::buildUnwind(UnwindClauseContext* uctx, SubPlan& subPlan) {
auto* yields = new YieldColumns();
uctx->qctx->objPool()->add(yields);
std::vector<std::string> colNames;

for (auto* col : uctx->yieldColumns->columns()) {
YieldColumn* newColumn =
new YieldColumn(MatchSolver::doRewrite(*uctx->aliasesUsed, col->expr()),
new std::string(*col->alias()));
yields->addColumn(newColumn);

if (col->alias() != nullptr) {
colNames.emplace_back(*col->alias());
} else {
return Status::Error("Expression in UNWIND must be aliased (use AS)");
}
}

auto* unwind = Unwind::make(uctx->qctx, nullptr, yields);
unwind->setColNames(std::move(colNames));
auto* newUnwindExpr =
uctx->qctx->objPool()->add(MatchSolver::doRewrite(*uctx->aliasesUsed, uctx->unwindExpr));
auto* unwind = Unwind::make(uctx->qctx, nullptr, newUnwindExpr, uctx->alias);
unwind->setColNames({uctx->alias});
subPlan.root = unwind;
subPlan.tail = unwind;

Expand Down
9 changes: 4 additions & 5 deletions src/planner/plan/Query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ void Project::cloneMembers(const Project &p) {

std::unique_ptr<PlanNodeDescription> Unwind::explain() const {
auto desc = SingleInputNode::explain();
addDescription("unwind", cols_? cols_->toString() : "", desc.get());
addDescription("alias", alias(), desc.get());
addDescription("unwindExpr", unwindExpr()->toString(), desc.get());
return desc;
}

Expand All @@ -308,10 +309,8 @@ PlanNode* Unwind::clone() const {
void Unwind::cloneMembers(const Unwind &p) {
SingleInputNode::cloneMembers(p);

cols_ = qctx_->objPool()->add(new YieldColumns());
for (const auto &col : p.columns()->columns()) {
cols_->addColumn(col->clone().release());
}
unwindExpr_ = qctx_->objPool()->add(p.unwindExpr()->clone().release());
alias_ = p.alias();
}


Expand Down
Loading

0 comments on commit fbb89aa

Please sign in to comment.