From da0c9c03d6b4334513fe1b43cc4aa6eb6a1c2a98 Mon Sep 17 00:00:00 2001 From: florijan Date: Thu, 16 Mar 2017 09:28:16 +0100 Subject: [PATCH] Query - create implementation started Summary: CreateOp tests Reviewers: teon.banek, buda, mislav.bradac Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D132 --- src/query/frontend/ast/ast.hpp | 14 +++++++ src/query/frontend/ast/ast_visitor.hpp | 6 +-- src/query/frontend/logical/operator.hpp | 48 +++++++++++++++++++-- src/query/frontend/logical/planner.hpp | 27 ++++++++++-- tests/unit/interpreter.cpp | 56 +++++++++++++++++++++---- 5 files changed, 133 insertions(+), 18 deletions(-) diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index 005160ce7..2edbec2ac 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -84,6 +84,7 @@ class NodeAtom : public PatternAtom { std::shared_ptr identifier_; std::vector labels_; + std::map> properties_; }; class EdgeAtom : public PatternAtom { @@ -133,6 +134,19 @@ class Query : public Tree { std::vector> clauses_; }; +class Create : public Clause { + public: + Create(int uid) : Clause(uid) {} + std::vector> patterns_; + void Accept(TreeVisitorBase &visitor) override { + visitor.Visit(*this); + for (auto &pattern : patterns_) { + pattern->Accept(visitor); + } + visitor.PostVisit(*this); + } +}; + class Match : public Clause { public: Match(int uid) : Clause(uid) {} diff --git a/src/query/frontend/ast/ast_visitor.hpp b/src/query/frontend/ast/ast_visitor.hpp index 5ca61b69a..193d66710 100644 --- a/src/query/frontend/ast/ast_visitor.hpp +++ b/src/query/frontend/ast/ast_visitor.hpp @@ -9,6 +9,7 @@ class Query; class NamedExpression; class Identifier; class PropertyLookup; +class Create; class Match; class Return; class Pattern; @@ -16,7 +17,6 @@ class NodeAtom; class EdgeAtom; using TreeVisitorBase = - ::utils::Visitor; - + ::utils::Visitor; } diff --git a/src/query/frontend/logical/operator.hpp b/src/query/frontend/logical/operator.hpp index 06f39a72b..6064a59a2 100644 --- a/src/query/frontend/logical/operator.hpp +++ b/src/query/frontend/logical/operator.hpp @@ -27,9 +27,51 @@ class LogicalOperator { std::vector> children_; }; +class CreateOp : public LogicalOperator { +public: + CreateOp(std::shared_ptr node_atom) : node_atom_(node_atom) {} + +private: + class CreateOpCursor : public Cursor { + public: + CreateOpCursor(CreateOp& self, GraphDbAccessor& db) : self_(self), db_(db) {} + + 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()); + } + + did_create_ = true; + return true; + } else + return false; + } + + private: + CreateOp &self_; + GraphDbAccessor &db_; + bool did_create_{false}; + }; + +public: + std::unique_ptr MakeCursor(GraphDbAccessor& db) override { + return std::make_unique(*this, db); + } + +private: + std::shared_ptr node_atom_; +}; + class ScanAll : public LogicalOperator { public: - ScanAll(std::shared_ptr node_atom) : node_atom(node_atom) {} + ScanAll(std::shared_ptr node_atom) : node_atom_(node_atom) {} private: class ScanAllCursor : public Cursor { @@ -41,7 +83,7 @@ class ScanAll : public LogicalOperator { bool Pull(Frame& frame, SymbolTable& symbol_table) override { if (vertices_it_ == vertices_.end()) return false; - frame[symbol_table[*self_.node_atom->identifier_]] = *vertices_it_++; + frame[symbol_table[*self_.node_atom_->identifier_]] = *vertices_it_++; return true; } @@ -57,7 +99,7 @@ class ScanAll : public LogicalOperator { } private: - std::shared_ptr node_atom; + std::shared_ptr node_atom_; }; class NodeFilter : public LogicalOperator { diff --git a/src/query/frontend/logical/planner.hpp b/src/query/frontend/logical/planner.hpp index e9dc10a5e..013e37f89 100644 --- a/src/query/frontend/logical/planner.hpp +++ b/src/query/frontend/logical/planner.hpp @@ -8,6 +8,23 @@ namespace query { +std::shared_ptr GenCreate( + Create& match, std::shared_ptr current_op) +{ + if (current_op) { + throw std::runtime_error("Not implemented"); + } + if (match.patterns_.size() != 1) { + throw std::runtime_error("Not implemented"); + } + auto& pattern = match.patterns_[0]; + if (pattern->atoms_.size() != 1) { + throw std::runtime_error("Not implemented"); + } + auto node_atom = std::dynamic_pointer_cast(pattern->atoms_[0]); + return std::make_shared(node_atom); +} + std::shared_ptr GenMatch( Match& match, std::shared_ptr current_op) { @@ -21,8 +38,8 @@ std::shared_ptr GenMatch( if (pattern->atoms_.size() != 1) { throw std::runtime_error("Not implemented"); } - auto node_part = std::dynamic_pointer_cast(pattern->atoms_[0]); - return std::make_shared(node_part); + auto node_atom = std::dynamic_pointer_cast(pattern->atoms_[0]); + return std::make_shared(node_atom); } std::shared_ptr GenReturn( @@ -39,9 +56,12 @@ std::shared_ptr MakeLogicalPlan(Query& query) std::shared_ptr current_op; for (auto& clause : query.clauses_) { auto* clause_ptr = clause.get(); + auto* create = dynamic_cast(clause_ptr); auto* match = dynamic_cast(clause_ptr); auto* ret = dynamic_cast(clause_ptr); - if (match) { + if (create) { + current_op = GenCreate(*create, current_op); + } else if (match) { current_op = GenMatch(*match, current_op); } else if (ret) { return GenReturn(*ret, current_op); @@ -51,5 +71,4 @@ std::shared_ptr MakeLogicalPlan(Query& query) } return current_op; } - } diff --git a/tests/unit/interpreter.cpp b/tests/unit/interpreter.cpp index 0aad3ee88..a3ee818a6 100644 --- a/tests/unit/interpreter.cpp +++ b/tests/unit/interpreter.cpp @@ -55,6 +55,14 @@ auto CollectProduce(std::shared_ptr produce, SymbolTable &symbol_table, return stream; } +void ExecuteCreate(std::shared_ptr create, GraphDbAccessor &db) { + SymbolTable symbol_table; + Frame frame(symbol_table.max_position()); + auto cursor = create->MakeCursor(db); + while (cursor->Pull(frame, symbol_table)) + ; +} + /* * Following are helper functions that create high level AST * and logical operator objects. @@ -137,7 +145,8 @@ TEST(Interpreter, NodeFilterLabelsAndProperties) { auto v4 = dba->insert_vertex(); auto v5 = dba->insert_vertex(); dba->insert_vertex(); - // test all combination of (label | no_label) * (no_prop | wrong_prop | right_prop) + // test all combination of (label | no_label) * (no_prop | wrong_prop | + // right_prop) // only v1 will have the right labels v1.add_label(label); v2.add_label(label); @@ -184,17 +193,17 @@ TEST(Interpreter, NodeFilterMultipleLabels) { GraphDb::Label label2 = dba->label("label2"); GraphDb::Label label3 = dba->label("label3"); // the test will look for nodes that have label1 and label2 - dba->insert_vertex(); // NOT accepted - dba->insert_vertex().add_label(label1); // NOT accepted - dba->insert_vertex().add_label(label2); // NOT accepted - dba->insert_vertex().add_label(label3); // NOT accepted - auto v1 = dba->insert_vertex(); // YES accepted + dba->insert_vertex(); // NOT accepted + dba->insert_vertex().add_label(label1); // NOT accepted + dba->insert_vertex().add_label(label2); // NOT accepted + dba->insert_vertex().add_label(label3); // NOT accepted + auto v1 = dba->insert_vertex(); // YES accepted v1.add_label(label1); v1.add_label(label2); - auto v2 = dba->insert_vertex(); // NOT accepted + auto v2 = dba->insert_vertex(); // NOT accepted v2.add_label(label1); v2.add_label(label3); - auto v3 = dba->insert_vertex(); // YES accepted + auto v3 = dba->insert_vertex(); // YES accepted v3.add_label(label1); v3.add_label(label2); v3.add_label(label3); @@ -226,3 +235,34 @@ TEST(Interpreter, NodeFilterMultipleLabels) { ResultStreamFaker result = CollectProduce(produce, symbol_table, *dba); EXPECT_EQ(result.GetResults().size(), 2); } + +TEST(Interpreter, CreateNodeWithAttributes) { + Dbms dbms; + auto dba = dbms.active(); + Config config; + Context ctx(config, *dba); + + GraphDb::Label label = dba->label("Person"); + GraphDb::Property property = dba->label("age"); + + auto node = MakeNode(ctx, MakeIdentifier(ctx, "n")); + node->labels_.emplace_back(label); + // TODO make a property here with an int literal expression + // node->properties_[property] = TypedValue(42); + + auto create = std::make_shared(node); + ExecuteCreate(create, *dba); + + // count the number of vertices + int vertex_count = 0; + for (VertexAccessor vertex : dba->vertices()) { + vertex_count++; + EXPECT_EQ(vertex.labels().size(), 1); + EXPECT_EQ(*vertex.labels().begin(), label); + EXPECT_EQ(vertex.Properties().size(), 1); + auto prop_eq = vertex.PropsAt(property) == TypedValue(42); + ASSERT_EQ(prop_eq.type(), TypedValue::Type::Bool); + EXPECT_TRUE(prop_eq.Value()); + } + EXPECT_EQ(vertex_count, 1); +}