Query - Logical - CreateNode now accepts an optional input. If provided, CreateNode creates a node for each successful Pull from input. If not provided, CreateNode creates a single node

Reviewers: buda, mislav.bradac, teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D173
This commit is contained in:
florijan 2017-03-24 12:59:23 +01:00
parent 63aae05ec0
commit f9b91cf680
5 changed files with 101 additions and 42 deletions

View File

@ -19,7 +19,7 @@ class Cursor {
virtual ~Cursor() {}
};
class CreateOp;
class CreateNode;
class CreateExpand;
class ScanAll;
class Expand;
@ -28,7 +28,7 @@ class EdgeFilter;
class Produce;
using LogicalOperatorVisitor =
::utils::Visitor<CreateOp, CreateExpand, ScanAll, Expand, NodeFilter,
::utils::Visitor<CreateNode, CreateExpand, ScanAll, Expand, NodeFilter,
EdgeFilter, Produce>;
class LogicalOperator : public ::utils::Visitable<LogicalOperatorVisitor> {
@ -41,33 +41,49 @@ class LogicalOperator : public ::utils::Visitable<LogicalOperatorVisitor> {
std::vector<std::shared_ptr<LogicalOperator>> children_;
};
class CreateOp : public LogicalOperator {
// TODO add an optional input that (if given) gets pulled
// and the create op gets executed for each success
// TODO rename to CreateSingle or CreateNode
/**
* Operator for creating a node. This op is used both for
* creating a single node (CREATE statement without
* a preceeding MATCH), or multiple nodes (MATCH CREATE).
*
* This node
*/
class CreateNode : public LogicalOperator {
public:
CreateOp(NodeAtom *node_atom) : node_atom_(node_atom) {}
DEFVISITABLE(LogicalOperatorVisitor);
/**
*
* @param node_atom
* @param input Optional. If nullptr, then a single node will be
* created (a single successfull Pull from this Op's Cursor).
* If a valid input, then a node will be created for each
* successful pull from the given input.
*/
CreateNode(NodeAtom *node_atom, std::shared_ptr<LogicalOperator> input)
: node_atom_(node_atom), input_(input) {}
void Accept(LogicalOperatorVisitor &visitor) override {
visitor.Visit(*this);
if (input_) input_->Accept(visitor);
visitor.PostVisit(*this);
}
private:
class CreateOpCursor : public Cursor {
class CreateNodeCursor : public Cursor {
public:
CreateOpCursor(CreateOp &self, GraphDbAccessor &db)
: self_(self), db_(db) {}
CreateNodeCursor(CreateNode &self, GraphDbAccessor &db)
: self_(self),
db_(db),
input_cursor_(self.input_ ? self.input_->MakeCursor(db) : nullptr) {}
bool Pull(Frame &frame, SymbolTable &symbol_table) override {
if (!did_create_) {
auto new_node = db_.insert_vertex();
for (auto label : self_.node_atom_->labels_) new_node.add_label(label);
ExpressionEvaluator evaluator(frame, symbol_table);
for (auto &kv : self_.node_atom_->properties_) {
kv.second->Accept(evaluator);
new_node.PropsSet(kv.first, evaluator.PopBack());
}
frame[symbol_table[*self_.node_atom_->identifier_]] = new_node;
if (input_cursor_) {
if (input_cursor_->Pull(frame, symbol_table)) {
Create(frame, symbol_table);
return true;
} else
return false;
} else if (!did_create_) {
Create(frame, symbol_table);
did_create_ = true;
return true;
} else
@ -75,18 +91,38 @@ class CreateOp : public LogicalOperator {
}
private:
CreateOp &self_;
CreateNode &self_;
GraphDbAccessor &db_;
// optional, used in situations in which this create op
// pulls from an input (in MATCH CREATE, CREATE ... CREATE)
std::unique_ptr<Cursor> input_cursor_;
// control switch when creating only one node (nullptr input)
bool did_create_{false};
/**
* Creates a single node and places it in the frame.
*/
void Create(Frame &frame, SymbolTable &symbol_table) {
auto new_node = db_.insert_vertex();
for (auto label : self_.node_atom_->labels_) new_node.add_label(label);
ExpressionEvaluator evaluator(frame, symbol_table);
for (auto &kv : self_.node_atom_->properties_) {
kv.second->Accept(evaluator);
new_node.PropsSet(kv.first, evaluator.PopBack());
}
frame[symbol_table[*self_.node_atom_->identifier_]] = new_node;
}
};
public:
std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override {
return std::make_unique<CreateOpCursor>(*this, db);
return std::make_unique<CreateNodeCursor>(*this, db);
}
private:
NodeAtom *node_atom_ = nullptr;
std::shared_ptr<LogicalOperator> input_;
};
class CreateExpand : public LogicalOperator {

View File

@ -61,16 +61,10 @@ auto GenCreateForPattern(Pattern &pattern, LogicalOperator *input_op,
const SymbolTable &symbol_table,
std::unordered_set<int> bound_symbols) {
auto base = [&](NodeAtom *node) -> LogicalOperator * {
if (BindSymbol(bound_symbols, symbol_table.at(*node->identifier_))) {
// TODO: Pass input_op when CreateOp gets support for it. This will
// support e.g. `MATCH (n) CREATE (m)` and `CREATE (n), (m)`.
if (input_op) {
throw NotYetImplemented();
}
return new CreateOp(node);
} else {
if (BindSymbol(bound_symbols, symbol_table.at(*node->identifier_)))
return new CreateNode(node, std::shared_ptr<LogicalOperator>(input_op));
else
return input_op;
}
};
auto collect = [&](LogicalOperator *last_op, NodeAtom *prev_node,

View File

@ -66,7 +66,7 @@ void Interpret(const std::string &query, GraphDbAccessor &db_accessor,
}
summary["type"] = "r";
} else if (auto create = dynamic_cast<CreateOp *>(logical_plan.get())) {
} else if (auto create = dynamic_cast<CreateNode *>(logical_plan.get())) {
auto cursor = create->MakeCursor(db_accessor);
while (cursor->Pull(frame, symbol_table)) {
continue;

View File

@ -259,7 +259,7 @@ TEST(Interpreter, CreateNodeWithAttributes) {
node->labels_.emplace_back(label);
node->properties_[property] = storage.Create<Literal>(42);
auto create = std::make_shared<CreateOp>(node);
auto create = std::make_shared<CreateNode>(node, nullptr);
ExecuteCreate(create, *dba, symbol_table);
dba->advance_command();
@ -294,7 +294,7 @@ TEST(Interpreter, CreateReturn) {
node->labels_.emplace_back(label);
node->properties_[property] = storage.Create<Literal>(42);
auto create = std::make_shared<CreateOp>(node);
auto create = std::make_shared<CreateNode>(node, nullptr);
auto named_expr_n =
storage.Create<NamedExpression>("n", storage.Create<Identifier>("n"));
symbol_table[*named_expr_n] = symbol_table.CreateSymbol("named_expr_n");
@ -360,7 +360,7 @@ TEST(Interpreter, CreateExpand) {
r->edge_types_.emplace_back(edge_type);
r->properties_[property] = storage.Create<Literal>(3);
auto create_op = std::make_shared<CreateOp>(n);
auto create_op = std::make_shared<CreateNode>(n, nullptr);
auto create_expand =
std::make_shared<CreateExpand>(m, r, create_op, n_sym, cycle);
ExecuteCreate(create_expand, *dba, symbol_table);
@ -395,7 +395,36 @@ TEST(Interpreter, CreateExpand) {
}
}
TEST(Interpreter, MatchCreate) {
TEST(Interpreter, MatchCreateNode) {
Dbms dbms;
auto dba = dbms.active();
// add three nodes we'll match and expand-create from
dba->insert_vertex();
dba->insert_vertex();
dba->insert_vertex();
dba->advance_command();
SymbolTable symbol_table;
AstTreeStorage storage;
// first node
auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
auto n_sym = symbol_table.CreateSymbol("n");
symbol_table[*std::get<0>(n_scan_all)->identifier_] = n_sym;
// second node
auto m = storage.Create<NodeAtom>(storage.Create<Identifier>("m"));
symbol_table[*m->identifier_] = symbol_table.CreateSymbol("m");
// creation op
auto create_node = std::make_shared<CreateNode>(m, std::get<1>(n_scan_all));
EXPECT_EQ(CountIterable(dba->vertices()), 3);
ExecuteCreate(create_node, *dba, symbol_table);
dba->advance_command();
EXPECT_EQ(CountIterable(dba->vertices()), 6);
}
TEST(Interpreter, MatchCreateExpand) {
Dbms dbms;
auto dba = dbms.active();

View File

@ -20,7 +20,7 @@ class PlanChecker : public LogicalOperatorVisitor {
PlanChecker(std::list<size_t> types) : types_(types) {}
void Visit(CreateOp &op) override { AssertType(op); }
void Visit(CreateNode &op) override { AssertType(op); }
void Visit(CreateExpand &op) override { AssertType(op); }
void Visit(ScanAll &op) override { AssertType(op); }
void Visit(Expand &op) override { AssertType(op); }
@ -97,7 +97,7 @@ TEST(TestLogicalPlanner, CreateNodeReturn) {
query->Accept(symbol_generator);
auto plan = MakeLogicalPlan(*query, symbol_table);
std::list<size_t> expected_types;
expected_types.emplace_back(typeid(CreateOp).hash_code());
expected_types.emplace_back(typeid(CreateNode).hash_code());
expected_types.emplace_back(typeid(Produce).hash_code());
PlanChecker plan_checker(expected_types);
plan->Accept(plan_checker);
@ -120,7 +120,7 @@ TEST(TestLogicalPlanner, CreateExpand) {
query->Accept(symbol_generator);
auto plan = MakeLogicalPlan(*query, symbol_table);
std::list<size_t> expected_types;
expected_types.emplace_back(typeid(CreateOp).hash_code());
expected_types.emplace_back(typeid(CreateNode).hash_code());
expected_types.emplace_back(typeid(CreateExpand).hash_code());
PlanChecker plan_checker(expected_types);
plan->Accept(plan_checker);