Dressipi Test Cases No 1 - DONE

This commit is contained in:
Marko Budiselic 2016-11-04 01:49:21 +01:00
parent d45121a1a2
commit dba261036b
14 changed files with 349 additions and 69 deletions

View File

@ -18,5 +18,9 @@ enum class ClauseAction : uint32_t
ReturnPack,
ReturnProjection,
ReturnCount,
ReturnLabels
ReturnLabels,
UpdateEntityLabels,
UpdateEntityLabels_Identifier,
UpdateEntityLabels_Labels
};

View File

@ -86,15 +86,75 @@ const std::string find_and_write_vertices_by_label =
" stream.write_meta(\"rw\");\n";
const std::string find_and_write_vertices_by_label_and_properties =
"{0}\n"
" auto properties = query_properties(indices, args);\n"
" auto &label = t.label_find_or_create(\"{1}\");\n"
" stream.write_field(\"{2}\");\n"
" label.index().for_range(t).properties_filter(t, properties).for_all(\n"
"{{\n"
" DbAccessor _t(db);\n"
" {0}\n"
" auto properties = query_properties(indices, args);\n"
" auto &label = _t.label_find_or_create(\"{1}\");\n"
" stream.write_field(\"{2}\");\n"
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
" [&](auto vertex_accessor) -> void {{\n"
" "+ write_vertex_accessor +
" }});\n"
" stream.write_meta(\"rw\");\n"
" _t.commit();"
"}}\n";
// -- LABELS
const std::string set_vertex_element =
"{{\n"
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
" {0}\n"
" auto properties = query_properties(indices, args);\n"
" auto &label = _t.label_find_or_create(\"{1}\");\n"
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
" [&](auto vertex_accessor) -> void {{\n"
" "+ write_vertex_accessor +
" }});\n"
" stream.write_meta(\"rw\");\n";
" auto {2} = _t.vertex_property_key(\"{2}\", args[{3}].key.flags());\n"
" vertex_accessor.set({2}, std::move(args[{3}]));\n"
" }}\n"
" );\n"
" _t.commit();\n"
"}}";
const std::string set_labels_start =
" {{\n"
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
" {0}\n"
" auto properties = query_properties(indices, args);\n"
" auto &label = _t.label_find_or_create(\"{1}\");\n"
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
" [&](auto vertex_accessor) -> void {{\n";
const std::string set_label =
" auto &{0} = _t.label_find_or_create(\"{0}\");\n"
" vertex_accessor.add_label({0});\n";
const std::string set_labels_end =
" }}\n"
" );\n"
" _t.commit();"
" }}";
const std::string return_labels =
"{{\n"
" DbAccessor _t(db);" // TODO: HACK (set labels should somehow persist the state)
" {0}\n"
" auto properties = query_properties(indices, args);\n"
" auto &label = _t.label_find_or_create(\"{1}\");\n"
" stream.write_field(\"labels({2})\");\n"
" label.index().for_range(_t).properties_filter(_t, properties).for_all(\n"
" [&](auto vertex_accessor) -> void {{\n"
" auto &labels = vertex_accessor.labels();\n"
" stream.write_record();\n"
" stream.write_list_header(1);\n" // TODO: figure out why
" stream.write_list_header(labels.size());\n"
" for (auto &label : labels) {{\n"
" stream.write(label.get().str());\n"
" }}\n"
" stream.chunk();\n"
" }}\n"
" );\n"
" stream.write_meta(\"rw\");\n"
" _t.commit();\n"
"}}";
const std::string write_all_edges =
"stream.write_field(\"{0}\");\n"

View File

@ -66,8 +66,6 @@ auto return_query_action =
}
}
if (cypher_data.source(entity) == EntitySource::TypeIndex)
{
if (cypher_data.type(entity) == EntityType::Relationship)
@ -116,7 +114,17 @@ auto return_query_action =
}
if (kv.second == ClauseAction::ReturnLabels)
{
// TODO: similar to above
if (cypher_data.source(name) == EntitySource::LabelIndex)
{
auto tags = cypher_data.tags(name);
if (tags.size() == 1)
{
auto label = tags.at(0);
code += code_line(code::return_labels,
cypher_data.print_indices(name), label,
name);
}
}
}
}

View File

@ -7,21 +7,66 @@ auto set_query_action = [](CypherStateData &cypher_data,
std::string code = "";
for (auto const &kv : action_data.actions) {
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) {
cypher_data.source(name) == EntitySource::InternalId &&
cypher_data.type(name) == EntityType::Node)
{
code += update_properties(cypher_data, action_data, name);
}
if (kv.second == ClauseAction::UpdateNode &&
cypher_data.status(name) == EntityStatus::Matched &&
cypher_data.source(name) == EntitySource::LabelIndex &&
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));
auto tmp_name = name::unique();
auto label = cypher_data.tags(name).at(0);
// TODO: move this code inside the loop (in generated code)
code += code_line(code::set_vertex_element,
cypher_data.print_indices(name), label,
property, index);
}
}
if (kv.second == ClauseAction::UpdateRelationship &&
cypher_data.status(name) == EntityStatus::Matched &&
cypher_data.type(name) == EntityType::Relationship) {
cypher_data.type(name) == EntityType::Relationship)
{
code += update_properties(cypher_data, action_data, name);
}
}
for (auto const &set_entity_labels : action_data.label_set_elements)
{
auto &entity = set_entity_labels.entity;
if (cypher_data.status(entity) == EntityStatus::Matched &&
cypher_data.source(entity) == EntitySource::LabelIndex)
{
auto label = cypher_data.tags(entity).at(0);
if (cypher_data.has_properties(entity))
{
code += code_line(code::set_labels_start,
cypher_data.print_indices(entity), label);
auto labels = set_entity_labels.labels;
for (auto const &set_label : labels)
{
code += code_line(code::set_label, set_label);
}
code += code_line(code::set_labels_end);
}
}
}
return code;
};

View File

@ -111,6 +111,22 @@ struct ReturnElement
bool is_projection() const { return has_entity() && has_property(); }
};
struct LabelSetElement
{
std::string entity;
std::vector<std::string> labels;
LabelSetElement() = default;
LabelSetElement(const LabelSetElement&) = default;
LabelSetElement(LabelSetElement&&) = default;
void clear()
{
entity.clear();
labels.clear();
}
};
struct QueryActionData
{
std::map<ParameterIndexKey, uint64_t> parameter_index;
@ -118,6 +134,7 @@ struct QueryActionData
std::map<std::string, EntityData> entity_data;
std::map<std::string, RelationshipData> relationship_data;
std::vector<ReturnElement> return_elements;
std::vector<LabelSetElement> label_set_elements;
bool is_detach;
CypherStateMachine csm;

View File

@ -30,8 +30,8 @@ struct SetElementState
void clear()
{
set_entity = "";
set_prop = "";
set_index = -1;
set_prop = "";
set_index = -1;
}
};
@ -52,7 +52,7 @@ struct PropertyState
void clear()
{
property_name = "";
property_name = "";
property_index = -1;
}
};
@ -79,6 +79,7 @@ private:
RelationshipData::Direction direction;
SetElementState set_element_state;
LabelSetElement labels_set_element;
PropertyState property_state;
void clear_state()
@ -95,7 +96,9 @@ private:
void finish_query_execution()
{
generator.add_action(QueryAction::TransactionCommit);
code += generator.generate();
generator.clear();
}
@ -182,7 +185,7 @@ public:
code += generator.generate();
generator.add_action(QueryAction::Create);
state = CypherState::Create;
state = CypherState::Create;
query_action = QueryAction::Create;
Traverser::visit(ast_create);
@ -194,10 +197,12 @@ public:
generator.add_action(QueryAction::Set);
state = CypherState::Set;
state = CypherState::Set;
query_action = QueryAction::Set;
Traverser::visit(ast_set);
code += generator.generate();
}
void visit(ast::Return &ast_return) override
@ -206,7 +211,7 @@ public:
generator.add_action(QueryAction::Return);
state = CypherState::Return;
state = CypherState::Return;
query_action = QueryAction::Return;
Traverser::visit(ast_return);
@ -225,11 +230,14 @@ public:
void visit(ast::Pattern &ast_pattern) override
{
// TODO: Is that traversal order OK for all cases? Probably NOT.
if (ast_pattern.has_next()) {
if (ast_pattern.has_next())
{
visit(*ast_pattern.next);
visit(*ast_pattern.node);
visit(*ast_pattern.relationship);
} else {
}
else
{
Traverser::visit(ast_pattern);
}
}
@ -242,17 +250,22 @@ public:
if (!ast_node.has_identifier()) return;
auto name = ast_node.idn->name;
entity = name;
entity = name;
visited_nodes.push_back(name);
if (state == CypherState::Match) {
if (state == CypherState::Match)
{
action_data.actions[name] = ClauseAction::MatchNode;
}
if (state == CypherState::Create) {
if (cypher_data.status(name) == EntityStatus::Matched) {
if (state == CypherState::Create)
{
if (cypher_data.status(name) == EntityStatus::Matched)
{
action_data.actions[name] = ClauseAction::MatchNode;
} else {
}
else
{
action_data.actions[name] = ClauseAction::CreateNode;
}
}
@ -260,7 +273,8 @@ public:
Traverser::visit(ast_node);
if (cypher_data.status(name) != EntityStatus::Matched &&
state == CypherState::Create) {
state == CypherState::Create)
{
cypher_data.node_created(name);
}
}
@ -286,9 +300,9 @@ public:
// -- RELATIONSHIP --
void create_relationship(const std::string &name)
{
auto &data = generator.action_data();
auto &data = generator.action_data();
data.actions[name] = ClauseAction::CreateRelationship;
auto nodes = visited_nodes.last_two();
auto nodes = visited_nodes.last_two();
data.relationship_data.emplace(name,
RelationshipData(nodes, direction));
}
@ -301,7 +315,7 @@ public:
if (!ast_relationship.has_name()) return;
entity = ast_relationship.name();
using ast_direction = ast::Relationship::Direction;
using ast_direction = ast::Relationship::Direction;
using generator_direction = RelationshipData::Direction;
if (ast_relationship.direction == ast_direction::Left)
@ -313,15 +327,19 @@ public:
// TODO: add suport for Direction::Both
// TODO: simplify somehow
if (state == CypherState::Create) {
if (!cypher_data.exist(entity)) {
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)) {
if (state == CypherState::Match)
{
if (!cypher_data.exist(entity))
{
action_data.actions[entity] = ClauseAction::MatchRelationship;
}
}
@ -331,20 +349,25 @@ public:
void visit(ast::RelationshipSpecs &ast_relationship_specs) override
{
if (state == CypherState::Match) {
if (ast_relationship_specs.has_identifier()) {
auto name = ast_relationship_specs.name();
if (state == CypherState::Match)
{
if (ast_relationship_specs.has_identifier())
{
auto name = ast_relationship_specs.name();
auto &cypher_data = generator.cypher_data();
if (!cypher_data.exist(name)) {
clause_action = ClauseAction::MatchRelationship;
auto &data = generator.action_data();
if (!cypher_data.exist(name))
{
clause_action = ClauseAction::MatchRelationship;
auto &data = generator.action_data();
data.actions[name] = ClauseAction::MatchRelationship;
}
}
}
if (state == CypherState::Create) {
if (ast_relationship_specs.has_identifier()) {
if (state == CypherState::Create)
{
if (ast_relationship_specs.has_identifier())
{
entity = ast_relationship_specs.name();
}
}
@ -356,7 +379,8 @@ public:
{
auto &action_data = generator.action_data();
if (ast_relationship_type_list.has_value()) {
if (ast_relationship_type_list.has_value())
{
auto type = ast_relationship_type_list.value->name;
action_data.add_entity_tag(entity, type);
action_data.csm.search_cost(entity,
@ -369,6 +393,13 @@ public:
void visit(ast::LabelList &ast_label_list) override
{
if (state == CypherState::Set)
{
clause_action = ClauseAction::UpdateEntityLabels_Labels;
Traverser::visit(ast_label_list);
return;
}
auto &action_data = generator.action_data();
auto &cypher_data = generator.cypher_data();
@ -398,12 +429,13 @@ public:
Traverser::visit(ast_property);
// TODO: too ugly refactor somehow (clear_state part is awful)
if (entity.empty() || !property_state.is_defined()) {
if (entity.empty() || !property_state.is_defined())
{
clear_state();
return;
}
auto prop = property_state.property_name;
auto prop = property_state.property_name;
auto index = property_state.property_index;
// update action data
@ -423,20 +455,33 @@ public:
{
property_state.property_name = ast_identifier.name;
if (state == CypherState::Delete) {
if (state == CypherState::Delete)
{
auto &action_data = generator.action_data();
auto name = ast_identifier.name;
auto name = ast_identifier.name;
auto &cypher_data = generator.cypher_data();
if (cypher_data.type(name) == EntityType::Node)
action_data.actions[name] = ClauseAction::DeleteNode;
if (cypher_data.type(name) == EntityType::Relationship)
action_data.actions[name] = ClauseAction::DeleteRelationship;
}
if (state == CypherState::Set &&
clause_action == ClauseAction::UpdateEntityLabels_Identifier)
{
labels_set_element.entity = ast_identifier.name;
}
if (state == CypherState::Set &&
clause_action == ClauseAction::UpdateEntityLabels_Labels)
{
labels_set_element.labels.emplace_back(ast_identifier.name);
}
}
void visit(ast::Long &ast_long) override
{
set_element_state.set_index = ast_long.value;
set_element_state.set_index = ast_long.value;
property_state.property_index = ast_long.value;
}
@ -454,17 +499,18 @@ public:
{
Traverser::visit(ast_set_element);
if (!set_element_state.is_defined()) {
if (!set_element_state.is_defined())
{
clear_state();
return;
}
auto entity = set_element_state.set_entity;
auto prop = set_element_state.set_prop;
auto index = set_element_state.set_index;
auto prop = set_element_state.set_prop;
auto index = set_element_state.set_index;
auto &cypher_data = generator.cypher_data();
auto entity_type = cypher_data.type(entity);
auto entity_type = cypher_data.type(entity);
if (entity_type == EntityType::None)
throw CypherSemanticError("Entity (" + entity + ") doesn't exist");
@ -489,12 +535,16 @@ public:
auto &action_data = generator.action_data();
if (state == CypherState::Return) {
if (state == CypherState::Return)
{
auto &return_elements = action_data.return_elements;
auto &entity = ast_accessor.entity_name();
if (!ast_accessor.has_prop()) {
auto &entity = ast_accessor.entity_name();
if (!ast_accessor.has_prop())
{
return_elements.emplace_back(ReturnElement(entity));
} else {
}
else
{
auto &property = ast_accessor.entity_prop();
return_elements.emplace_back(ReturnElement(entity, property));
}
@ -502,9 +552,10 @@ public:
if (!ast_accessor.has_prop()) return;
if (state == CypherState::Set) {
if (state == CypherState::Set)
{
set_element_state.set_entity = ast_accessor.entity_name();
set_element_state.set_prop = ast_accessor.entity_prop();
set_element_state.set_prop = ast_accessor.entity_prop();
}
}
@ -513,6 +564,21 @@ public:
Traverser::visit(ast_set_value);
}
void visit(ast::LabelSetElement &ast_label_set_element) override
{
clause_action = ClauseAction::UpdateEntityLabels_Identifier;
labels_set_element.clear();
Traverser::visit(ast_label_set_element);
auto &action_data = generator.action_data();
action_data.label_set_elements.emplace_back(std::move(labels_set_element));
clause_action = ClauseAction::Undefined;
}
void visit(ast::Delete &ast_delete) override
{
code += generator.generate();
@ -536,4 +602,10 @@ public:
action_data.actions[ast_count.argument] = ClauseAction::ReturnCount;
// }
}
void visit(ast::LabelsFunction &ast_label) override
{
auto &action_data = generator.action_data();
action_data.actions[ast_label.argument] = ClauseAction::ReturnLabels;
}
};

View File

@ -70,6 +70,7 @@ struct ReadWriteQuery;
struct SetKey;
struct SetValue;
struct SetElement;
struct LabelSetElement;
struct SetList;
struct WithList;
@ -87,8 +88,8 @@ struct AstVisitor
PatternList, Match, ReadQuery, Start, Where, WriteQuery, Create,
Return, Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey,
ReadWriteQuery, IdentifierList, WithList, WithClause, WithQuery, Long,
CountFunction, LabelsFunction,
InternalIdExpr, SetValue, SetElement, SetList>
CountFunction, LabelsFunction, InternalIdExpr, SetValue, SetElement,
LabelSetElement, SetList>
{
};
}

View File

@ -35,7 +35,19 @@ struct SetValue : public AstNode<SetValue>
bool has_value() const { return value != nullptr; }
};
struct SetElement : public AstNode<SetElement>
struct SetElementBase : public AstVisitable
{
};
template <class Derived>
struct SetElementDerivedBase : public Crtp<Derived>, public SetElementBase
{
using uptr = std::unique_ptr<Derived>;
virtual void accept(AstVisitor &visitor) { visitor.visit(this->derived()); }
};
struct SetElement : public SetElementDerivedBase<SetElement>
{
SetElement(Accessor* accessor, SetValue* set_value)
: accessor(accessor), set_value(set_value) {}
@ -47,7 +59,19 @@ struct SetElement : public AstNode<SetElement>
bool has_value() const { return set_value != nullptr; }
};
struct SetList : public List<SetElement, SetList>
struct LabelSetElement : public SetElementDerivedBase<LabelSetElement>
{
LabelSetElement(Identifier* identifier, LabelList* label_list)
: identifier(identifier), label_list(label_list) {}
Identifier* identifier;
LabelList* label_list;
bool has_identifier() const { return identifier != nullptr; }
bool has_label_list() const { return label_list != nullptr; }
};
struct SetList : public List<SetElementBase, SetList>
{
using List::List;
};

View File

@ -544,12 +544,16 @@ set_list(L) ::= set_element(E). {
L = ast->create<ast::SetList>(E, nullptr);
}
%type set_element {ast::SetElement*}
%type set_element {ast::SetElementBase*}
set_element(E) ::= accessor(A) EQ set_value(V). {
E = ast->create<ast::SetElement>(A, V);
}
set_element(E) ::= idn(I) label_idn(L). {
E = ast->create<ast::LabelSetElement>(I, L);
}
%type accessor {ast::Accessor*}
accessor(A) ::= idn(E) DOT idn(P). {

View File

@ -395,6 +395,12 @@ public:
Traverser::visit(set_element);
}
void visit(ast::LabelSetElement &label_set_element) override
{
auto entry = printer.advance("Label Set Element");
Traverser::visit(label_set_element);
}
void visit(ast::SetList &set_list) override
{
auto entry = printer.advance("Set List");

View File

@ -244,6 +244,12 @@ public:
accept(set_element.set_value);
}
void visit(ast::LabelSetElement& label_set_element) override
{
accept(label_set_element.identifier);
accept(label_set_element.label_list);
}
void visit(ast::SetList& set_list) override
{
accept(set_list.value);

View File

@ -4,18 +4,21 @@
import logging
# TODO: auto import
from scenario import no_000001
# TODO: better logging (scenario visibility)
# TODO: result storage
from scenario import no_000001, no_000002
from neo4j.v1 import GraphDatabase, basic_auth, types, Node
scenarios = [no_000001.scenario_1]
# scenarios = [no_000001.scenario_1, no_000002.scenario_2]
scenarios = [no_000002.scenario_2]
# logging init
log = logging.getLogger(__name__)
# initialize driver and create session
session = GraphDatabase.driver("bolt://localhost",
auth=basic_auth("neo4j", "1234"),
encrypted=0).session()
auth=basic_auth("neo4j", "1234"),
encrypted=0).session()
def check(condition, scenario_no, message):
@ -55,6 +58,7 @@ if __name__ == "__main__":
# in case that the result is single
if count == 1:
# extract properties from record
record = records[0]
record_name, = record

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
__author__ = "Marko Budiselic"
__date__ = "2016_11_02"
class Scenario1:
'''

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Marko Budiselic"
__date__ = "2016_11_03"
class Scenario2:
'''
Create node, edit labels and return all labels for the node.
'''
def __init__(self):
'''
Constructor
'''
self.no = 2
self.desctiption = "Create node, edit labels and return all labels for the node."
self.init_graph = []
self.queries = [
("MATCH (n) DETACH DELETE n", (0, None)),
("CREATE (n:Garment {garment_id: 1234, garment_category_id: 1}) RETURN n", (1, [{"garment_id": 1234, "garment_category_id": 1}])),
("MATCH (g:Garment {garment_id: 1234}) SET g:FF RETURN labels(g)", (1, [["Garment", "FF"]])),
("MATCH(g:Garment {garment_id: 1234}) SET g.reveals = 50 RETURN g", (1, [{"garment_id": 1234, "garment_category_id": 1, "reveals": 50}])),
("MATCH (n) RETURN n", (1, [{"garment_id": 1234, "garment_category_id": 1, "reveals": 50}])),
]
self.side_effects = None
scenario_2 = Scenario2()