diff --git a/.gitignore b/.gitignore index 9c371ef99..eb4c6612d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ release/memgraph_* release/libs/ release/barrier/ release/barrier_* - +build/compiled/ diff --git a/build/.gitignore b/build/.gitignore index 051042cc0..78d910160 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -1,3 +1,2 @@ /* !.gitignore -!/compiled diff --git a/include/query_engine/code_generator/clause_action.hpp b/include/query_engine/code_generator/clause_action.hpp index 6681259b5..4829460e2 100644 --- a/include/query_engine/code_generator/clause_action.hpp +++ b/include/query_engine/code_generator/clause_action.hpp @@ -17,5 +17,6 @@ enum class ClauseAction : uint32_t ReturnRelationship, ReturnPack, ReturnProjection, - ReturnCount + ReturnCount, + ReturnLabels }; diff --git a/include/query_engine/code_generator/handlers/all.hpp b/include/query_engine/code_generator/handlers/all.hpp index 7c1d11937..1561d953f 100644 --- a/include/query_engine/code_generator/handlers/all.hpp +++ b/include/query_engine/code_generator/handlers/all.hpp @@ -1,5 +1,7 @@ #pragma once +// TODO: refactor build state machine instead of ifs + #include "query_engine/code_generator/handlers/create.hpp" #include "query_engine/code_generator/handlers/delete.hpp" #include "query_engine/code_generator/handlers/match.hpp" diff --git a/include/query_engine/code_generator/handlers/delete.hpp b/include/query_engine/code_generator/handlers/delete.hpp index f9e1acbec..4a4fabb4b 100644 --- a/include/query_engine/code_generator/handlers/delete.hpp +++ b/include/query_engine/code_generator/handlers/delete.hpp @@ -11,7 +11,7 @@ auto delete_query_action = for (auto const &kv : action_data.actions) { auto entity = kv.first; if (kv.second == ClauseAction::DeleteNode) { - code += code_line("// DELETE Node({})", entity); + code += code_line(detach_delete_all_nodes); } if (kv.second == ClauseAction::DeleteRelationship) { code += code_line("// DELETE Relationship({})", entity); diff --git a/include/query_engine/code_generator/handlers/return.hpp b/include/query_engine/code_generator/handlers/return.hpp index 9d16cb550..e9aad4b63 100644 --- a/include/query_engine/code_generator/handlers/return.hpp +++ b/include/query_engine/code_generator/handlers/return.hpp @@ -80,6 +80,10 @@ auto return_query_action = code += code_line(code::count, name); } } + if (kv.second == ClauseAction::ReturnLabels) + { + // TODO: similar to above + } } return code; diff --git a/include/query_engine/traverser/code.hpp b/include/query_engine/traverser/code.hpp index e274efe66..2f3a1fba3 100644 --- a/include/query_engine/traverser/code.hpp +++ b/include/query_engine/traverser/code.hpp @@ -126,3 +126,8 @@ const std::string print_properties = const std::string print_property = "cout_property(\"{0}\", {0}.property(\"{1}\"));"; } + +// DELETE +const std::string detach_delete_all_nodes = + "t.vertex_access().fill().isolated().for_all(" + " [&](auto a) {{ a.remove(); }});"; diff --git a/src/examples/bolt_py_client/requirements.txt b/requirements.txt similarity index 100% rename from src/examples/bolt_py_client/requirements.txt rename to requirements.txt diff --git a/src/cypher/ast/ast_visitor.hpp b/src/cypher/ast/ast_visitor.hpp index 76a0cf331..2cb8fb2d3 100644 --- a/src/cypher/ast/ast_visitor.hpp +++ b/src/cypher/ast/ast_visitor.hpp @@ -38,6 +38,7 @@ struct Rem; // functions struct CountFunction; +struct LabelsFunction; struct RelationshipSpecs; struct RelationshipTypeList; @@ -86,7 +87,7 @@ struct AstVisitor PatternList, Match, ReadQuery, Start, Where, WriteQuery, Create, Return, Distinct, Delete, DeleteQuery, UpdateQuery, Set, SetKey, ReadWriteQuery, IdentifierList, WithList, WithClause, WithQuery, Long, - CountFunction, + CountFunction, LabelsFunction, InternalIdExpr, SetValue, SetElement, SetList> { }; diff --git a/src/cypher/ast/functions.hpp b/src/cypher/ast/functions.hpp index 53d2bb7b8..c155e67a2 100644 --- a/src/cypher/ast/functions.hpp +++ b/src/cypher/ast/functions.hpp @@ -11,4 +11,12 @@ struct CountFunction : public FunctionExpr { } }; + +struct LabelsFunction : public FunctionExpr +{ + LabelsFunction(const std::string &argument) : FunctionExpr("labels", argument) + { + } +}; + } diff --git a/src/cypher/cypher.y b/src/cypher/cypher.y index bd65e6a2a..2081f4a11 100644 --- a/src/cypher/cypher.y +++ b/src/cypher/cypher.y @@ -501,6 +501,10 @@ function_expr(E) ::= COUNT LP IDN(A) RP. { E = ast->create(A->value); } +function_expr(E) ::= LABELS LP IDN(A) RP. { + E = ast->create(A->value); +} + %type expr {ast::Expr*} expr(E) ::= value_expr(V). { diff --git a/src/cypher/debug/tree_print.hpp b/src/cypher/debug/tree_print.hpp index d16fb30d9..b7a21e2db 100644 --- a/src/cypher/debug/tree_print.hpp +++ b/src/cypher/debug/tree_print.hpp @@ -300,6 +300,12 @@ public: entry << count.name << "(" << count.argument << ")"; } + void visit(ast::LabelsFunction& labels) override + { + auto entry = printer.advance("Labels "); + entry << labels.name << "(" << labels.argument << ")"; + } + void visit(ast::PropertyList& prop_list) override { auto entry = printer.advance("Property List"); diff --git a/src/cypher/tokenizer/cypher_lexer.hpp b/src/cypher/tokenizer/cypher_lexer.hpp index 3d708b48a..971fbb5f3 100644 --- a/src/cypher/tokenizer/cypher_lexer.hpp +++ b/src/cypher/tokenizer/cypher_lexer.hpp @@ -60,6 +60,7 @@ public: // functions rule("(?i:COUNT)", TK_COUNT); + rule("(?i:LABELS)", TK_LABELS); // string literal TODO single quote escape rule("'(.*?)'", TK_STR); diff --git a/src/cypher/visitor/traverser.hpp b/src/cypher/visitor/traverser.hpp index 66c5e9090..941fa28d6 100644 --- a/src/cypher/visitor/traverser.hpp +++ b/src/cypher/visitor/traverser.hpp @@ -158,6 +158,10 @@ public: { } + void visit(ast::LabelsFunction& labels) override + { + } + void visit(ast::PropertyList& prop_list) override { accept(prop_list.value); diff --git a/src/examples/bolt_py_client/.initial_test.py.swn b/src/examples/bolt_py_client/.initial_test.py.swn deleted file mode 100644 index a7f394ee9..000000000 Binary files a/src/examples/bolt_py_client/.initial_test.py.swn and /dev/null differ diff --git a/src/examples/bolt_py_client/create_benchmark.py b/src/examples/bolt_py_client/create_benchmark.py deleted file mode 100644 index f826dabce..000000000 --- a/src/examples/bolt_py_client/create_benchmark.py +++ /dev/null @@ -1,35 +0,0 @@ -import time -from neo4j.v1 import GraphDatabase, basic_auth, types -from concurrent.futures import ProcessPoolExecutor - -# create session -driver = GraphDatabase.driver("bolt://localhost", - auth=basic_auth("neo4j", "neo4j"), - encrypted=0) -session = driver.session() - -queries_no = 10 - -queries = ["CREATE (n {prop: 10}) RETURN n"] * queries_no - -def create_query(index): - ''' - Task (process or thread) - Runs create query agains the database. - - :param index: int -> number of task - :returns: (int, float) -> (task index, elapsed time) - ''' - start = time.time() - for query in queries: - for record in session.run(query): - pass - end = time.time() - return time - - -with ProcessPoolExecutor(processes=4) as executor: - results = [] - print(results) - -# print(1.0 * queries_no / (end - start)) diff --git a/tests/concurrent/common.h b/tests/concurrent/common.h index d43f7976b..5b2568812 100644 --- a/tests/concurrent/common.h +++ b/tests/concurrent/common.h @@ -1,27 +1,30 @@ -#include "stdio.h" -#include "stdlib.h" -#include "string.h" #include #include #include #include #include +#include "stdio.h" +#include "stdlib.h" +#include "string.h" #include "data_structures/bitset/dynamic_bitset.hpp" +#include "data_structures/concurrent/concurrent_list.hpp" #include "data_structures/concurrent/concurrent_map.hpp" #include "data_structures/concurrent/concurrent_multimap.hpp" #include "data_structures/concurrent/concurrent_multiset.hpp" #include "data_structures/concurrent/concurrent_set.hpp" #include "data_structures/concurrent/skiplist.hpp" -#include "data_structures/concurrent/concurrent_list.hpp" #include "data_structures/static_array.hpp" -#include "utils/assert.hpp" #include "logging/default.hpp" #include "logging/streams/stdout.hpp" +#include "utils/assert.hpp" #include "utils/sysinfo/memory.hpp" +// NOTE: this file is highly coupled to data_structures +// TODO: REFACTOR + // Sets max number of threads that will be used in concurrent tests. -constexpr int max_no_threads=8; +constexpr int max_no_threads = 8; using std::cout; using std::endl; @@ -300,8 +303,9 @@ void memory_check(size_t no_threads, std::function f) permanent_assert(leaked <= 0, "Memory leak check"); } -//Initializes loging faccilityes -void init_log(){ +// Initializes loging faccilityes +void init_log() +{ logging::init_async(); logging::log->pipe(std::make_unique()); } diff --git a/src/examples/bolt_py_client/initial_test.py b/tests/integration/internal/crud.py similarity index 100% rename from src/examples/bolt_py_client/initial_test.py rename to tests/integration/internal/crud.py diff --git a/tests/integration/pilot/dressipi/__init__.py b/tests/integration/pilot/dressipi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/pilot/dressipi/crud.py b/tests/integration/pilot/dressipi/crud.py new file mode 100644 index 000000000..6cef5524f --- /dev/null +++ b/tests/integration/pilot/dressipi/crud.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from neo4j.v1 import GraphDatabase, basic_auth, types + +# initialize driver and create session +# default username and password are used +driver = GraphDatabase.driver("bolt://localhost", + auth=basic_auth("neo4j", "neo4j"), + encrypted=0) +session = driver.session() + +# all test queries and expected results +# one element in queries is called test entry +# it contains test query + +# touple(number of expected results, expected properties) +# TODO: create custom data structures +queries = [ + ("CREATE (n:Garment {garment_id: 1234, garment_category_id: 1}) RETURN n", + (1, [{"garment_id": 1234, "garment_category_id": 1}])), + ("CREATE(p:Profile {profile_id: 111, partner_id: 55}) RETURN p", + (1, [{"profile_id": 111, "partner_id": 55}])), + # ("MATCH (p:Profile) RETURN p", + # (1, [{"profile_id": 111, "partner_id": 55}])), + ("MATCH (n) DELETE n", + (0, [])) +]; + +# iterate through all queries and execute them agains the database +for query, result in queries: + + # extract count and properties from test entries + count, test_properties = result + records = [record for record in session.run(query)] + + # check count + assert len(records) == count, \ + "Number of results for %s isn't good;" \ + " expected: %s, got %s" % (query, count, len(records)) + + # in case that result should contain just one result + # test properties + # TODO: test others + if count == 1: + # extract properties from record + record = records[0] + record_name, = record + received_properties = {key: value + for (key, value) in record[record_name].items()} + + # get expected properties + expected_properties = test_properties[0] + + # check properties + assert expected_properties == received_properties, \ + "Received properties for %s are not good; expected: %s, " \ + "got %s" % (query, expected_properties, received_properties) + +print("Dressipi integration test passed OK") diff --git a/tests/integration/pilot/dressipi/run.py b/tests/integration/pilot/dressipi/run.py new file mode 100644 index 000000000..059efc6b4 --- /dev/null +++ b/tests/integration/pilot/dressipi/run.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import crud