Cypher CRUD support is becomes bigger and bigger. Implemented: create relationship, match relationship and update relationship

This commit is contained in:
Marko Budiselic 2016-07-31 18:58:12 +01:00
parent 6970170f69
commit d04cf101b4
12 changed files with 222 additions and 61 deletions

View File

@ -9,8 +9,11 @@ project(${ProjectId})
find_package(Threads REQUIRED)
# flags
# c++14
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y")
# glibcxx debug
# set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG")
# functions
@ -175,14 +178,13 @@ EXECUTE_PROCESS(
)
# # memgraph executable
# add_executable(memgraph src/memgraph.cpp)
# add_dependencies(memgraph cypher_lib)
# # memgraph link libraries
# target_link_libraries(memgraph Threads::Threads)
# target_link_libraries(memgraph pcre)
# target_link_libraries(memgraph ${libuv_static_lib})
# target_link_libraries(memgraph ${r3_static_lib})
# target_link_libraries(memgraph ${http_parser_static_lib})
# add_executable(memgraph_http src/memgraph.cpp)
# add_dependencies(memgraph_http cypher_lib)
# target_link_libraries(memgraph_http Threads::Threads)
# target_link_libraries(memgraph_http pcre)
# target_link_libraries(memgraph_http ${libuv_static_lib})
# target_link_libraries(memgraph_http ${r3_static_lib})
# target_link_libraries(memgraph_http ${http_parser_static_lib})
# # query_engine executable
# add_executable(query_engine src/query_engine/main_query_engine.cpp)
@ -214,11 +216,33 @@ set(memgraph_src_files
${src_dir}/transactions/transaction.cpp
${src_dir}/template_engine/engine.cpp
)
# hard coded implementation of queries
# add_executable(queries src/query_engine/main_queries.cpp ${memgraph_src_files})
# target_link_libraries(queries ${fmt_static_lib})
add_library(libmemgraph ${memgraph_src_files})
# tests
enable_testing()
add_subdirectory(tests)
# memgraph build name
execute_process(
OUTPUT_VARIABLE COMMIT_NO
COMMAND git rev-list --count HEAD
)
execute_process(
OUTPUT_VARIABLE COMMIT_HASH
COMMAND git rev-parse --short HEAD
)
string(STRIP ${COMMIT_HASH} COMMIT_HASH)
string(STRIP ${COMMIT_NO} COMMIT_NO)
set(MEMGRAPH_BUILD_NAME "memgraph_${COMMIT_HASH}_${COMMIT_NO}")
# DEBUG BUILD
add_executable(${MEMGRAPH_BUILD_NAME}_debug ${src_dir}/memgraph_bolt.cpp)
# TEST BUILD
# TODO
# O-OPTIMIZED BUILD
# TODO
# RELEASE BUILD
# TODO

17
src/memgraph_bolt.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "utils/terminate_handler.hpp"
int main(int argc, char *argv[])
{
if (argc < 2) {
std::cout << "Port not defined" << std::endl;
std::exit(0);
}
auto port = std::stoi(argv[1]);
std::cout << "Port is: " << port << std::endl;
std::set_terminate(&terminate_handler);
return 0;
}

View File

@ -2,6 +2,23 @@
#include "query_engine/code_generator/handlers/includes.hpp"
using Direction = RelationshipData::Direction;
auto update_properties(const QueryActionData &action_data,
const std::string &name)
{
std::string code = "";
auto entity_data = action_data.get_entity_property(name);
for (auto &property : entity_data.properties) {
auto index =
action_data.parameter_index.at(ParameterIndexKey(name, property));
code += LINE(fmt::format(code::set_property, name, property, index));
}
return code;
}
auto create_query_action =
[](CypherStateData &cypher_data,
const QueryActionData &action_data) -> std::string {
@ -11,26 +28,72 @@ auto create_query_action =
for (auto const &kv : action_data.actions) {
if (kv.second == ClauseAction::CreateNode) {
// 1. create node 2. update labels 3. update properties
// create node
auto &name = kv.first;
code += LINE(fmt::format(code::create_vertex, name));
// update properties
code += update_properties(action_data, name);
// update labels
auto entity_data = action_data.get_entity_property(name);
for (auto &property : entity_data.properties) {
auto index = action_data.parameter_index.at(
ParameterIndexKey(name, property));
code += LINE(fmt::format(code::set_vertex_property, name,
property, index));
}
for (auto &label : entity_data.tags) {
code += LINE(fmt::format(code::create_label, label));
code += LINE(fmt::format(code::add_label, name, label));
}
// mark node as created
cypher_data.node_created(name);
}
if (kv.second == ClauseAction::CreateRelationship) {
// create relationship
auto name = kv.first;
code += LINE(fmt::format(code::create_edge, name));
// update properties
code += update_properties(action_data, name);
// update tag
auto entity_data = action_data.get_entity_property(name);
for (auto &tag : entity_data.tags) {
code += LINE(fmt::format(code::find_type, tag));
code += LINE(fmt::format(code::set_type, name, tag));
}
// find start and end node
auto &relationships_data = action_data.relationship_data;
if (relationships_data.find(name) == relationships_data.end())
throw CodeGenerationError("Unable to find data for: " + name);
auto &relationship_data = relationships_data.at(name);
auto left_node = relationship_data.nodes.first;
auto right_node = relationship_data.nodes.second;
// TODO: If node isn't already matched or created it has to be
// created here. It is not possible for now.
if (cypher_data.status(left_node) != EntityStatus::Matched) {
throw SemanticError("Create Relationship: node " + left_node +
" can't be found");
}
if (cypher_data.status(right_node) != EntityStatus::Matched) {
throw SemanticError("Create Relationship: node " + right_node +
" can't be found");
}
// define direction
if (relationship_data.direction == Direction::Right) {
code += LINE(fmt::format(code::node_out, left_node, name));
code += LINE(fmt::format(code::node_in, right_node, name));
code += LINE(fmt::format(code::edge_from, name, left_node));
code += LINE(fmt::format(code::edge_to, name, right_node));
} else if (relationship_data.direction == Direction::Left) {
code += LINE(fmt::format(code::node_out, right_node, name));
code += LINE(fmt::format(code::node_in, left_node, name));
code += LINE(fmt::format(code::edge_from, name, right_node));
code += LINE(fmt::format(code::edge_to, name, left_node));
}
// mark relationship as created
cypher_data.relationship_created(name);
}
}

View File

@ -31,12 +31,12 @@ auto match_query_action =
if (kv.second == ClauseAction::MatchNode) {
auto name = kv.first;
if (already_matched(cypher_data, name, EntityType::Node)) continue;
cypher_data.node_matched(name);
auto place = action_data.csm.min(kv.first);
if (place == entity_search::search_internal_id) {
auto index = fetch_internal_index(action_data, name);
code +=
LINE(fmt::format(code::match_vertex_by_id, name, index));
cypher_data.node_matched(name);
}
}
@ -45,11 +45,11 @@ auto match_query_action =
auto name = kv.first;
if (already_matched(cypher_data, name, EntityType::Relationship))
continue;
cypher_data.relationship_matched(name);
auto place = action_data.csm.min(kv.first);
if (place == entity_search::search_internal_id) {
auto index = fetch_internal_index(action_data, name);
code += LINE(fmt::format(code::match_edge_by_id, name, index));
cypher_data.relationship_matched(name);
}
}
}

View File

@ -9,16 +9,17 @@ auto set_query_action = [](CypherStateData &cypher_data,
for (auto const &kv : action_data.actions) {
auto name = kv.first;
if (kv.second == ClauseAction::UpdateNode &&
cypher_data.status(name) == EntityStatus::Matched &&
cypher_data.type(name) == EntityType::Node) {
auto entity_data = action_data.get_entity_property(name);
for (auto &property : entity_data.properties) {
auto index = action_data.parameter_index.at(
ParameterIndexKey(name, property));
code += LINE(
fmt::format(code::update_property, name, property, index));
}
code += update_properties(action_data, name);
}
if (kv.second == ClauseAction::UpdateRelationship &&
cypher_data.status(name) == EntityStatus::Matched &&
cypher_data.type(name) == EntityType::Relationship) {
code += update_properties(action_data, name);
}
}

View File

@ -76,7 +76,7 @@ struct ParameterIndexKey
}
};
struct RelationshipData : public EntityData
struct RelationshipData
{
enum class Direction
{

View File

@ -14,6 +14,6 @@ public:
{
runtime_assert(this->size() > 1, "Array size shoud be bigger than 1");
return std::make_pair(*(this->end() - 2), *(this->end() - 1));
return std::make_pair(*(this->end() - 1), *(this->end() - 2));
}
};

View File

@ -10,3 +10,10 @@ public:
SemanticError(const std::string& what) :
BasicException("Semantic error: " + what) {}
};
class CodeGenerationError : public BasicException
{
public:
CodeGenerationError(const std::string& what) :
BasicException("Code Generation error: " + what) {}
};

View File

@ -16,6 +16,16 @@ auto load_queries(Db& db)
{
std::map<uint64_t, std::function<bool(const properties_t &)>> queries;
// CREATE (n {prop: 0}) RETURN n)
auto create_node = [&db](const properties_t &args) {
auto &t = db.tx_engine.begin();
auto vertex_accessor = db.graph.vertices.insert(t);
vertex_accessor.property("prop", args[0]);
t.commit();
return true;
};
queries[11597417457737499503u] = create_node;
auto create_labeled_and_named_node = [&db](const properties_t &args) {
auto &t = db.tx_engine.begin();
auto vertex_accessor = db.graph.vertices.insert(t);
@ -137,6 +147,28 @@ auto load_queries(Db& db)
return true;
};
// MATCH (n1), (n2) WHERE ID(n1)=0 AND ID(n2)=1 CREATE (n1)<-[r:IS {age: 25, weight: 70}]-(n2) RETURN r]
auto create_edge_v2 = [&db](const properties_t &args)
{
auto& t = db.tx_engine.begin();
auto n1 = db.graph.vertices.find(t, args[0]->as<Int64>().value);
if (!n1) return t.commit(), false;
auto n2 = db.graph.vertices.find(t, args[1]->as<Int64>().value);
if (!n2) return t.commit(), false;
auto r = db.graph.edges.insert(t);
r.property("age", args[2]);
r.property("weight", args[3]);
auto &IS = db.graph.edge_type_store.find_or_create("IS");
r.edge_type(IS);
n2.vlist->update(t)->data.out.add(r.vlist);
n1.vlist->update(t)->data.in.add(r.vlist);
r.from(n2.vlist);
r.to(n1.vlist);
t.commit();
return true;
};
queries[15648836733456301916u] = create_edge_v2;
queries[10597108978382323595u] = create_account;
queries[5397556489557792025u] = create_labeled_and_named_node;
queries[7939106225150551899u] = create_edge;
@ -146,5 +178,6 @@ auto load_queries(Db& db)
queries[6813335159006269041u] = update_node;
queries[4857652843629217005u] = find_by_label;
return queries;
}

View File

@ -20,15 +20,24 @@ const std::string transaction_begin = "auto& t = db.tx_engine.begin();";
const std::string transaction_commit = "t.commit();";
const std::string set_property = "{}.property(\"{}\", args[{}]);";
// create vertex e.g. CREATE (n:PERSON {name: "Test", age: 23})
const std::string create_vertex =
"auto {} = db.graph.vertices.insert(t);";
const std::string set_vertex_property =
"{}.property(\"{}\", args[{}]);";
const std::string create_vertex = "auto {} = db.graph.vertices.insert(t);";
const std::string create_label =
"auto &{0} = db.graph.label_store.find_or_create(\"{0}\");";
const std::string add_label = "{}.add_label({});";
// create edge e.g CREATE (n1)-[r:COST {cost: 100}]->(n2)
const std::string create_edge = "auto {} = db.graph.edges.insert(t);";
const std::string find_type =
"auto &{0} = db.graph.edge_type_store.find_or_create(\"{0}\");";
const std::string set_type = "{}.edge_type({});";
const std::string node_out = "{}.vlist->update(t)->data.out.add({}.vlist);";
const std::string node_in = "{}.vlist->update(t)->data.in.add({}.vlist);";
const std::string edge_from = "{}.from({}.vlist);";
const std::string edge_to = "{}.to({}.vlist);";
const std::string args_id = "auto id = args[{}]->as<Int32>();";
const std::string vertex_accessor_args_id =
@ -36,21 +45,16 @@ const std::string vertex_accessor_args_id =
const std::string match_vertex_by_id =
"auto {0} = db.graph.vertices.find(t, args[{1}]->as<Int64>().value);\n"
"if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
" if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
const std::string match_edge_by_id =
"auto {0} = db.graph.edges.find(t, args[{1}]->as<Int64>().value);\n"
"if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
const std::string create_edge =
"auto {} = db.graph.edges.insert(t);";
" if (!{0}) return t.commit(), std::make_shared<QueryResult>();";
const std::string return_empty_result =
"return std::make_shared<QueryResult>();";
const std::string update_property =
"{}.property(\"{}\", args[{}]);";
const std::string update_property = "{}.property(\"{}\", args[{}]);";
const std::string todo = "// TODO: {}";

View File

@ -3,6 +3,7 @@
#include <string>
#include "cypher/visitor/traverser.hpp"
#include "query_engine/code_generator/cpp_generator.hpp"
#include "query_engine/code_generator/entity_search.hpp"
#include "query_engine/code_generator/structures.hpp"
@ -209,10 +210,9 @@ public:
{
// TODO: Is that traversal order OK for all cases? Probably NOT.
if (ast_pattern.has_next()) {
Traverser::visit(*(ast_pattern.next));
if (ast_pattern.has_node()) Traverser::visit(*(ast_pattern.node));
if (ast_pattern.has_relationship())
Traverser::visit(*(ast_pattern.relationship));
visit(*ast_pattern.next);
visit(*ast_pattern.node);
visit(*ast_pattern.relationship);
} else {
Traverser::visit(ast_pattern);
}
@ -252,7 +252,7 @@ public:
if (!internal_id_expr.has_id()) return;
auto name = internal_id_expr.entity_name();
// because entity_id will be value index inside the parameters array
// because entity_id value will be index inside the parameters array
auto index = internal_id_expr.entity_id();
auto &data = generator.action_data();
@ -275,19 +275,11 @@ public:
void visit(ast::Relationship &ast_relationship) override
{
auto &cypher_data = generator.cypher_data();
auto &action_data = generator.action_data();
if (ast_relationship.has_name()) entity = ast_relationship.name();
// TODO: simplify somehow
if (state == CypherState::Create) {
if (ast_relationship.has_name()) {
auto name = ast_relationship.name();
if (!cypher_data.exist(name)) {
clause_action = ClauseAction::CreateRelationship;
create_relationship(name);
}
}
}
if (!ast_relationship.has_name())
return;
entity = ast_relationship.name();
using ast_direction = ast::Relationship::Direction;
using generator_direction = RelationshipData::Direction;
@ -298,9 +290,23 @@ public:
if (ast_relationship.direction == ast_direction::Right)
direction = generator_direction::Right;
Traverser::visit(ast_relationship);
// TODO: add suport for Direction::Both
// TODO: simplify somehow
if (state == CypherState::Create) {
if (!cypher_data.exist(entity)) {
clause_action = ClauseAction::CreateRelationship;
create_relationship(entity);
}
}
if (state == CypherState::Match) {
if (!cypher_data.exist(entity)) {
action_data.actions[entity] = ClauseAction::MatchRelationship;
}
}
Traverser::visit(ast_relationship);
}
void visit(ast::RelationshipSpecs &ast_relationship_specs) override
@ -317,6 +323,12 @@ public:
}
}
if (state == CypherState::Create) {
if (ast_relationship_specs.has_identifier()) {
entity = ast_relationship_specs.name();
}
}
Traverser::visit(ast_relationship_specs);
}