diff --git a/tests/gql_behave/run.py b/tests/gql_behave/run.py index c2112681a..5a8e3c194 100755 --- a/tests/gql_behave/run.py +++ b/tests/gql_behave/run.py @@ -65,11 +65,30 @@ def main(): add_argument("--single-scenario", action="store_true", help="pause after every scenario") add_argument("--single-feature", action="store_true", help="pause after every feature") add_argument("--stats-file", default="", help="statistics output file") - add_argument("--storage-mode", default="in_memory", help="Memgraph storage mode") + add_argument("--storage-mode", help="Memgraph storage mode") # Parse arguments parsed_args = argp.parse_args() + if parsed_args.storage_mode is None: + if parsed_args.test_suite == "memgraph_V1_on_disk": + parsed_args.storage_mode = "ON_DISK_TRANSACTIONAL" + else: + parsed_args.storage_mode = "IN_MEMORY_TRANSACTIONAL" + + print(f"Test suite: {parsed_args.test_suite}") + print(f"Storage mode: {parsed_args.storage_mode}") + + if parsed_args.test_suite == "memgraph_V1_on_disk" and parsed_args.storage_mode != "ON_DISK_TRANSACTIONAL": + raise Exception( + "memgraph_V1_on_disk test suite can only be run with ON_DISK_TRANSACTIONAL storage mode. For other storage modes, use memgraph_V1 test suite." + ) + + if parsed_args.test_suite == "memgraph_V1" and parsed_args.storage_mode == "ON_DISK_TRANSACTIONAL": + raise Exception( + "memgraph_V1 test suite cannot be run with ON_DISK_TRANSACTIONAL storage mode. For ON_DISK_TRANSACTIONAL storage mode, use memgraph_V1_on_disk test suite." + ) + # Find tests test_directory = os.path.join(SCRIPT_DIR, "tests", parsed_args.test_suite) diff --git a/tests/gql_behave/tests/config.yaml b/tests/gql_behave/tests/config.yaml index 4efaaaed6..a66bdb1fa 100644 --- a/tests/gql_behave/tests/config.yaml +++ b/tests/gql_behave/tests/config.yaml @@ -19,7 +19,7 @@ must_pass: false - name: memgraph_V1_on_disk - test_suite: memgraph_V1 + test_suite: memgraph_V1_on_disk storage_mode: ON_DISK_TRANSACTIONAL must_pass: true diff --git a/tests/gql_behave/tests/memgraph_V1/features/foreach.feature b/tests/gql_behave/tests/memgraph_V1/features/foreach.feature index 7d37e6b98..c5fa79c92 100644 --- a/tests/gql_behave/tests/memgraph_V1/features/foreach.feature +++ b/tests/gql_behave/tests/memgraph_V1/features/foreach.feature @@ -242,7 +242,7 @@ Feature: Foreach Given an empty graph And having executed """ - FOREACH(i in [1, 2, 3] | FOREACH(j in [2] | MERGE (n { age : i }))); + FOREACH(i in [1, 2, 3] | FOREACH(j in [1] | MERGE (n { age : i }))); """ When executing query: """ diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/aggregations.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/aggregations.feature new file mode 100644 index 000000000..8fe6a47ad --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/aggregations.feature @@ -0,0 +1,404 @@ +Feature: Aggregations + + Scenario: Count test 01: + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C) + """ + When executing query: + """ + MATCH (a) RETURN COUNT(a) AS n + """ + Then the result should be: + | n | + | 3 | + + Scenario: Count test 02: + Given an empty graph + When executing query: + """ + RETURN COUNT(123) AS n + """ + Then the result should be: + | n | + | 1 | + + Scenario: Count test 03: + Given an empty graph + When executing query: + """ + RETURN COUNT(true) AS n + """ + Then the result should be: + | n | + | 1 | + + Scenario: Count test 04: + Given an empty graph + When executing query: + """ + RETURN COUNT('abcd') AS n + """ + Then the result should be: + | n | + | 1 | + + Scenario: Count test 05: + Given an empty graph + And having executed + """ + CREATE (a{x: 0}), (b{x: 0}), (c{x: 0}), (d{x: 1}), (e{x: 1}) + """ + When executing query: + """ + MATCH (a) RETURN COUNT(a) AS n, a.x + """ + Then the result should be: + | n | a.x | + | 3 | 0 | + | 2 | 1 | + + Scenario: Count test 06: + Given an empty graph + And having executed + """ + CREATE (), (), (), (), () + """ + When executing query: + """ + MATCH (n) RETURN COUNT(*) AS n + """ + Then the result should be: + | n | + | 5 | + + Scenario: Count test 07: + Given an empty graph + When executing query: + """ + RETURN count(null) + """ + Then the result should be: + | count(null) | + | 0 | + + Scenario: Sum test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 7}), (c{x: 5}), (d{x: 'x'}) + """ + When executing query: + """ + MATCH (a) RETURN SUM(a.x) AS n + """ + Then an error should be raised + + Scenario: Sum test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b), (c{x: 5}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN SUM(a.x) AS n + """ + Then the result should be: + | n | + | 6 | + + Scenario: Sum test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0, y:3}), (b{x: 0, y:1}), (c{x: 0}), (d{x: 1, y:4}), (e{x: 1}) + """ + When executing query: + """ + MATCH (a) RETURN SUM(a.y) AS n, a.x + """ + Then the result should be: + | n | a.x | + | 4 | 0 | + | 4 | 1 | + + Scenario: Sum test 04: + Given an empty graph + When executing query: + """ + RETURN sum(null) + """ + Then the result should be: + | sum(null) | + | 0 | + + Scenario: Avg test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 7}), (c{x: 5}), (d{x: 'x'}) + """ + When executing query: + """ + MATCH (a) RETURN AVG(a.x) AS n + """ + Then an error should be raised + + Scenario: Avg test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1.25}), (b), (c{x: 4.75}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN AVG(a.x) AS n + """ + Then the result should be: + | n | + | 3.0 | + + Scenario: Avg test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0, y:3}), (b{x: 0, y:1}), (c{x: 0}), (d{x: 1, y:4}), (e{x: 1}) + """ + When executing query: + """ + MATCH (a) RETURN AVG(a.y) AS n, a.x + """ + Then the result should be: + | n | a.x | + | 2.0 | 0 | + | 4.0 | 1 | + + Scenario: Avg test 04: + Given an empty graph + When executing query: + """ + RETURN avg(null) + """ + Then the result should be: + | avg(null) | + | null | + + Scenario: Min test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 7}), (c{x: 5}), (d{x: 'x'}) + """ + When executing query: + """ + MATCH (a) RETURN MIN(a.x) AS n + """ + Then an error should be raised + + Scenario: Min test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b), (c{x: 9}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN MIN(a.x) AS n + """ + Then the result should be: + | n | + | 1 | + + Scenario: Min test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0, y:3}), (b{x: 0, y:1}), (c{x: 0}), (d{x: 1, y:4}), (e{x: 1}) + """ + When executing query: + """ + MATCH (a) RETURN MIN(a.y) AS n, a.x + """ + Then the result should be: + | n | a.x | + | 1 | 0 | + | 4 | 1 | + + Scenario: Min test 04: + Given an empty graph + When executing query: + """ + RETURN min(null) + """ + Then the result should be: + | min(null) | + | null | + + Scenario: Max test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 7}), (c{x: 5}), (d{x: 'x'}) + """ + When executing query: + """ + MATCH (a) RETURN MAX(a.x) AS n + """ + Then an error should be raised + + Scenario: Max test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b), (c{x: 9}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN MAX(a.x) AS n + """ + Then the result should be: + | n | + | 9 | + + Scenario: Max test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0, y:3}), (b{x: 0, y:1}), (c{x: 0}), (d{x: 1, y:4}), (e{x: 1}) + """ + When executing query: + """ + MATCH (a) RETURN Max(a.y) AS n, a.x + """ + Then the result should be: + | n | a.x | + | 3 | 0 | + | 4 | 1 | + + Scenario: Max test 04: + Given an empty graph + When executing query: + """ + RETURN max(null) + """ + Then the result should be: + | max(null) | + | null | + + Scenario: Collect test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 0}), (b{x: True}), (c{x: 'asdf'}) + """ + When executing query: + """ + MATCH (a) RETURN collect(a.x) AS n + """ + Then the result should be (ignoring element order for lists) + | n | + | [0, true, 'asdf'] | + + Scenario: Collect test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 0}), (b{x: True}), (c{x: 'asdf'}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN collect(a.x) AS n + """ + Then the result should be (ignoring element order for lists) + | n | + | [0, true, 'asdf'] | + + Scenario: Collect test 03: + Given an empty graph + And having executed + """ + CREATE ({k: "a", v: 3}), ({k: "b", v: 1}), ({k: "c", v: 2}) + """ + When executing query: + """ + MATCH (a) RETURN collect(a.k + "_key", a.v + 10) AS n + """ + Then the result should be + | n | + | {a_key: 13, b_key: 11, c_key: 12} | + + Scenario: Combined aggregations - some evauluates to null: + Given an empty graph + And having executed + """ + CREATE (f) + CREATE (n {property: 1}) + """ + When executing query: + """ + MATCH (n) RETURN count(n) < n.property, count(n.property), count(n), avg(n.property), min(n.property), max(n.property), sum(n.property) + """ + Then the result should be: + | count(n) < n.property | count(n.property) | count(n) | avg(n.property) | min(n.property) | max(n.property) | sum(n.property) | + | false | 1 | 1 | 1.0 | 1 | 1 | 1 | + | null | 0 | 1 | null | null | null | 0 | + + Scenario: Graph projection test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 2}), (c{x: 3}), (d{x: 4}), (a)-[:X]->(b), (b)-[:X]->(c), (c)-[:X]->(a), (a)-[:B]->(d) + """ + When executing query: + """ + MATCH p=()-[:X]->() WITH project(p) as graph WITH graph.nodes as nodes UNWIND nodes as n RETURN n.x as x ORDER BY x DESC + """ + Then the result should be: + | x | + | 3 | + | 2 | + | 1 | + + Scenario: Graph projection test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 2}), (c{x: 3}), (d{x: 4}), (a)-[:X]->(b), (b)-[:X]->(c), (c)-[:X]->(a), (a)-[:B]->(d) + """ + When executing query: + """ + MATCH p=()-[:Z]->() WITH project(p) as graph WITH graph.nodes as nodes UNWIND nodes as n RETURN n.x as x ORDER BY x DESC + """ + Then the result should be: + | x | + + Scenario: Graph projection test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 2}), (c{x: 3}), (d{x: 4}), (a)-[:X {prop:1}]->(b), (b)-[:X {prop:2}]->(c), (c)-[:X {prop:3}]->(a), (a)-[:B {prop:4}]->(d) + """ + When executing query: + """ + MATCH p=()-[:X]->() WITH project(p) as graph WITH graph.edges as edges UNWIND edges as e RETURN e.prop as y ORDER BY y DESC + """ + Then the result should be: + | y | + | 3 | + | 2 | + | 1 | + + Scenario: Graph projection test 04: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 2}), (c{x: 3}), (d{x: 4}), (a)-[:X {prop:1}]->(b), (b)-[:X {prop:2}]->(c), (c)-[:X {prop:3}]->(a), (a)-[:B {prop:4}]->(d) + """ + When executing query: + """ + MATCH p=()-[:Z]->() WITH project(p) as graph WITH graph.edges as edges UNWIND edges as e RETURN e.prop as y ORDER BY y DESC + """ + Then the result should be: + | y | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/cartesian.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/cartesian.feature new file mode 100644 index 000000000..7cc127884 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/cartesian.feature @@ -0,0 +1,173 @@ +Feature: Cartesian + + Scenario: Match multiple patterns 01 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (a)-[:X]->(b), (c)-[:X]->(a) + """ + When executing query: + """ + MATCH (a)-[]->(), (b) CREATE (a)-[r:R]->(b) RETURN a, b, r + """ + Then the result should be: + | a | b | r | + | (:C) | (:A) | [:R] | + | (:C) | (:B) | [:R] | + | (:C) | (:C) | [:R] | + | (:A) | (:A) | [:R] | + | (:A) | (:B) | [:R] | + | (:A) | (:C) | [:R] | + + Scenario: Match multiple patterns 02 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (e:E), (f:F), (a)-[:X]->(b), (b)-[:X]->(c), (d)-[:X]->(e), (e)-[:X]->(f) + """ + When executing query: + """ + MATCH (a:B)--(b), (c:E)--(d) CREATE (b)-[r:R]->(d) return b, d, r + """ + Then the result should be: + | b | d | r | + | (:A) | (:D) | [:R] | + | (:A) | (:F) | [:R] | + | (:C) | (:D) | [:R] | + | (:C) | (:F) | [:R] | + + Scenario: Match multiple patterns 03 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (a)-[:R]->(b), (b)-[:R]->(c), (c)-[:R]->(d) + """ + When executing query: + """ + MATCH (a:B)--(b), (c:B)--(d) RETURN b, d + """ + Then the result should be: + | b | d | + | (:A) | (:C) | + | (:C) | (:A) | + + Scenario: Match multiple patterns 04 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (a)-[:R]->(b), (b)-[:R]->(c), (c)-[:R]->(d) + """ + When executing query: + """ + MATCH (a:A)--(b), (c:A)--(d) RETURN a, b, c, d + """ + Then the result should be empty + + Scenario: Multiple match 01 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (a)-[:R]->(b), (b)-[:R]->(c), (c)-[:R]->(d) + """ + When executing query: + """ + MATCH (a:B)--(b) MATCH (c:B)--(d) RETURN b, d + """ + Then the result should be: + | b | d | + | (:A) | (:A) | + | (:A) | (:C) | + | (:C) | (:A) | + | (:C) | (:C) | + + Scenario: Multiple match 02 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (a)-[:R]->(b), (b)-[:R]->(c), (c)-[:R]->(d) + """ + When executing query: + """ + MATCH (a:A)--(b) MATCH (a)--(c) RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:A) | (:B) | (:B) | + + Scenario: Multiple match 03 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (a)-[:X]->(b), (c)-[:X]->(a) + """ + When executing query: + """ + MATCH (a)-[]->() MATCH (b) CREATE (a)-[r:R]->(b) RETURN a, b, r + """ + Then the result should be: + | a | b | r | + | (:C) | (:A) | [:R] | + | (:C) | (:B) | [:R] | + | (:C) | (:C) | [:R] | + | (:A) | (:A) | [:R] | + | (:A) | (:B) | [:R] | + | (:A) | (:C) | [:R] | + + Scenario: Multiple match 04 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (d:D), (e:E), (f:F), (a)-[:X]->(b), (b)-[:X]->(c), (d)-[:X]->(e), (e)-[:X]->(f) + """ + When executing query: + """ + MATCH (a:B)--(b) MATCH (c:E)--(d) CREATE (b)-[r:R]->(d) return b, d, r + """ + Then the result should be: + | b | d | r | + | (:A) | (:D) | [:R] | + | (:A) | (:F) | [:R] | + | (:C) | (:D) | [:R] | + | (:C) | (:F) | [:R] | + + Scenario: Multiple match 05 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C) + """ + When executing query: + """ + MATCH(a) MATCH(a) RETURN a + """ + Then the result should be: + | a | + | (:A) | + | (:B) | + | (:C) | + + Scenario: Multiple match 06 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (a)-[:R]->(b), (b)-[:R]->(c) + """ + When executing query: + """ + MATCH (a)-[]->() MATCH (a:B) MATCH (b:C) RETURN a, b + """ + Then the result should be: + | a | b | + | (:B) | (:C) | + + Scenario: Multiple match 07 + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B), (c:C), (a)-[:R]->(b), (b)-[:R]->(c) + """ + When executing query: + """ + MATCH (a)-[]->() MATCH (a:B) MATCH (a:C) RETURN a + """ + Then the result should be empty diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/case.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/case.feature new file mode 100644 index 000000000..0ad70d4dc --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/case.feature @@ -0,0 +1,99 @@ +Feature: Case + + Scenario: Simple CASE: + Given an empty graph + When executing query: + """ + UNWIND range(1, 3) as x RETURN CASE x WHEN 2 THEN "two" END + """ + Then the result should be: + | CASE x WHEN 2 THEN "two" END | + | null | + | 'two' | + | null | + + Scenario: Simple CASE with ELSE: + Given an empty graph + When executing query: + """ + UNWIND range(1, 3) as x RETURN CASE x WHEN 2 THEN "two" ELSE "nottwo" END as z + """ + Then the result should be: + | z | + | 'nottwo' | + | 'two' | + | 'nottwo' | + + Scenario: Generic CASE: + Given an empty graph + When executing query: + """ + UNWIND range(1, 3) as x RETURN CASE WHEN x > 1 THEN "greater" END as z + """ + Then the result should be: + | z | + | null | + | 'greater' | + | 'greater' | + + Scenario: Generic CASE multiple matched whens: + Given an empty graph + When executing query: + """ + UNWIND range(1, 3) as x RETURN CASE WHEN x > 10 THEN 10 WHEN x > 1 THEN 1 WHEN x > 0 THEN 0 WHEN x > "mirko" THEN 1000 END as z + """ + Then the result should be: + | z | + | 0 | + | 1 | + | 1 | + + Scenario: Simple CASE in collect: + Given an empty graph + When executing query: + """ + UNWIND range(1, 3) as x RETURN collect(CASE x WHEN 2 THEN "two" ELSE "nottwo" END) as z + """ + Then the result should be: + | z | + | ['nottwo', 'two', 'nottwo'] | + + Scenario: Simple CASE nullcheck does not have match: + Given an empty graph + When executing query: + """ + WITH 2 AS name RETURN CASE name WHEN 3 THEN 'something went wrong' WHEN null THEN "doesn't work" ELSE 'works' END + """ + Then the result should be: + | CASE name WHEN 3 THEN 'something went wrong' WHEN null THEN "doesn't work" ELSE 'works' END | + | 'works' | + + Scenario: Simple CASE nullcheck does have match: + Given an empty graph + When executing query: + """ + WITH 2 AS name RETURN CASE name WHEN 2 THEN 'works' WHEN null THEN "doesn't work" ELSE 'something went wrong' END + """ + Then the result should be: + | CASE name WHEN 2 THEN 'works' WHEN null THEN "doesn't work" ELSE 'something went wrong' END | + | 'works' | + + Scenario: Generic CASE nullcheck does have match: + Given an empty graph + When executing query: + """ + WITH 2 AS name RETURN CASE WHEN name is NULL THEN "doesn't work" WHEN name = 2 THEN "works" ELSE "something went wrong" END + """ + Then the result should be: + | CASE WHEN name is NULL THEN "doesn't work" WHEN name = 2 THEN "works" ELSE "something went wrong" END | + | 'works' | + + Scenario: Generic CASE expression is null: + Given an empty graph + When executing query: + """ + WITH null AS name RETURN CASE name WHEN null THEN "doesn't work" WHEN 2 THEN "doesn't work" ELSE 'works' END + """ + Then the result should be: + | CASE name WHEN null THEN "doesn't work" WHEN 2 THEN "doesn't work" ELSE 'works' END | + | 'works' | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/create.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/create.feature new file mode 100644 index 000000000..0bdb159f4 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/create.feature @@ -0,0 +1,177 @@ +Feature: Create + + Scenario: Create empty node without clearing database + When executing query: + """ + CREATE (n) + """ + Then the result should be empty + + Scenario: Create node with label without clearing database + When executing query: + """ + CREATE (n:L:K) + """ + Then the result should be empty + + Scenario: Create node with int property without clearing database + When executing query: + """ + CREATE (n{a: 1}) + """ + Then the result should be empty + + Scenario: Create node with float property without clearing database + When executing query: + """ + CREATE (n{a: 1.0}) + """ + Then the result should be empty + + Scenario: Create node with string property without clearing database + When executing query: + """ + CREATE (n{a: 'string'}) + """ + Then the result should be empty + + Scenario: Create node with bool properties without clearing database + When executing query: + """ + CREATE (n{a: True, b: false}) + """ + Then the result should be empty + + Scenario: Create node with null property without clearing database + When executing query: + """ + CREATE (n{a: NULL}) + """ + Then the result should be empty + + Scenario: Create node with properties without clearing database + When executing query: + """ + CREATE (n{a: 1.0, b: false, c: 1, d: 'neki"string"', e: NULL}) + """ + Then the result should be empty + + Scenario: Create node with properties without clearing database + When executing query: + """ + CREATE (n:L:K:T {a: 1.0, b: false, c: 1, d: 'neki"string"', e: NULL}) + """ + Then the result should be empty + + Scenario: Create multiple nodes connected by relationships + When executing query: + """ + CREATE (a)-[b:X]->(c)<-[d:Y]-(e) + """ + Then the result should be empty + + Scenario: Create multiple nodes connected by relationships + When executing query: + """ + CREATE (n)-[:X]->(n)<-[:Y]-(n) + """ + Then the result should be empty + + Scenario: Create multiple nodes connected by relationships with properties + When executing query: + """ + CREATE (a)-[b:X{a: 1.0}]->(c)<-[d:Y{d: "xyz"}]-(e) + """ + Then the result should be empty + + Scenario: Create empty node without clearing database: + When executing query: + """ + CREATE (n) RETURN n + """ + Then the result should be: + | n | + |( )| + + Scenario: Create node with labels without clearing database and return it + When executing query: + """ + CREATE (n:A:B) RETURN n + """ + Then the result should be: + | n | + |(:A:B)| + + Scenario: Create node with properties without clearing database and return it + When executing query: + """ + CREATE (n{a: 1.0, b: FALSE, c: 1, d: 'neki"string"', e: NULL}) RETURN n + """ + Then the result should be: + | n | + |({a: 1.0, b: false, c: 1, d: 'neki"string"'}) | + + Scenario: Create node with labels and properties without clearing database and return it + When executing query: + """ + CREATE (n:A:B{a: 1.0, b: False, c: 1, d: 'neki"string"', e: NULL}) RETURN n + """ + Then the result should be: + | n | + | (:A:B{a: 1.0, b: false, c: 1, d: 'neki"string"'}) | + + Scenario: Create node with properties and labels without clearing database and return its properties + When executing query: + """ + CREATE (n:A:B{a: 1.0, b: false, c: 1, d: 'neki"string"', e: NULL}) RETURN n.a, n.b, n.c, n.d, n.e + """ + Then the result should be: + | n.a | n.b | n.c | n.d | n.e | + | 1.0 | false | 1 | 'neki"string"'| null | + + Scenario: Create multiple nodes connected by relationships with properties and return it + When executing query: + """ + CREATE (a)-[b:X{a: 1.0}]->(c)<-[d:Y{d: "xyz"}]-(e) RETURN b + """ + Then the result should be: + | b | + | [:X{a: 1.0}] | + + Scenario: Multiple create 01: + When executing query: + """ + CREATE (a)-[b:X{a: 1.0}]->(c)<-[d:Y{d: "xyz"}]-(e) CREATE (n:A:B{a: 1.0, b: False, c: 1, d: 'neki"string"', e: NULL}) RETURN b, n + """ + Then the result should be: + | n | b | + | (:A:B{a: 1.0, b: false, c: 1, d: 'neki"string"'}) | [:X{a: 1.0}] | + + Scenario: Multiple create 02: + When executing query: + """ + CREATE (a)-[:X]->(b) CREATE (a)-[:Y]->(c) + """ + Then the result should be empty + + Scenario: Multiple create 03: + Given an empty graph + And having executed + """ + CREATE (a:A), (b:B) CREATE (c:C), (a)-[:R]->(b) CREATE (b)-[:R]->(c) + """ + When executing query: + """ + MATCH (a)-[]->() MATCH (a:B) MATCH (b:C) RETURN a, b + """ + Then the result should be: + | a | b | + | (:B) | (:C) | + + Scenario: Multiple create 04: + Given an empty graph + When executing query: + """ + CREATE (a:A) CREATE (a:B) + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/delete.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/delete.feature new file mode 100644 index 000000000..027e40b94 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/delete.feature @@ -0,0 +1,211 @@ +Feature: Delete + + Scenario: Delete all from database and yield number of deleted items + Given an empty graph + And having executed + """ + CREATE (n), (m), (o) + """ + When executing query: + """ + MATCH (n) DETACH DELETE n RETURN COUNT(*) AS cnt + """ + Then the result should be: + | cnt | + | 3 | + + Scenario: Delete all from database and match nothing after + Given an empty graph + And having executed + """ + CREATE (n), (m) + """ + When executing query: + """ + MATCH (n) DETACH DELETE n WITH n MATCH (m) RETURN COUNT(*) AS cnt + """ + Then the result should be: + | cnt | + | 0 | + + Scenario: Delete relationship in the pattern + Given an empty graph + And having executed + """ + CREATE (n)-[:REL]->(m), (k)-[:REL]->(z) + """ + When executing query: + """ + MATCH (n)-[r:REL]->(m) DELETE r RETURN COUNT(*) AS cnt + """ + Then the result should be: + | cnt | + | 2 | + + Scenario: Delete node and return property throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n RETURN n.prop AS prop + """ + Then an error should be raised + + Scenario: Delete node, set property throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n.prop = 2 + """ + Then the result should be empty + + Scenario: Delete node, set property and return throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n.prop = 2 RETURN n + """ + Then an error should be raised + + Scenario: Delete node, remove property throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n REMOVE n.prop + """ + Then the result should be empty + + Scenario: Delete node, remove property and return throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n REMOVE n.prop RETURN n + """ + Then an error should be raised + + Scenario: Delete node, set label throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n:Label + """ + Then the result should be empty + + Scenario: Delete node, set label and return throws an error + Given an empty graph + And having executed + """ + CREATE (n {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n:Label RETURN n + """ + Then an error should be raised + + Scenario: Delete node, remove label throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n REMOVE n:Label + """ + Then the result should be empty + + Scenario: Delete node, remove label and return throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n REMOVE n:Label RETURN n + """ + Then an error should be raised + + Scenario: Delete node, set update properties and return throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n += {prop: 2} RETURN n + """ + Then an error should be raised + + Scenario: Delete node, set properties throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n += {prop: 2} + """ + Then the result should be empty + + Scenario: Delete node, set replace properties and return throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n = {prop: 2} RETURN n + """ + Then an error should be raised + + Scenario: Delete node, set replace properties throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n = {prop: 2} + """ + Then the result should be empty + + Scenario: Delete node, set property and return it with aggregation throws an error + Given an empty graph + And having executed + """ + CREATE (n:Label {prop: 1}); + """ + When executing query: + """ + MATCH (n) DETACH DELETE n SET n.prop = 1 WITH n RETURN n + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/expressions.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/expressions.feature new file mode 100644 index 000000000..25d284f97 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/expressions.feature @@ -0,0 +1,133 @@ +Feature: Expressions + + Scenario: Test equal operator + Given an empty graph + When executing query: + """ + CREATE (a) + RETURN 1=1 and 1.0=1.0 and 'abc'='abc' and false=false and a.age is null as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test not equal operator + Given an empty graph + When executing query: + """ + CREATE (a{age: 1}) + RETURN not 1<>1 and 1.0<>1.1 and 'abcd'<>'abc' and false<>true and a.age is not null as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test greater operator + Given an empty graph + When executing query: + """ + RETURN 2>1 and not 1.0>1.1 and 'abcd'>'abc' as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test less operator + Given an empty graph + When executing query: + """ + RETURN not 2<1 and 1.0<1.1 and not 'abcd'<'abc' as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test greater equal operator + Given an empty graph + When executing query: + """ + RETURN 2>=2 and not 1.0>=1.1 and 'abcd'>='abc' as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test less equal operator + Given an empty graph + When executing query: + """ + RETURN 2<=2 and 1.0<=1.1 and not 'abcd'<='abc' as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test plus operator + Given an empty graph + When executing query: + """ + RETURN 3+2=1.09+3.91 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test minus operator + Given an empty graph + When executing query: + """ + RETURN 3-2=1.09-0.09 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test multiply operator + Given an empty graph + When executing query: + """ + RETURN 3*2=1.5*4 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test divide operator1 + Given an empty graph + When executing query: + """ + RETURN 3/2<>7.5/5 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test divide operator2 + Given an empty graph + When executing query: + """ + RETURN 3.0/2=7.5/5 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test mod operator + Given an empty graph + When executing query: + """ + RETURN 3%2=1 as n + """ + Then the result should be: + | n | + | true | + + Scenario: Test one big logical equation + Given an empty graph + When executing query: + """ + RETURN not true or true and false or not ((true xor false or true) and true or false xor true ) as n + """ + Then the result should be: + | n | + | false | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/foreach.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/foreach.feature new file mode 100644 index 000000000..c5fa79c92 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/foreach.feature @@ -0,0 +1,257 @@ +# Copyright 2022 Memgraph Ltd. +# +# Use of this software is governed by the Business Source License +# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +# License, and you may not use this file except in compliance with the Business Source License. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0, included in the file +# licenses/APL.txt. + +Feature: Foreach + Behaviour tests for memgraph FOREACH clause + + Scenario: Foreach create + Given an empty graph + And having executed + """ + FOREACH( i IN [1, 2, 3] | CREATE (n {age : i})) + """ + When executing query: + """ + MATCH (n) RETURN n.age + """ + Then the result should be: + | n.age | + | 1 | + | 2 | + | 3 | + And no side effects + + Scenario: Foreach Foreach create + Given an empty graph + And having executed + """ + FOREACH( i IN [1, 2, 3] | CREATE (n {age : i})) FOREACH( i in [4, 5, 6] | CREATE (n {age : i})) + """ + When executing query: + """ + MATCH (n) RETURN n.age + """ + Then the result should be: + | n.age | + | 1 | + | 2 | + | 3 | + | 4 | + | 5 | + | 6 | + And no side effects + + Scenario: Foreach shadowing + Given an empty graph + And having executed + """ + FOREACH( i IN [1] | FOREACH( i in [2, 3, 4] | CREATE (n {age : i}))) + """ + When executing query: + """ + MATCH (n) RETURN n.age + """ + Then the result should be: + | n.age | + | 2 | + | 3 | + | 4 | + And no side effects + + Scenario: Foreach set + Given an empty graph + And having executed + """ + CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false }) + """ + And having executed + """ + MATCH p=(n1)-[*]->(n2) + FOREACH (n IN nodes(p) | SET n.marked = true) + """ + When executing query: + """ + MATCH (n) + RETURN n.marked + """ + Then the result should be: + | n.marked | + | true | + | true | + And no side effects + + Scenario: Foreach remove + Given an empty graph + And having executed + """ + CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false }) + """ + And having executed + """ + MATCH p=(n1)-[*]->(n2) + FOREACH (n IN nodes(p) | REMOVE n.marked) + """ + When executing query: + """ + MATCH (n) + RETURN n; + """ + Then the result should be: + | n | + | () | + | () | + And no side effects + + # Scenario: Foreach delete + # Given an empty graph + # And having executed + # """ + # CREATE (n1 { marked: false })-[:RELATES]->(n2 { marked: false }) + # """ + # And having executed + # """ + # MATCH p=(n1)-[*]->(n2) + # FOREACH (n IN nodes(p) | DETACH delete n) + # """ + # When executing query: + # """ + # MATCH (n) + # RETURN n; + # """ + # Then the result should be: + # | | + # And no side effects + + Scenario: Foreach merge + Given an empty graph + And having executed + """ + FOREACH (i IN [1, 2, 3] | MERGE (n { age : i })) + """ + When executing query: + """ + MATCH (n) + RETURN n.age; + """ + Then the result should be: + | n.age | + | 1 | + | 2 | + | 3 | + And no side effects + + Scenario: Foreach nested + Given an empty graph + And having executed + """ + FOREACH (i IN [1, 2, 3] | FOREACH( j IN [1] | CREATE (k { prop : j }))) + """ + When executing query: + """ + MATCH (n) + RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + | 1 | + | 1 | + + Scenario: Foreach multiple update clauses + Given an empty graph + And having executed + """ + CREATE (n1 { marked1: false, marked2: false })-[:RELATES]->(n2 { marked1: false, marked2: false }) + """ + And having executed + """ + MATCH p=(n1)-[*]->(n2) + FOREACH (n IN nodes(p) | SET n.marked1 = true SET n.marked2 = true) + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be: + | n | + | ({marked1: true, marked2: true}) | + | ({marked1: true, marked2: true}) | + And no side effects + + Scenario: Foreach multiple nested update clauses + Given an empty graph + And having executed + """ + CREATE (n1 { marked1: false, marked2: false })-[:RELATES]->(n2 { marked1: false, marked2: false }) + """ + And having executed + """ + MATCH p=(n1)-[*]->(n2) + FOREACH (n IN nodes(p) | FOREACH (j IN [1] | SET n.marked1 = true SET n.marked2 = true)) + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be: + | n | + | ({marked1: true, marked2: true}) | + | ({marked1: true, marked2: true}) | + And no side effects + + Scenario: Foreach match foreach return + Given an empty graph + And having executed + """ + CREATE (n {prop: [[], [1,2]]}); + """ + When executing query: + """ + MATCH (n) FOREACH (i IN n.prop | CREATE (:V { i: i})) RETURN n; + """ + Then the result should be: + | n | + | ({prop: [[], [1, 2]]}) | + And no side effects + + Scenario: Foreach on null value + Given an empty graph + And having executed + """ + CREATE (n); + """ + When executing query: + """ + MATCH (n) FOREACH (i IN n.prop | CREATE (:V { i: i})); + """ + Then the result should be: + | | + And no side effects + + Scenario: Foreach nested merge + Given an empty graph + And having executed + """ + FOREACH(i in [1, 2, 3] | FOREACH(j in [1] | MERGE (n { age : i }))); + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be: + | n | + | ({age: 1}) | + | ({age: 2}) | + | ({age: 3}) | + And no side effects diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/functions.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/functions.feature new file mode 100644 index 000000000..d9348518f --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/functions.feature @@ -0,0 +1,1160 @@ +Feature: Functions + + Scenario: Sqrt test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 7}), (c{x: 5}), (d{x: 'x'}) + """ + When executing query: + """ + MATCH (a) RETURN SQRT(a.x) AS n + """ + Then an error should be raised + + Scenario: Sqrt test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b), (c{x: 9}), (d{x: null}) + """ + When executing query: + """ + MATCH (a) RETURN SQRT(a.x) AS n + """ + Then the result should be: + | n | + | 1.0 | + | null | + | 3.0 | + | null | + + Scenario: ToBoolean test 01: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 0})) + """ + When executing query: + """ + MATCH (a) RETURN TOBOOLEAN(a.x) AS n + """ + Then an error should be raised + + Scenario: ToBoolean test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 'TrUe'}), (b{x: 'not bool'}), (c{x: faLsE}), (d{x: null}), (e{x: 'fALse'}), (f{x: tRuE}) + """ + When executing query: + """ + MATCH (a) RETURN TOBOOLEAN(a.x) AS n + """ + Then the result should be: + | n | + | true | + | null | + | false | + | null | + | false | + | true | + + Scenario: ToInteger test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN TOINTEGER(a.x) AS n + """ + Then an error should be raised + + Scenario: ToInteger test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 'not int'}), (c{x: '-12'}), (d{x: null}), (e{x: '1.2'}), (f{x: '1.9'}) + """ + When executing query: + """ + MATCH (a) RETURN TOINTEGER(a.x) AS n + """ + Then the result should be: + | n | + | 1 | + | null | + | -12 | + | null | + | 1 | + | 1 | + + + Scenario: ToFloat test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN TOFLOAT(a.x) AS n + """ + Then an error should be raised + + Scenario: ToFloat test 02: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (b{x: 'not float'}), (c{x: '-12'}), (d{x: null}), (e{x: '1.2'}), (f{x: 1.9}) + """ + When executing query: + """ + MATCH (a) RETURN TOFLOAT(a.x) AS n + """ + Then the result should be: + | n | + | 1.0 | + | null | + | -12.0 | + | null | + | 1.2 | + | 1.9 | + + + Scenario: Abs test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true}) + """ + When executing query: + """ + MATCH (a) RETURN ABS(a.x) AS n + """ + Then an error should be raised + + Scenario: Abs test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'}) + """ + When executing query: + """ + MATCH (a) RETURN ABS(a.x) AS n + """ + Then an error should be raised + + Scenario: Abs test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (c{x: -12}), (d{x: null}), (e{x: -2.3}), (f{x: 1.9}) + """ + When executing query: + """ + MATCH (a) RETURN ABS(a.x) AS n + """ + Then the result should be: + | n | + | 1 | + | 12 | + | null | + | 2.3 | + | 1.9 | + + + Scenario: Exp test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN EXP(a.x) AS n + """ + Then an error should be raised + + Scenario: Exp test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'})), + """ + When executing query: + """ + MATCH (a) RETURN EXP(a.x) AS n + """ + Then an error should be raised + + Scenario: Exp test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 1}), (c{x: -12}), (d{x: null}), (e{x: -2.3}), (f{x: 1.9}) + """ + When executing query: + """ + MATCH (a) RETURN EXP(a.x) AS n + """ + Then the result should be: + | n | + | 2.718281828459045 | + | .00000614421235332821 | + | null | + | 0.10025884372280375 | + | 6.6858944422792685 | + + Scenario: Log test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN LOG(a.x) AS n + """ + Then an error should be raised + + Scenario: Log test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'})), + """ + When executing query: + """ + MATCH (a) RETURN LOG(a.x) AS n + """ + Then an error should be raised + + Scenario: Log test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0.123}), (c{x: -12}), (d{x: null}), (e{x: 27}) + """ + When executing query: + """ + MATCH (a) RETURN LOG(a.x) AS n + """ + Then the result should be: + | n | + | -2.0955709236097197 | + | nan | + | null | + | 3.295836866004329 | + + Scenario: Log10 test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN LOG10(a.x) AS n + """ + Then an error should be raised + + Scenario: Log10 test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'})), + """ + When executing query: + """ + MATCH (a) RETURN LOG10(a.x) AS n + """ + Then an error should be raised + + Scenario: Log10 test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0.123}), (c{x: -12}), (d{x: null}), (e{x: 27}) + """ + When executing query: + """ + MATCH (a) RETURN LOG10(a.x) AS n + """ + Then the result should be: + | n | + | -0.9100948885606021 | + | nan | + | null | + | 1.4313637641589874 | + + + Scenario: Sin test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN SIN(a.x) AS n + """ + Then an error should be raised + + Scenario: Sin test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'})), + """ + When executing query: + """ + MATCH (a) RETURN SIN(a.x) AS n + """ + Then an error should be raised + + Scenario: Sin test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0.123}), (c{x: -12}), (d{x: null}), (e{x: 27}) + """ + When executing query: + """ + MATCH (a) RETURN SIN(a.x) AS n + """ + Then the result should be: + | n | + | 0.12269009002431533 | + | 0.5365729180004349 | + | null | + | 0.956375928404503 | + + Scenario: Cos test 01: + Given an empty graph + And having executed + """ + CREATE (b{x: true})) + """ + When executing query: + """ + MATCH (a) RETURN COS(a.x) AS n + """ + Then an error should be raised + + Scenario: Cos test 02: + Given an empty graph + And having executed + """ + CREATE (b{x: '1.0'})), + """ + When executing query: + """ + MATCH (a) RETURN COS(a.x) AS n + """ + Then an error should be raised + + Scenario: Cos test 03: + Given an empty graph + And having executed + """ + CREATE (a{x: 0.123}), (c{x: -12}), (d{x: null}), (e{x: 27}) + """ + When executing query: + """ + MATCH (a) RETURN COS(a.x) AS n + """ + Then the result should be: + | n | + | 0.9924450321351935 | + | 0.8438539587324921 | + | null | + | -0.2921388087338362 | + + Scenario: Sign test: + When executing query: + """ + RETURN SIGN(null) AS n, SIGN(123) AS a, SIGN(0) AS b, SIGN(1.23) AS c, + SIGN(-123) AS d, SIGN(-1.23) AS e, SIGN(-0.00) AS f + """ + Then the result should be: + | n | a | b | c | d | e | f | + | null | 1 | 0 | 1 | -1 | -1 | 0 | + + Scenario: Tan test: + When executing query: + """ + RETURN TAN(null) AS n, TAN(123) AS a, TAN(-2.5) AS b, TAN(1.23) AS c + """ + Then the result should be: + | n | a | b | c | + | null | 0.5179274715856552 | 0.7470222972386603 | 2.819815734268152 | + + Scenario: Atan test: + When executing query: + """ + RETURN ATAN(null) AS n, ATAN(1.23) AS a, ATAN(123) AS b, ATAN(0) AS c + """ + Then the result should be: + | n | a | b | c | + | null | 0.8881737743776796 | 1.5626664246149526 | 0.0 | + + Scenario: Atan2 test: + When executing query: + """ + RETURN ATAN2(1, null) AS n, ATAN2(0, 0) AS a, ATAN2(2, 3) AS b, ATAN2(1.5, 2.5) AS c + """ + Then the result should be: + | n | a | b | c | + | null | 0.0 | 0.5880026035475675 | 0.5404195002705842 | + + Scenario: Asin test: + When executing query: + """ + RETURN ASIN(null) AS n, ASIN(1.23) AS a, ASIN(0.48) AS b, ASIN(-0.25) AS c + """ + Then the result should be: + | n | a | b | c | + | null | nan | 0.5006547124045881 | -0.25268025514207865 | + + Scenario: Acos test: + When executing query: + """ + RETURN ACOS(null) AS n, ACOS(1.23) AS a, ACOS(0.48) AS b, ACOS(-0.25) AS c + """ + Then the result should be: + | n | a | b | c | + | null | nan | 1.0701416143903084 | 1.8234765819369754 | + + Scenario: Round test: + When executing query: + """ + RETURN ROUND(null) AS n, ROUND(1.49999) AS a, ROUND(-1.5) AS b, ROUND(-1.51) AS c, + ROUND(1.5) as d + """ + Then the result should be: + | n | a | b | c | d | + | null | 1.0 | -2.0 | -2.0 | 2.0 | + + Scenario: Floor test: + When executing query: + """ + RETURN FLOOR(null) AS n, FLOOR(1.49999) AS a, FLOOR(-1.5) AS b, FLOOR(-1.51) AS c, + FLOOR(1.00) as d + """ + Then the result should be: + | n | a | b | c | d | + | null | 1.0 | -2.0 | -2.0 | 1.0 | + + Scenario: Ceil test: + When executing query: + """ + RETURN CEIL(null) AS n, CEIL(1.49999) AS a, CEIL(-1.5) AS b, CEIL(-1.51) AS c, + CEIL(1.00) as d + """ + Then the result should be: + | n | a | b | c | d | + | null | 2.0 | -1.0 | -1.0 | 1.0 | + + Scenario: Tail test: + When executing query: + """ + RETURN TAIL(null) AS n, TAIL([[1, 2], 3, 4]) AS a, TAIL([1, [2, 3, 4]]) AS b + """ + Then the result should be: + | n | a | b | + | null | [3, 4] | [[2, 3, 4]] | + + Scenario: Range test: + When executing query: + """ + RETURN RANGE(1, 3) AS a, RANGE(1, 5, 2) AS b, RANGE(1, -1) AS c, + RANGE(1, -1, -3) as d + """ + Then the result should be: + | a | b | c | d | + | [1, 2, 3] | [1, 3, 5] | [] | [1] | + + Scenario: Size test: + When executing query: + """ + RETURN SIZE(null) AS n, SIZE([[1, 2], 3, 4]) AS a, SIZE([1, [2, 3, 4]]) AS b + """ + Then the result should be: + | n | a | b | + | null | 3 | 2 | + + Scenario: Degree test: + When executing query: + """ + CREATE (a)-[:Type]->(b)<-[:Type]-(c) + RETURN DEGREE(a) AS da, DEGREE(b) AS db, DEGREE(null) AS dn + """ + Then the result should be: + | da | db | dn | + | 1 | 2 | null | + + + Scenario: Last test: + When executing query: + """ + RETURN LAST(null) AS n, LAST([[1, 2], 3, 4]) AS a, LAST([1, [2, 3, 4]]) AS b + """ + Then the result should be: + | n | a | b | + | null | 4 | [2, 3, 4] | + + Scenario: Head test: + When executing query: + """ + RETURN HEAD(null) AS n, HEAD([[1, 2], 3, 4]) AS a, HEAD([1, [2, 3, 4]]) AS b + """ + Then the result should be: + | n | a | b | + | null | [1, 2] | 1 | + + Scenario: Nodes test: + Given an empty graph + And having executed: + """ + CREATE (:L1)-[:E1]->(:L2)-[:E2]->(:L3) + """ + When executing query: + """ + MATCH p=()-[]->()-[]->() RETURN nodes(p) AS ns + """ + Then the result should be: + | ns | + | [(:L1), (:L2), (:L3)] | + + Scenario: Relationships test: + Given an empty graph + And having executed: + """ + CREATE (:L1)-[:E1]->(:L2)-[:E2]->(:L3) + """ + When executing query: + """ + MATCH p=()-[]->()-[]->() RETURN relationships(p) as rels + """ + Then the result should be: + | rels | + | [[:E1], [:E2]] | + + Scenario: Labels test: + Given an empty graph + And having executed: + """ + CREATE(:x:y:z), (), (:a:b) + """ + When executing query: + """ + MATCH(n) RETURN LABELS(n) AS l + """ + Then the result should be: + | l | + | [] | + | ['x', 'y', 'z'] | + | ['a', 'b'] | + + Scenario: Type test: + Given an empty graph + And having executed: + """ + CREATE(a), (b), (c), (a)-[:A]->(b), (a)-[:B]->(c), (b)-[:C]->(c) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN TYPE(r) AS t + """ + Then the result should be: + | t | + | 'A' | + | 'B' | + | 'C' | + + Scenario: Properties test1: + Given an empty graph + And having executed: + """ + CREATE(a), (b), (c), (a)-[:A{a: null}]->(b), (a)-[:B{b: true}]->(c), + (b)-[:C{c: 123}]->(c) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN PROPERTIES(r) AS p ORDER BY p.prop; + """ + Then the result should be: + | p | + | {} | + | {b: true} | + | {c: 123} | + + Scenario: Properties test2: + Given an empty graph + And having executed: + """ + CREATE({a: 'x'}), ({n: 1.1}), () + """ + When executing query: + """ + MATCH(n) RETURN PROPERTIES(n) AS p + """ + Then the result should be: + | p | + | {} | + | {a: 'x'} | + | {n: 1.1} | + + Scenario: Coalesce test: + When executing query: + """ + RETURN COALESCE(null) AS n, COALESCE([null, null]) AS a, + COALESCE(null, null, 1, 2, 3) AS b + """ + Then the result should be: + | n | a | b | + | null | [null, null] | 1 | + + Scenario: Endnode test: + Given an empty graph + And having executed: + """ + CREATE(a:x), (b:y), (c:z), (a)-[:A]->(b), (b)-[:B]->(c), (c)-[:C]->(b) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN ENDNODE(r) AS n, ENDNODE(r):z AS a, ENDNODE(r):y AS b + """ + Then the result should be: + | n | a | b | + | (:z) | true | false | + | (:y) | false | true | + | (:y) | false | true | + + Scenario: E test: + When executing query: + """ + RETURN E() as n + """ + Then the result should be: + | n | + | 2.718281828459045 | + + Scenario: Pi test: + When executing query: + """ + RETURN PI() as n + """ + Then the result should be: + | n | + | 3.141592653589793 | + + Scenario: Rand test: + When executing query: + """ + WITH rand() as r RETURN r >= 0.0 AND r < 1.0 as result + """ + Then the result should be: + | result | + | true | + + Scenario: All test 01: + When executing query: + """ + RETURN all(x IN [1, 2, '3'] WHERE x < 2) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: All test 02: + When executing query: + """ + RETURN all(x IN [1, 2, 3] WHERE x < 4) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: All test 03: + When executing query: + """ + RETURN all(x IN [1, 2, '3'] WHERE x < 3) AS a + """ + Then an error should be raised + + Scenario: All test 04: + When executing query: + """ + RETURN all(x IN [Null, Null, Null] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: All test 05: + When executing query: + """ + RETURN all(x IN [Null, Null, 0] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: All test 06: + When executing query: + """ + RETURN all(x IN [Null, Null, 0] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: All test 07: + When executing query: + """ + RETURN all(x IN [Null, Null, Null] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: All test 08: + When executing query: + """ + RETURN all(x IN ["a", "b", "c"] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Single test 01: + When executing query: + """ + RETURN single(x IN [1, 2, '3'] WHERE x < 4) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Single test 02: + When executing query: + """ + RETURN single(x IN [1, 2, 3] WHERE x = 1) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: Single test 03: + When executing query: + """ + RETURN single(x IN [1, 2, '3'] WHERE x > 2) AS a + """ + Then an error should be raised + + Scenario: Single test 04: + When executing query: + """ + RETURN single(x IN [Null, Null, Null] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Single test 05: + When executing query: + """ + RETURN single(x IN [Null, Null, 0] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: Single test 06: + When executing query: + """ + RETURN single(x IN [Null, 0, Null, 0] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Single test 07: + When executing query: + """ + RETURN single(x IN [Null, Null, 0] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Single test 08: + When executing query: + """ + RETURN single(x IN [Null, 1, Null, 0] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: Single test 09: + When executing query: + """ + RETURN single(x IN [Null, Null, Null] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Single test 10: + When executing query: + """ + RETURN single(x IN ["a", "b", "c"] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Any test 01: + When executing query: + """ + RETURN any(x IN [1, 2, 3] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: Any test 02: + When executing query: + """ + RETURN any(x IN [1, 2, 3] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Any test 03: + When executing query: + """ + RETURN any(x IN ["a", "b", "c"] WHERE x = 1) AS a + """ + Then the result should be: + | a | + | false | + + + Scenario: Any test 04: + When executing query: + """ + RETURN any(x IN [Null, Null, Null] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Any test 05: + When executing query: + """ + RETURN any(x IN [Null, Null, 0] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: Any test 06: + When executing query: + """ + RETURN any(x IN [Null, Null, 0] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Any test 07: + When executing query: + """ + RETURN any(x IN [Null, Null, Null] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: Any test 08: + When executing query: + """ + RETURN any(x IN ["a", "b", "c"] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: None test 01: + When executing query: + """ + RETURN none(x IN [1, 2, 3] WHERE x < 1) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: None test 02: + When executing query: + """ + RETURN none(x IN [1, 2, 3] WHERE x = 1) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: None test 03: + When executing query: + """ + RETURN none(x IN ["a", "b", "c"] WHERE x = 1) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: None test 04: + When executing query: + """ + RETURN none(x IN [Null, Null, Null] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: None test 05: + When executing query: + """ + RETURN none(x IN [Null, Null, 0] WHERE x > 0) AS a + """ + Then the result should be: + | a | + | true | + + Scenario: None test 06: + When executing query: + """ + RETURN none(x IN [Null, Null, 0] WHERE x = 0) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: None test 07: + When executing query: + """ + RETURN none(x IN [Null, Null, Null] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: None test 08: + When executing query: + """ + RETURN none(x IN ["a", "b", "c"] WHERE x = Null) AS a + """ + Then the result should be: + | a | + | null | + + Scenario: PredicateFunctions test 01: + When executing query: + """ + WITH [1, 2, 3] as lst + RETURN ALL(x IN lst WHERE x > 0) as all, + SINGLE(x IN lst WHERE x > 0) AS single, + ANY(x IN lst WHERE x > 0) AS any, + NONE(x IN lst WHERE x > 0) AS none + """ + Then the result should be: + | all | single | any | none | + | true | false | true | false | + + Scenario: PredicateFunctions test 02: + When executing query: + """ + WITH [1, 2, 3] as lst + RETURN ALL(x IN lst WHERE x > 1) as all, + SINGLE(x IN lst WHERE x > 1) AS single, + ANY(x IN lst WHERE x > 1) AS any, + NONE(x IN lst WHERE x > 1) AS none + """ + Then the result should be: + | all | single | any | none | + | false | false | true | false | + + Scenario: PredicateFunctions test 03: + When executing query: + """ + WITH [1, 2, 3] as lst + RETURN ALL(x IN lst WHERE x > 2) as all, + SINGLE(x IN lst WHERE x > 2) AS single, + ANY(x IN lst WHERE x > 2) AS any, + NONE(x IN lst WHERE x > 2) AS none + """ + Then the result should be: + | all | single | any | none | + | false | true | true | false | + + Scenario: PredicateFunctions test 04: + When executing query: + """ + WITH [1, 2, 3] as lst + RETURN ALL(x IN lst WHERE x > 3) as all, + SINGLE(x IN lst WHERE x > 3) AS single, + ANY(x IN lst WHERE x > 3) AS any, + NONE(x IN lst WHERE x > 3) AS none + """ + Then the result should be: + | all | single | any | none | + | false | false | false | true | + + Scenario: Reduce test 01: + When executing query: + """ + RETURN reduce(a = true, x IN [1, 2, '3'] | a AND x < 2) AS a + """ + Then the result should be: + | a | + | false | + + Scenario: Reduce test 02: + When executing query: + """ + RETURN reduce(s = 0, x IN [1, 2, 3] | s + x) AS s + """ + Then the result should be: + | s | + | 6 | + + Scenario: Reduce test 03: + When executing query: + """ + RETURN reduce(a = true, x IN [true, true, '3'] | a AND x) AS a + """ + Then an error should be raised + + Scenario: Assert test fail, no message: + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) + """ + When executing query: + """ + MATCH (n) RETURN assert(n.a = 2) AS res + """ + Then an error should be raised + + + Scenario: Assert test fail: + Given an empty graph + And having executed: + """ + CREATE ({a: 1, b: "string"}) + """ + When executing query: + """ + MATCH (n) RETURN assert(n.a = 2, n.b) AS res + """ + Then an error should be raised + + + Scenario: Assert test pass: + Given an empty graph + And having executed: + """ + CREATE ({a: 1, b: "string"}) + """ + When executing query: + """ + MATCH (n) RETURN assert(n.a = 1, n.b) AS res + """ + Then the result should be: + | res | + | true | + + Scenario: Counter test: + Given an empty graph + And having executed: + """ + CREATE (), (), () + """ + When executing query: + """ + MATCH (n) SET n.id = counter("n.id", 0) WITH n SKIP 1 + RETURN n.id, counter("other", 0) AS c2 + """ + Then the result should be: + | n.id | c2 | + | 1 | 0 | + | 2 | 1 | + + Scenario: Vertex Id test: + Given an empty graph + And having executed: + """ + CREATE (), (), () + """ + When executing query: + """ + MATCH (n) WITH n ORDER BY id(n) + WITH COLLECT(id(n)) AS node_ids + UNWIND node_ids AS node_id + RETURN node_id - node_ids[0] AS id; + """ + Then the result should be: + | id | + | 0 | + | 1 | + | 2 | + + Scenario: Edge Id test: + Given an empty graph + And having executed: + """ + CREATE (v1)-[e1:A]->(v2)-[e2:A]->(v3)-[e3:A]->(v4) + """ + When executing query: + """ + MATCH ()-[e]->() WITH e ORDER BY id(e) + WITH COLLECT(id(e)) AS edge_ids + UNWIND edge_ids AS edge_id + RETURN edge_id - edge_ids[0] AS id; + """ + Then the result should be: + | id | + | 0 | + | 1 | + | 2 | + + Scenario: Aggregate distinct does not impact other aggregates: + Given an empty graph + And having executed: + """ + CREATE (:Node_A {id:1}) + CREATE (:Node_A {id:2}) + CREATE (:Node_A {id:3}) + CREATE (:Node_B {id:1}) + CREATE (:Node_B {id:2}) + CREATE (:Node_B {id:3}) + CREATE (:Node_B {id:4}) + CREATE (:Node_B {id:4}) + """ + When executing query: + """ + MATCH (a:Node_A), (b:Node_B) + RETURN COUNT(DISTINCT a.id) AS A_COUNT, + COUNT(b.id) AS B_COUNT; + """ + Then the result should be: + | A_COUNT | B_COUNT | + | 3 | 15 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/list_operations.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/list_operations.feature new file mode 100644 index 000000000..eed738446 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/list_operations.feature @@ -0,0 +1,265 @@ +Feature: List operators + + Scenario: In test1 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN 3 IN l as x + """ + Then the result should be: + | x | + | true | + + Scenario: In test2 + When executing query: + """ + WITH [1, '2', 3, 4] AS l + RETURN 2 IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: In test4 + When executing query: + """ + WITH [1, [2, 3], 4] AS l + RETURN [3, 2] IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: In test5 + When executing query: + """ + WITH [[1, 2], 3, 4] AS l + RETURN 1 IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: In test6 + When executing query: + """ + WITH [1, [[2, 3], 4]] AS l + RETURN [[2, 3], 4] IN l as x + """ + Then the result should be: + | x | + | true | + + Scenario: In test7 + When executing query: + """ + WITH [1, [[2, 3], 4]] AS l + RETURN [1, [[2, 3], 4]] IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: Index test1 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[2] as x + """ + Then the result should be: + | x | + | 3 | + + Scenario: Index test2 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[-2] as x + """ + Then the result should be: + | x | + | 3 | + + Scenario: Index test3 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[2][0] as x + """ + Then an error should be raised + + Scenario: Index test4 + When executing query: + """ + WITH [1, 2, [3], 4] AS l + RETURN l[2][0] as x + """ + Then the result should be: + | x | + | 3 | + + Scenario: Index test5 + When executing query: + """ + WITH [[1, [2, [3]]], 4] AS l + RETURN l[0][1][1][0] as x + """ + Then the result should be: + | x | + | 3 | + + Scenario: Slice test1 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[0..2] as x + """ + Then the result should be, in order: + | x | + | [1, 2] | + + Scenario: Slice test2 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[-2..5] as x + """ + Then the result should be, in order: + | x | + | [3, 4] | + + Scenario: Slice test3 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[-2..4] as x + """ + Then the result should be, in order: + | x | + | [3, 4] | + + Scenario: Slice test4 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[-1..4] as x + """ + Then the result should be, in order: + | x | + | [4] | + + Scenario: Slice test5 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[-2..-2] as x + """ + Then the result should be, in order: + | x | + | [] | + + Scenario: Slice test6 + When executing query: + """ + WITH [1, 2, 3, 4] AS l + RETURN l[4..-2] as x + """ + Then the result should be, in order: + | x | + | [] | + + Scenario: Concatenate test1 + When executing query: + """ + WITH [1, 2, 3, 4] AS l1, [5, 6, 7] AS l2 + RETURN l1+l2 as x + """ + Then the result should be, in order: + | x | + | [1, 2, 3, 4, 5, 6, 7] | + + Scenario: Concatenate test2 + When executing query: + """ + WITH [[1, [2]]] AS l1, [[[3], 4]] AS l2 + RETURN l1+l2 as x + """ + Then the result should be, in order: + | x | + | [[1, [2]], [[3], 4]] | + + Scenario: Concatenate test3 + When executing query: + """ + WITH [1, 2, 3, 4] AS l1, NULL AS l2 + RETURN l1+l2 as x + """ + Then the result should be, in order: + | x | + | null | + + Scenario: Concatenate test4 + When executing query: + """ + WITH [] AS l1, [] AS l2 + RETURN l1+l2 as x + """ + Then the result should be, in order: + | x | + | [] | + + Scenario: Unwind test + When executing query: + """ + UNWIND [ [[1], 2], [3], 4] as l + RETURN l + """ + Then the result should be: + | l | + | [[1], 2] | + | [3] | + | 4 | + + Scenario: Unwind + InList test1 + When executing query: + """ + UNWIND [[1,2], [3,4]] as l + RETURN 2 in l as x + """ + Then the result should be: + | x | + | true | + | false | + + Scenario: Unwind + InList test2 + When executing query: + """ + WITH [[1,2], [3,4]] as list + UNWIND list as l + RETURN 2 in l as x + """ + Then the result should be: + | x | + | true | + | false | + + Scenario: Unwind + InList test3 + Given an empty graph + And having executed + """ + CREATE ({id: 1}), ({id: 2}), ({id: 3}), ({id: 4}) + """ + When executing query: + """ + WITH [1, 2, 3] as list + MATCH (n) WHERE n.id in list + WITH n + WITH n, [1, 2] as list + WHERE n.id in list + RETURN n.id as id + ORDER BY id; + """ + Then the result should be: + | id | + | 1 | + | 2 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/map.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map.feature new file mode 100644 index 000000000..2b4327ce6 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map.feature @@ -0,0 +1,167 @@ +Feature: Creating map values + + Scenario: Creating a map with multiple properties from a vertex + Given an empty graph + And having executed + """ + CREATE (:Employee {name: "Andy", surname: "Walker", age: 24, id: 1234}); + """ + When executing query: + """ + MATCH (e:Employee) RETURN {name: e.name, surname: e.surname, age: e.age} AS public_data; + """ + Then the result should be: + | public_data | + | {age: 24, name: 'Andy', surname: 'Walker'} | + + Scenario: Creating instances of a map with multiple properties from a vertex + Given an empty graph + And having executed + """ + CREATE (:Employee {name: "Andy", surname: "Walker", age: 24, id: 1234}), (:Person), (:Person); + """ + When executing query: + """ + MATCH (e:Employee), (n:Person) RETURN {name: e.name, surname: e.surname, age: e.age} AS public_data; + """ + Then the result should be: + | public_data | + | {age: 24, name: 'Andy', surname: 'Walker'} | + | {age: 24, name: 'Andy', surname: 'Walker'} | + + Scenario: Creating a map with multiple properties from each vertex + Given an empty graph + And having executed + """ + CREATE (:Cat {name: "Luigi", age: 11}), (:Dog {name: "Don", age: 10}), (:Owner {name: "Ivan"}); + """ + When executing query: + """ + MATCH (m:Cat), (n:Dog), (o:Owner) SET o += {catName: m.name, catAge: m.age, dogName: n.name, dogAge: n.age} RETURN o; + """ + Then the result should be: + | o | + | (:Owner {catAge: 11, catName: 'Luigi', dogAge: 10, dogName: 'Don', name: 'Ivan'}) | + + Scenario: Creating distinct maps with multiple properties, each from one vertex + Given an empty graph + And having executed + """ + FOREACH (i in range(1, 5) | CREATE (:Node {prop1: i, prop2: 2 * i})); + """ + When executing query: + """ + MATCH (n) RETURN {prop1: n.prop1, prop2: n.prop2} AS prop_data; + """ + Then the result should be: + | prop_data | + | {prop1: 1, prop2: 2} | + | {prop1: 2, prop2: 4} | + | {prop1: 3, prop2: 6} | + | {prop1: 4, prop2: 8} | + | {prop1: 5, prop2: 10} | + + Scenario: Creating a map with multiple properties from a vertex; one property is null + Given an empty graph + And having executed + """ + CREATE (:Employee {name: "Andy", surname: "Walker", age: 24, id: 1234}); + """ + When executing query: + """ + MATCH (e:Employee) RETURN {name: e.name, surname: e.surname, age: e.age, null_prop: e.nonexistent} AS public_data; + """ + Then the result should be: + | public_data | + | {age: 24, name: 'Andy', null_prop: null, surname: 'Walker'} | + + Scenario: Creating a map with multiple properties from an edge + Given an empty graph + And having executed + """ + CREATE (m)-[:ROUTE {km: 466, cross_border: true}]->(n); + """ + When executing query: + """ + MATCH ()-[r:ROUTE]->() RETURN {km: r.km, cross_border: r.cross_border} AS route_data; + """ + Then the result should be: + | route_data | + | {cross_border: true, km: 466} | + + Scenario: Creating instances of a map with multiple properties from an edge + Given an empty graph + And having executed + """ + CREATE (m)-[:ROUTE {km: 466, cross_border: true}]->(n), (:City), (:City); + """ + When executing query: + """ + MATCH (:City), ()-[r:ROUTE]->() RETURN {km: r.km, cross_border: r.cross_border} AS route_data; + """ + Then the result should be: + | route_data | + | {cross_border: true, km: 466} | + | {cross_border: true, km: 466} | + + Scenario: Creating a map with multiple properties from each edge + Given an empty graph + And having executed + """ + CREATE (m)-[:HIGHWAY {km: 466, cross_border: true}]->(n), (m)-[:FLIGHT {km: 350, daily: true}]->(n); + """ + When executing query: + """ + MATCH ()-[h:HIGHWAY]->(), ()-[f:FLIGHT]->() + RETURN {km_hwy: h.km, cross_border: h.cross_border, km_air: f.km, daily_flight: f.daily} AS routes_data; + """ + Then the result should be: + | routes_data | + | {cross_border: true, daily_flight: true, km_air: 350, km_hwy: 466} | + + Scenario: Creating distinct maps with multiple properties, each from one edge + Given an empty graph + And having executed + """ + MERGE (m:City) MERGE (n:Country) FOREACH (i in range(1, 5) | CREATE (m)-[:IN {prop1: i, prop2: 2 * i}]->(n)); + """ + When executing query: + """ + MATCH (m)-[r]->(n) RETURN {prop1: r.prop1, prop2: r.prop2} AS prop_data; + """ + Then the result should be: + | prop_data | + | {prop1: 1, prop2: 2} | + | {prop1: 2, prop2: 4} | + | {prop1: 3, prop2: 6} | + | {prop1: 4, prop2: 8} | + | {prop1: 5, prop2: 10} | + + Scenario: Creating a map with multiple properties from an edge; one property is null + Given an empty graph + And having executed + """ + CREATE (m)-[:ROUTE {km: 466, cross_border: true}]->(n); + """ + When executing query: + """ + MATCH ()-[r:ROUTE]->() RETURN {km: r.km, cross_border: r.cross_border, null_prop: r.nonexistent} AS route_data; + """ + Then the result should be: + | route_data | + | {cross_border: true, km: 466, null_prop: null} | + + Scenario: Creating a map with multiple properties from both a vertex and an edge + Given an empty graph + And having executed + """ + CREATE (m:City {name: "Split", highway_connected: true})-[:ROUTE {km: 466, cross_border: true}]->(n:City {name: "Ljubljana"}); + """ + When executing query: + """ + MATCH (m:City {name: "Split"})-[r:ROUTE]->() + RETURN {km: r.km, cross_border: r.cross_border, start_city: m.name, highway_connected: m.highway_connected} AS route_data; + """ + Then the result should be: + | route_data | + | {cross_border: true, highway_connected: true, km: 466, start_city: 'Split'} | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_operations.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_operations.feature new file mode 100644 index 000000000..3a39e57b7 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_operations.feature @@ -0,0 +1,45 @@ +Feature: Map operators + + Scenario: Returning a map + When executing query: + """ + RETURN {a: 1, b: 'bla', c: [1, 2], d: {a: 42}} as result + """ + Then the result should be: + | result | + | {a: 1, b: 'bla', c: [1, 2], d: {a: 42}} | + + + Scenario: Storing a map property + Given an empty graph + And having executed + """ + CREATE ({prop: {x: 1, y: true, z: "bla"}}) + """ + When executing query: + """ + MATCH (n) RETURN n.prop + """ + Then the result should be: + | n.prop | + | {x: 1, y: true, z: 'bla'} | + + + Scenario: A property lookup on a map literal + When executing query: + """ + WITH {a: 1, b: 'bla', c: {d: 42}} AS x RETURN x.a, x.c.d + """ + Then the result should be: + | x.a | x.c.d | + | 1 | 42 | + + + Scenario: Map indexing + When executing query: + """ + WITH {a: 1, b: 'bla', c: {d: 42}} AS x RETURN x["a"] as xa, x["c"]["d"] as xcd, x["z"] as xz + """ + Then the result should be: + | xa | xcd | xz | + | 1 | 42 | null | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_projection.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_projection.feature new file mode 100644 index 000000000..da20f2856 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/map_projection.feature @@ -0,0 +1,87 @@ +Feature: Map projection + + Scenario: Returning an empty map projection + When executing query: + """ + WITH {} AS map + RETURN map {} AS result + """ + Then the result should be: + | result | + | {} | + + Scenario: Returning a map projection with each type of map projection element + Given an empty graph + And having executed + """ + CREATE (n:Actor {name: "Morgan", lastName: "Freeman"}) + """ + When executing query: + """ + WITH 85 as age + MATCH (actor:Actor) + RETURN actor {.*, .name, age, oscars: 1} AS result + """ + Then the result should be: + | result | + | {age: 85, lastName: 'Freeman', name: 'Morgan', oscars: 1} | + + Scenario: Projecting from a null value + When executing query: + """ + WITH "value" AS var + OPTIONAL MATCH (n:Nonexistent) + RETURN n {.*} AS result0, n {.prop} AS result1, n {prop: "value"} AS result2, n {var} AS result3; + """ + Then the result should be: + | result0 | result1 | result2 | result3 | + | null | null | null | null | + + Scenario: Projecting a nonexistent property + When executing query: + """ + WITH {name: "Morgan", lastName: "Freeman"} as actor + RETURN actor.age; + """ + Then the result should be: + | actor.age | + | null | + + Scenario: Storing a map projection as a property + Given an empty graph + And having executed + """ + WITH {name: "Morgan", lastName: "Freeman"} as person + WITH person {.*, wonOscars: true} as actor + CREATE (n:Movie {lead: actor}); + """ + When executing query: + """ + MATCH (movie:Movie) + RETURN movie.lead + """ + Then the result should be: + | movie.lead | + | {lastName: 'Freeman', name: 'Morgan', wonOscars: true} | + + Scenario: Looking up the properties of a map projection + When executing query: + """ + WITH {name: "Morgan", lastName: "Freeman"} as actor, {oscars: 1} as awards + WITH actor {.*, awards: awards} AS actor + RETURN actor.name, actor.awards.oscars; + """ + Then the result should be: + | actor.name | actor.awards.oscars | + | 'Morgan' | 1 | + + Scenario: Indexing a map projection + When executing query: + """ + WITH {name: "Morgan", lastName: "Freeman"} as actor, {oscars: 1} as awards + WITH actor {.*, awards: awards} AS actor + RETURN actor["name"], actor["awards"]["oscars"] + """ + Then the result should be: + | actor["name"] | actor["awards"]["oscars"] | + | 'Morgan' | 1 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/match.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/match.feature new file mode 100644 index 000000000..830e1b9f5 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/match.feature @@ -0,0 +1,687 @@ +Feature: Match + + Scenario: Create node and self relationships and match + Given an empty graph + And having executed: + """ + CREATE (n)-[:X]->(n)<-[:Y]-(n) + """ + When executing query: + """ + MATCH ()-[a]-()-[b]-() RETURN a, b + """ + Then the result should be: + | a | b | + | [:X] | [:Y] | + | [:Y] | [:X] | + + Scenario: Create node and self relationship and match + Given an empty graph + And having executed: + """ + CREATE (n)-[:X]->(n) + """ + When executing query: + """ + MATCH ()-[a]-() RETURN a + """ + Then the result should be: + | a | + | [:X] | + + Scenario: Create node and self relationships and match + Given an empty graph + And having executed: + """ + CREATE (n)-[:X]->(n)<-[:Y]-(n) + """ + When executing query: + """ + MATCH ()<-[a]-()-[b]->() RETURN a, b + """ + Then the result should be: + | a | b | + | [:X] | [:Y] | + | [:Y] | [:X] | + + Scenario: Create multiple nodes and relationships and match + Given an empty graph + And having executed: + """ + CREATE ()-[:X]->()<-[:Y]-() + """ + When executing query: + """ + MATCH ()-[a]-()-[b]-() RETURN a, b + """ + Then the result should be: + | a | b | + | [:X] | [:Y] | + | [:Y] | [:X] | + + Scenario: Create multiple nodes and relationships and match + Given an empty graph + And having executed: + """ + CREATE ()-[:X]->()<-[:Y]-() + """ + When executing query: + """ + MATCH ()<-[a]-()-[b]-() RETURN a, b + """ + Then the result should be empty + + Scenario: Create cycle and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->()-[:Y]->()-[:Z]->(a) + """ + When executing query: + """ + MATCH ()-[a]->()-[b]->() RETURN a, b + """ + Then the result should be: + | a | b | + | [:X] | [:Y] | + | [:Y] | [:Z] | + | [:Z] | [:X] | + + Scenario: Create cycle and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->()-[:Y]->()-[:Z]->(a) + """ + When executing query: + """ + MATCH ()-[a]-()-[b]-() RETURN a, b + """ + Then the result should be: + | a | b | + | [:X] | [:Y] | + | [:Y] | [:Z] | + | [:Z] | [:X] | + | [:X] | [:Z] | + | [:Y] | [:X] | + | [:Z] | [:Y] | + + Scenario: Create cycle and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->()-[:Y]->()-[:Z]->(a) + """ + When executing query: + """ + MATCH ()<-[a]-()-[b]->() RETURN a, b + """ + Then the result should be empty + + Scenario: Create cycle and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->()-[:Y]->()-[:Z]->(a) + """ + When executing query: + """ + MATCH ()-[a]->()-[]->()-[]->()-[]->() RETURN a + """ + Then the result should be empty + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->(b)-[:Y]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a]->()-[b]->()-[c]->() RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | [:X] | [:Y] | [:Z] | + | [:Z] | [:Y] | [:X] | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X]->(b)-[:Y]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a]-()-[b]-()-[c]-() RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | [:X] | [:Y] | [:Z] | + | [:X] | [:Z] | [:Y] | + | [:Y] | [:X] | [:Z] | + | [:Y] | [:Z] | [:X] | + | [:Z] | [:Y] | [:X] | + | [:Z] | [:X] | [:Y] | + | [:X] | [:Y] | [:Z] | + | [:X] | [:Z] | [:Y] | + | [:Y] | [:X] | [:Z] | + | [:Y] | [:Z] | [:X] | + | [:Z] | [:Y] | [:X] | + | [:Z] | [:X] | [:Y] | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X{a: 1.0}]->(b)-[:Y]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a{a: 1.0}]-()-[b]-()-[c]-() RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | [:X{a: 1.0}] | [:Y] | [:Z] | + | [:X{a: 1.0}] | [:Z] | [:Y] | + | [:X{a: 1.0}] | [:Y] | [:Z] | + | [:X{a: 1.0}] | [:Z] | [:Y] | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X{a: 1.0}]->(b)-[:Y]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a{a: 1.0}]-()-[b]-()-[c:Y]-() RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | [:X{a: 1.0}] | [:Z] | [:Y] | + | [:X{a: 1.0}] | [:Z] | [:Y] | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X{a: 1.0}]->(b)-[:Y{a: 1.0}]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a{a: 1.0}]-()-[b]-()-[c:Y]-() RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | [:X{a: 1.0}] | [:Z] | [:Y{a: 1.0}] | + | [:X{a: 1.0}] | [:Z] | [:Y{a: 1.0}] | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a)-[:X{a: 1.0}]->(b)-[:Y{a: 1.0}]->(a)-[:Z]->(b) + """ + When executing query: + """ + MATCH ()-[a{a: 1.0}]-()-[b]-()-[c{a: 1.0}]-() RETURN a, b, c, c.a as t + """ + Then the result should be: + | a | b | c | t | + | [:X{a: 1.0}] | [:Z] | [:Y{a: 1.0}] | 1.0 | + | [:X{a: 1.0}] | [:Z] | [:Y{a: 1.0}] | 1.0 | + | [:Y{a: 1.0}] | [:Z] | [:X{a: 1.0}] | 1.0 | + | [:Y{a: 1.0}] | [:Z] | [:X{a: 1.0}] | 1.0 | + + Scenario: Create two nodes with three relationships and match + Given an empty graph + And having executed: + """ + CREATE (a:T{c: True})-[:X{x: 2.5}]->(:A:B)-[:Y]->()-[:Z{r: 1}]->(a) + """ + When executing query: + """ + MATCH (:T{c: True})-[a:X{x: 2.5}]->(node:A:B)-[:Y]->()-[:Z{r: 1}]->() RETURN a AS node, node AS a + """ + Then the result should be: + | node | a | + | [:X{x: 2.5}] | (:A:B) | + + Scenario: Create and match with label + Given graph "graph_01" + When executing query: + """ + MATCH (n:Person) RETURN n + """ + Then the result should be: + | n | + | (:Person {age: 20}) | + | (:Person :Student {age: 20}) | + | (:Person {age: 21}) | + + Scenario: Create and match with label + Given graph "graph_01" + When executing query: + """ + MATCH (n:Student) RETURN n + """ + Then the result should be: + | n | + | (:Person :Student {age: 20}) | + | (:Student {age: 21}) | + + Scenario: Create, match with label and property + Given graph "graph_01" + When executing query: + """ + MATCH (n:Person {age: 20}) RETURN n AS x + """ + Then the result should be: + | x | + | (:Person {age: 20}) | + | (:Person :Student {age: 20}) | + + Scenario: Create, match with label and filter property using WHERE + Given graph "graph_01" + When executing query: + """ + MATCH (n:Person) WHERE n.age = 20 RETURN n + """ + Then the result should be: + | n | + | (:Person {age: 20}) | + | (:Person :Student {age: 20}) | + + Scenario: Create and match with property + Given graph "graph_01" + When executing query: + """ + MATCH (n {age: 20}) RETURN n + """ + Then the result should be: + | n | + | (:Person {age: 20}) | + | (:Person :Student {age: 20}) | + + Scenario: Create and match pattern with cross referencing variables in property maps + Given an empty graph + And having executed: + """ + CREATE ({x: 1, y: 5, z: 3})-[:E]->({x: 10, y: 1, z: 3}) + """ + When executing query: + """ + MATCH (n {x: m.y, z: m.z})-[]-(m {y: n.x, z: n.z}) RETURN n, m + """ + Then the result should be: + | n | m | + | ({x: 1, y: 5, z: 3}) | ({x: 10, y: 1, z: 3}) | + + Scenario: Test match with order by + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a + """ + Then the result should be, in order: + | n.a | + | 1 | + | 2 | + | 3 | + | 4 | + | 5 | + + Scenario: Test match with order by and skip + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a SKIP 3 + """ + Then the result should be, in order: + | n.a | + | 4 | + | 5 | + + Scenario: Test match with order by and limit + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a LIMIT 2 + """ + Then the result should be, in order: + | n.a | + | 1 | + | 2 | + + Scenario: Test match with order by, skip and limit + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a SKIP 2 LIMIT 2 + """ + Then the result should be, in order: + | n.a | + | 3 | + | 4 | + + Scenario: Test match with order by and skip + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a SKIP 6 + """ + Then the result should be empty + + Scenario: Test match with order by and limit + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 2}), ({a: 3}), ({a: 4}), ({a: 5}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a LIMIT 0 + """ + Then the result should be empty + + Scenario: Test match with order by and date + Given an empty graph + And having executed: + """ + CREATE({a: DATE('2021-12-31')}), ({a: DATE('2021-11-11')}), ({a: DATE('2021-12-28')}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a + """ + Then the result should be, in order: + | n.a | + | 2021-11-11 | + | 2021-12-28 | + | 2021-12-31 | + + Scenario: Test match with order by and localtime + Given an empty graph + And having executed: + """ + CREATE({a: LOCALTIME('09:12:31')}), ({a: LOCALTIME('09:09:20')}), ({a: LOCALTIME('09:11:21')}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a + """ + Then the result should be, in order: + | n.a | + | 09:09:20.000000000 | + | 09:11:21.000000000 | + | 09:12:31.000000000 | + + Scenario: Test match with order by and localdatetime + Given an empty graph + And having executed: + """ + CREATE({a: LOCALDATETIME('2021-11-22T09:12:31')}), ({a: LOCALDATETIME('2021-11-23T09:10:30')}), ({a: LOCALDATETIME('2021-11-10T09:14:21')}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a + """ + Then the result should be, in order: + | n.a | + | 2021-11-10T09:14:21.000000000 | + | 2021-11-22T09:12:31.000000000 | + | 2021-11-23T09:10:30.000000000 | + + Scenario: Test match with order by and duration + Given an empty graph + And having executed: + """ + CREATE({a: DURATION('P12DT3M')}), ({a: DURATION('P11DT8M')}), ({a: DURATION('P11DT60H')}) + """ + When executing query: + """ + MATCH (n) RETURN n.a ORDER BY n.a + """ + Then the result should be, in order: + | n.a | + | P11DT8M | + | P12DT3M | + | P13DT12H | + + Scenario: Test distinct + Given an empty graph + And having executed: + """ + CREATE({a: 1}), ({a: 4}), ({a: 3}), ({a: 1}), ({a: 4}) + """ + When executing query: + """ + MATCH (n) RETURN DISTINCT n.a + """ + Then the result should be: + | n.a | + | 1 | + | 3 | + | 4 | + + Scenario: Test match unbounded variable path + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) -[:r]-> ({a:2}) -[:r]-> ({a:3}) + """ + When executing query: + """ + MATCH (n) -[r*]-> (m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | 1 | 2 | + | 1 | 3 | + | 2 | 3 | + + Scenario: Test match 0 length variable path + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) -[:r]-> ({a:2}) -[:r]-> ({a:3}) + """ + When executing query: + """ + MATCH (n) -[r*0]-> (m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | 1 | 1 | + | 2 | 2 | + | 3 | 3 | + + Scenario: Test match bounded variable path + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) -[:r]-> ({a:2}) -[:r]-> ({a:3}) + """ + When executing query: + """ + MATCH (n) -[r*0..1]-> (m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | 1 | 1 | + | 1 | 2 | + | 2 | 2 | + | 2 | 3 | + | 3 | 3 | + + Scenario: Test match filtered edge type variable path + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) -[:r1]-> ({a:2}) -[:r2]-> ({a:3}) + """ + When executing query: + """ + MATCH (n) -[:r1*]-> (m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | 1 | 2 | + + Scenario: Test match filtered properties variable path + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) -[:r {p1: 1, p2: 2}]-> ({a:2}) -[:r {p1: 1, p2: 3}]-> ({a:3}) + """ + When executing query: + """ + MATCH (n) -[*{p1: 1, p2:2}]-> (m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | 1 | 2 | + + Scenario: Named path with length function. + Given an empty graph + And having executed: + """ + CREATE (:starting)-[:type]->() + """ + When executing query: + """ + MATCH path = (:starting) -[*0..1]-> () RETURN size(path) + """ + Then the result should be: + | size(path) | + | 0 | + | 1 | + + Scenario: Variable expand to existing symbol 1 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 3})-[:KNOWS*2]->(pers) RETURN path; + """ + Then the result should be empty + + Scenario: Variable expand to existing symbol 2 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 3})-[:KNOWS*]->(pers) RETURN path + """ + Then the result should be: + | path | + | <(:Person{id:3})-[:KNOWS]->(:Person{id:4})-[:KNOWS]->(:Person{id:1})-[:KNOWS]->(:Person{id:2})-[:KNOWS]->(:Person{id:3})> | + + Scenario: Variable expand to existing symbol 3 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 3})-[:KNOWS*0..]->(pers) RETURN path + """ + Then the result should be: + | path | + | <(:Person{id:3})> | + | <(:Person{id:3})-[:KNOWS]->(:Person{id:4})-[:KNOWS]->(:Person{id:1})-[:KNOWS]->(:Person{id:2})-[:KNOWS]->(:Person{id:3})> | + + Scenario: Variable expand to existing symbol 4 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 3})-[:KNOWS*2..6]->(pers) RETURN path + """ + Then the result should be: + | path | + | <(:Person{id:3})-[:KNOWS]->(:Person{id:4})-[:KNOWS]->(:Person{id:1})-[:KNOWS]->(:Person{id:2})-[:KNOWS]->(:Person{id:3})> | + + Scenario: Variable expand to existing symbol 5 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(:Person {id: 2})-[:KNOWS]->(:Person {id: 3})-[:KNOWS]->(:Person {id: 4})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 3})-[:KNOWS*5..]->(pers) RETURN path + """ + Then the result should be empty + + Scenario: Variable expand to existing symbol 6 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 1})-[:KNOWS*]->(pers) RETURN path + """ + Then the result should be + | path | + | <(:Person{id:1})-[:KNOWS]->(:Person{id:1})> | + + Scenario: Variable expand to existing symbol 7 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 1})-[:KNOWS*0..]->(pers) RETURN path + """ + Then the result should be + | path | + | <(:Person{id:1})> | + | <(:Person{id:1})-[:KNOWS]->(:Person{id:1})> | + + Scenario: Variable expand to existing symbol 8 + Given an empty graph + And having executed: + """ + CREATE (p1:Person {id: 1})-[:KNOWS]->(p1); + """ + When executing query: + """ + MATCH path = (pers:Person {id: 1})-[:KNOWS*2..]->(pers) RETURN path + """ + Then the result should be empty diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph.feature new file mode 100644 index 000000000..8a13a5a74 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph.feature @@ -0,0 +1,78 @@ +Feature: Memgraph only tests (queries in which we choose to be incompatible with neo4j) + + Scenario: Multiple sets (undefined behaviour) + Given an empty graph + And having executed + """ + CREATE (n{x: 3})-[:X]->(m{x: 5}) + """ + When executing query: + """ + MATCH (n)--(m) SET n.x = n.x + 1 SET m.x = m.x + 2 SET m.x = n.x RETURN n.x + """ + # TODO: Figure out if we can define a test with multiple possible outputs in cucumber, + # until then this test just documents behaviour instead of testing it. + # Then the result should be: + # | n.x | | n.x | + # | 5 | or | 7 | + # | 5 | | 7 | + + Scenario: Multiple comparisons + Given an empty graph + When executing query: + """ + RETURN 1 < 10 > 5 < 7 > 6 < 8 AS x + """ + Then the result should be: + | x | + | true | + + Scenario: Use deleted node + Given an empty graph + When executing query: + """ + CREATE(a:A), (b:B), (c:C), (a)-[:T]->(b) WITH a DETACH DELETE a WITH a MATCH(a)-[r:T]->() RETURN r + """ + Then an error should be raised + + Scenario: In test3 + When executing query: + """ + WITH [[1], 2, 3, 4] AS l + RETURN 1 IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: In test8 + When executing query: + """ + WITH [[[[1]]], 2, 3, 4] AS l + RETURN 1 IN l as x + """ + Then the result should be: + | x | + | false | + + Scenario: Keyword as symbolic name + Given an empty graph + And having executed + """ + CREATE(a:DELete) + """ + When executing query: + """ + MATCH (n) RETURN n + """ + Then the result should be: + | n | + | (:DELete) | + + Scenario: Aggregation in CASE: + Given an empty graph + When executing query: + """ + MATCH (n) RETURN CASE count(n) WHEN 10 THEN 10 END + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_allshortest.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_allshortest.feature new file mode 100644 index 000000000..73fb9e75b --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_allshortest.feature @@ -0,0 +1,205 @@ +Feature: All Shortest Path + + Scenario: Test match allShortest upper bound + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 1}]->({a:'2'}), (n)-[:r {w: 1}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 1 (e, n | e.w ) w]->(m) RETURN m.a + """ + Then the result should be: + | m.a | + | '1' | + | '3' | + + Scenario: Test match allShortest upper bound 2 + Given an empty graph + And having executed: + """ + CREATE (a {a:'0'})-[:r {w: 2}]->(b {a:'1'})-[:r {w: 3}]->(c {a:'2'}), + (a)-[:re {w: 2}]->(b), + (b)-[:re {w:3}]->(c), + ({a: '4'})<-[:r {w: 1}]-(a), + ({a: '5'})<-[:r {w: 1}]-(a), + (c)-[:r {w: 1}]->({a: '6'}), + (c)-[:r {w: 1}]->({a: '7'}) + """ + When executing query: + """ + MATCH path=(n {a:'0'})-[r *allShortest ..2 (e, n | 1 ) w]->(m {a:'2'}) RETURN COUNT(path) AS c + """ + Then the result should be: + | c | + | 4 | + + Scenario: Test match allShortest filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 1}]->({a:'2'}), (n)-[:r {w: 1}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 1 (e, n | e.w ) w (e, n | n.a = '3')]->(m) RETURN m.a + """ + Then the result should be: + | m.a | + | '3' | + + Scenario: Test match allShortest resulting edge list + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 4}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then the result should be: + | m.a | s | w | + | '1' | 1 | 1 | + | '2' | 2 | 3 | + | '3' | 1 | 4 | + + Scenario: Test match allShortest single edge type filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r0 {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le:r0 *allShortest 10 (e, n | e.w) w]->(m) + RETURN size(le) AS s, m.a + """ + Then the result should be: + | s | m.a | + | 1 | '1' | + + Scenario: Test match allShortest multiple edge types filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r0 {w: 1}]->({a:'1'})-[:r1 {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le :r0|:r1 *allShortest 10 (e, n | e.w) w]->(m) WHERE size(le) > 1 + RETURN size(le) AS s, (le[0]).w AS r0, (le[1]).w AS r1 + """ + Then the result should be: + | s | r0 | r1 | + | 2 | 1 | 2 | + + Scenario: Test match allShortest property filters + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *allShortest 10 {w:1} (e, n | e.w ) total_weight]->(m) + RETURN size(le) AS s, (le[0]).w AS r0 + """ + Then the result should be: + | s | r0 | + | 1 | 1 | + + Scenario: Test match allShortest weight not a number + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 'not a number'}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *allShortest 10 (e, n | e.w ) total_weight]->(m) + RETURN le, total_weight + """ + Then an error should be raised + + Scenario: Test match allShortest negative weight + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: -1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *allShortest 10 (e, n | e.w ) total_weight]->(m) + RETURN le, total_weight + """ + Then an error should be raised + + Scenario: Test match allShortest weight duration + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: DURATION('PT1S')}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then the result should be: + | m.a | s | w | + | '1' | 1 | PT1S | + | '2' | 2 | PT3S | + | '3' | 1 | PT4S | + + Scenario: Test match allShortest weight negative duration + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: DURATION({seconds: -1})}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then an error should be raised + + Scenario: Test match allShortest weight mixed numeric and duration as weights + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 2}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *allShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then an error should be raised + + Scenario: Test allShortest return both paths of same length + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 2}]->({a:'1'})-[:r {w: 3}]->({a:'2'}), (n)-[:r {w: 5}]->({a:'2'}) + """ + When executing query: + """ + MATCH path=(n {a:'0'})-[r *allShortest (e, n | e.w ) w]->(m {a:'2'}) RETURN COUNT(path); + """ + Then the result should be: + | COUNT(path) | + | 2 | + + Scenario: Test allShortest on different edge between two nodes + Given an empty graph + And having executed: + """ + CREATE (n:One), (o:Two), (m:Three), (n)-[:TYPE {cost: 0.3}]->(o), (o)-[:TYPE {cost: 40}]->(m), (o)-[:TYPE {cost: 20}]->(m) + """ + When executing query: + """ + MATCH p=(h:One)-[r*allshortest ..5 (e, v | e.cost) total_cost]->(k:Three) return total_cost; + """ + Then the result should be: + | total_cost | + | 20.3 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_bfs.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_bfs.feature new file mode 100644 index 000000000..5634efd7f --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_bfs.feature @@ -0,0 +1,105 @@ +Feature: Bfs + + Scenario: Test match BFS upper bound + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r]->({a:'1.1'})-[:r]->({a:'2.1'}), (n)-[:r]->({a:'1.2'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[*bfs..1]->(m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | '0' | '1.1' | + | '0' | '1.2' | + + Scenario: Test match BFS lower bound + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r]->({a:'1.1'})-[:r]->({a:'2.1'})-[:r]->({a:'3.1'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[*bfs 2..]->(m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | '0' | '2.1' | + | '0' | '3.1' | + + Scenario: Test match BFS filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r]->({a:'1.1'})-[:r]->({a:'2.1'}), (n)-[:r]->({a:'1.2'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[*bfs..10 (e, m| m.a = '1.1' OR m.a = '0')]->(m) RETURN n.a, m.a + """ + Then the result should be: + | n.a | m.a | + | '0' | '1.1' | + + Scenario: Test match BFS resulting edge list + Given an empty graph + And having executed: + """ + CREATE (:Starting)-[:r {id: 0}]->()-[:r {id: 1}]->()-[:r {id: 2}]->()-[:r {id: 3}]->() + """ + When executing query: + """ + MATCH (:Starting)-[r *bfs..10]->(m) WHERE size(r) > 3 + RETURN size(r) AS s, (r[0]).id AS r0, (r[2]).id AS r2 + """ + Then the result should be: + | s | r0 | r2 | + | 4 | 0 | 2 | + + Scenario: Test match BFS single edge type filtered + Given an empty graph + And having executed: + """ + CREATE ()-[:r0 {id: 0}]->()-[:r1 {id: 1}]->()-[:r2 {id: 2}]->()-[:r3 {id: 3}]->() + """ + When executing query: + """ + MATCH ()-[r:r0 *bfs..10]->(m) + RETURN size(r) AS s, (r[0]).id AS r0 + """ + Then the result should be: + | s | r0 | + | 1 | 0 | + + Scenario: Test match BFS multiple edge types filtered + Given an empty graph + And having executed: + """ + CREATE ()-[:r0 {id: 0}]->()-[:r1 {id: 1}]->()-[:r2 {id: 2}]->()-[:r3 {id: 3}]->() + """ + When executing query: + """ + MATCH ()-[r :r0|:r1 *bfs..10 ]->(m) WHERE size(r) > 1 + RETURN size(r) AS s, (r[0]).id AS r0, (r[1]).id AS r1 + """ + Then the result should be: + | s | r0 | r1 | + | 2 | 0 | 1 | + + Scenario: Test match BFS property filters + Given an empty graph + And having executed: + """ + CREATE ()-[:r0 {id: 0}]->()-[:r1 {id: 1}]->()-[:r2 {id: 2}]->()-[:r3 {id: 3}]->() + """ + When executing query: + """ + MATCH ()-[r *bfs..10 {id: 1}]->(m) + RETURN size(r) AS s, (r[0]).id AS r0 + """ + Then the result should be: + | s | r0 | + | 1 | 1 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_exists.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_exists.feature new file mode 100644 index 000000000..aef6940ce --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_exists.feature @@ -0,0 +1,541 @@ +Feature: WHERE exists + + Scenario: Test exists with empty edge and node specifiers + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with empty edge and node specifiers return 2 entries + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two), (:One {prop: 3})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) RETURN n.prop ORDER BY n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + | 3 | + + Scenario: Test exists with edge specifier + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE]-()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with wrong edge specifier + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE2]-()) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with correct edge direction + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE]->()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with wrong edge direction + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)<-[:TYPE]-()) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with destination node label + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]->(:Two)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with wrong destination node label + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]->(:Three)) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with destination node property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]->({prop: 2})) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with wrong destination node property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]->({prop: 3})) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with edge property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 1}]->()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with wrong edge property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 2}]->()) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with both edge property and node label property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 1}]->(:Two {prop: 2})) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists with correct edge property and wrong node label property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 1}]->(:Two {prop: 3})) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with wrong edge property and correct node label property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 2}]->(:Two {prop:2})) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with wrong edge property and wrong node label property + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE {prop: 2}]->(:Two {prop:3})) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists AND exists + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE]->()) AND exists((n)-[]->(:Two)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists OR exists first condition + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE]->()) OR exists((n)-[]->(:Three)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists OR exists second condition + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE2]->()) OR exists((n)-[]->(:Two)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists OR exists fail + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE2]->()) OR exists((n)-[]->(:Three)) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test NOT exists + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2}) + """ + When executing query: + """ + MATCH (n:One) WHERE NOT exists((n)-[:TYPE2]->()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test multi-hop first in sequence + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists((n)-[]->()-[]->()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test multi-hop in middle sequence + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists(()-[]->(n)-[]->()) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 2 | + + Scenario: Test multi-hop at the end of the sequence + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists(()-[]->()-[]->(n)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 3 | + + Scenario: Test multi-hop not exists + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists(()-[]->(n)<-[]-()) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test multi-hop with filters + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists(({prop: 1})-[:TYPE]->(n)-[{prop:2}]->(:Three)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 2 | + + Scenario: Test multi-hop with wrong filters + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists(({prop: 1})-[:TYPE]->(n)-[:TYPE2]->(:Three)) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test node-only hop + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n) WHERE exists((n)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + | 2 | + | 3 | + + Scenario: Test exists with different edge type + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[:TYPE2]->()) RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists with correct edge type multiple edges + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two {prop: 10}), (:One {prop: 2})-[:TYPE]->(:Two {prop: 11}); + """ + When executing query: + """ + MATCH (n:Two) WHERE exists((n)<-[:TYPE]-()) RETURN n.prop ORDER BY n.prop; + """ + Then the result should be: + | n.prop | + | 10 | + | 11 | + + Scenario: Test exists does not work in WITH clauses + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:Two) WITH n WHERE exists((n)<-[:TYPE]-()) RETURN n.prop; + """ + Then an error should be raised + + Scenario: Test exists is not null + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) is not null RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists is null + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) is null RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists equal to true + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) = true RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists equal to true + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) = false RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists in list + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) in [true] RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test BFS hop + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE {prop: 1}]->(:Two {prop: 2})-[:TYPE {prop:2}]->(:Three {prop: 3}) + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[*bfs]->(:Three)) RETURN n.prop; + """ + Then the result should be: + | n.prop | + | 1 | + + Scenario: Test exists not in list + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:One) WHERE exists((n)-[]-()) in [false] RETURN n.prop; + """ + Then the result should be empty + + Scenario: Test exists on multihop patterns without results + Given an empty graph + And having executed: + """ + MATCH (n) DETACH DELETE n; + """ + When executing query: + """ + MATCH ()-[]-(m)-[]->(a) WHERE m.prop=1 and a.prop=3 and exists(()-[]->(m)) RETURN m, a; + """ + Then the result should be empty + + Scenario: Test exists does not work in SetProperty clauses + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n:Two) SET n.prop = exists((n)<-[:TYPE]-()) RETURN n.prop; + """ + Then an error should be raised + + Scenario: Test exists does not work in RETURN clauses + Given an empty graph + And having executed: + """ + CREATE (:One {prop:1})-[:TYPE]->(:Two); + """ + When executing query: + """ + MATCH (n) RETURN exists((n)-[]-()); + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_wshortest.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_wshortest.feature new file mode 100644 index 000000000..1c98c2830 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/memgraph_wshortest.feature @@ -0,0 +1,157 @@ +Feature: Weighted Shortest Path + + Scenario: Test match wShortest upper bound + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 1}]->({a:'2'}), (n)-[:r {w: 1}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 1 (e, n | e.w ) w]->(m) RETURN m.a + """ + Then the result should be: + | m.a | + | '1' | + | '3' | + + Scenario: Test match wShortest filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 1}]->({a:'2'}), (n)-[:r {w: 1}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 1 (e, n | e.w ) w (e, n | n.a = '3')]->(m) RETURN m.a + """ + Then the result should be: + | m.a | + | '3' | + + Scenario: Test match wShortest resulting edge list + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 4}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then the result should be: + | m.a | s | w | + | '1' | 1 | 1 | + | '2' | 2 | 3 | + | '3' | 1 | 4 | + + Scenario: Test match wShortest single edge type filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r0 {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le:r0 *wShortest 10 (e, n | e.w) w]->(m) + RETURN size(le) AS s, m.a + """ + Then the result should be: + | s | m.a | + | 1 | '1' | + + Scenario: Test match wShortest multiple edge types filtered + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r0 {w: 1}]->({a:'1'})-[:r1 {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le :r0|:r1 *wShortest 10 (e, n | e.w) w]->(m) WHERE size(le) > 1 + RETURN size(le) AS s, (le[0]).w AS r0, (le[1]).w AS r1 + """ + Then the result should be: + | s | r0 | r1 | + | 2 | 1 | 2 | + + Scenario: Test match wShortest property filters + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *wShortest 10 {w:1} (e, n | e.w ) total_weight]->(m) + RETURN size(le) AS s, (le[0]).w AS r0 + """ + Then the result should be: + | s | r0 | + | 1 | 1 | + + Scenario: Test match wShortest weight not a number + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 'not a number'}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *wShortest 10 (e, n | e.w ) total_weight]->(m) + RETURN le, total_weight + """ + Then an error should be raised + + Scenario: Test match wShortest negative weight + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: -1}]->({a:'1'})-[:r {w: 2}]->({a:'2'}), (n)-[:r {w: 3}]->({a:'4'}) + """ + When executing query: + """ + MATCH ()-[le *wShortest 10 (e, n | e.w ) total_weight]->(m) + RETURN le, total_weight + """ + Then an error should be raised + + Scenario: Test match wShortest weight duration + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: DURATION('PT1S')}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then the result should be: + | m.a | s | w | + | '1' | 1 | PT1S | + | '2' | 2 | PT3S | + | '3' | 1 | PT4S | + + Scenario: Test match wShortest weight negative duration + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: DURATION({seconds: -1})}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then an error should be raised + + Scenario: Test match wShortest weight mixed numeric and duration as weights + Given an empty graph + And having executed: + """ + CREATE (n {a:'0'})-[:r {w: 2}]->({a:'1'})-[:r {w: DURATION('PT2S')}]->({a:'2'}), (n)-[:r {w: DURATION('PT4S')}]->({a:'3'}) + """ + When executing query: + """ + MATCH (n {a:'0'})-[le *wShortest 10 (e, n | e.w ) w]->(m) RETURN m.a, size(le) as s, w + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/merge.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/merge.feature new file mode 100644 index 000000000..448f31d18 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/merge.feature @@ -0,0 +1,419 @@ +Feature: Merge feature + + Scenario: Merge node test01 + Given an empty graph + And having executed: + """ + CREATE (:X{a: 1}) + """ + And having executed: + """ + MERGE(n:X) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | (:X{a: 1}) | + + Scenario: Merge node test02 + Given an empty graph + And having executed: + """ + CREATE (:X) + """ + And having executed: + """ + MERGE(n:X:Y) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | (:X) | + | (:X:Y) | + + Scenario: Merge node test03 + Given an empty graph + And having executed: + """ + CREATE (:Y{a: 1}) + """ + And having executed: + """ + MERGE(n:X) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | (:X) | + | (:Y{a: 1}) | + + Scenario: Merge node test04 + Given an empty graph + And having executed: + """ + CREATE ({a: 1, b: 2}) + """ + And having executed: + """ + MERGE(n{a: 1, b: 2}) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | ({a: 1, b: 2}) | + + Scenario: Merge node test05 + Given an empty graph + And having executed: + """ + CREATE ({a: 2}) + """ + And having executed: + """ + MERGE(n{a: 1, b:2}) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | ({a: 2}) | + | ({a: 1, b: 2}) | + + Scenario: Merge node test06 + Given an empty graph + And having executed: + """ + CREATE ({a: 2}) + """ + And having executed: + """ + MERGE(n{a: 2, b: 2}) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | ({a: 2}) | + | ({a: 2, b: 2}) | + + Scenario: Merge node test07 + Given an empty graph + And having executed: + """ + CREATE ({a: 2}) + """ + And having executed: + """ + MERGE(n:A{a: 2}) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | (:A{a: 2}) | + | ({a: 2}) | + + Scenario: Merge node test08 + Given an empty graph + And having executed: + """ + CREATE (:A:B{a: 2, b: 1}) + """ + And having executed: + """ + MERGE(n:A{a: 2}) + """ + When executing query: + """ + MATCH(n) RETURN n + """ + Then the result should be: + | n | + | (:A:B{a: 2, b: 1}) | + + Scenario: Merge node test09 + Given an empty graph + And having executed: + """ + CREATE (:A{a: 2}), (:A{a: 2}), (:A{a: 1}) + """ + And having executed: + """ + MATCH(n:A) + MERGE(m:B{x: n.a}) + """ + When executing query: + """ + MATCH(n:B) RETURN * + """ + Then the result should be: + | n | + | (:B{x: 1}) | + | (:B{x: 2}) | + + Scenario: Merge relationship test01 + Given an empty graph + And having executed: + """ + CREATE (a), (b), (a)-[:X]->(b) + """ + And having executed: + """ + MATCH (a)--(b) + MERGE ((a)-[r:X]-(b)) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN * + """ + Then the result should be: + | r | + | [:X] | + + Scenario: Merge relationship test02 + Given an empty graph + And having executed: + """ + CREATE (a), (b), (a)-[:X]->(b) + """ + And having executed: + """ + MATCH (a)--(b) + MERGE ((a)-[r:Y]-(b)) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN * + """ + Then the result should be: + | r | + | [:X] | + | [:Y] | + + Scenario: Merge relationship test03 + Given an empty graph + And having executed: + """ + CREATE (a), (b), (a)-[:X{a: 1}]->(b) + """ + And having executed: + """ + MATCH (a)--(b) + MERGE ((a)-[r:X{a: 1}]-(b)) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN * + """ + Then the result should be: + | r | + | [:X{a: 1}] | + + Scenario: Merge relationship test04 + Given an empty graph + And having executed: + """ + CREATE (a), (b), (a)-[:X{a: 1}]->(b) + """ + And having executed: + """ + MATCH (a)--(b) + MERGE ((a)-[r:X{a: 2}]-(b)) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN * + """ + Then the result should be: + | r | + | [:X{a: 1}] | + | [:X{a: 2}] | + + Scenario: Merge relationship test05 + Given an empty graph + And having executed: + """ + CREATE (a), (b), (a)-[:X{a: 1}]->(b) + """ + And having executed: + """ + MATCH (a)--(b) + MERGE ((a)-[r:Y{a: 1}]-(b)) + """ + When executing query: + """ + MATCH ()-[r]->() RETURN * + """ + Then the result should be: + | r | + | [:X{a: 1}] | + | [:Y{a: 1}] | + + Scenario: Merge relationship test06 + Given an empty graph + And having executed: + """ + CREATE (a:A{a: 1}), (b:B{b: 1}), (c:C), (a)-[:X]->(c), (c)<-[:Y]-(b) + """ + And having executed: + """ + MERGE (:A)-[:X]->(:C)<-[:Y]-(:B) + """ + When executing query: + """ + MATCH (a)-[r]->(b) RETURN * + """ + Then the result should be: + | a | r | b | + | (:A{a: 1}) | [:X] | (:C) | + | (:B{b: 1}) | [:Y] | (:C) | + + Scenario: Merge relationship test07 + Given an empty graph + And having executed: + """ + CREATE (a:A{a: 1}), (b:B{b: 1}), (c:C), (a)-[:X{x: 1}]->(c), (c)<-[:Y{y: 1}]-(b) + """ + And having executed: + """ + MERGE (:A)-[:X]->(:D)<-[:Y]-(:B) + """ + When executing query: + """ + MATCH (a)-[r]->(b) RETURN * + """ + Then the result should be: + | a | r | b | + | (:A) | [:X] | (:D) | + | (:B) | [:Y] | (:D) | + | (:A{a: 1}) | [:X{x: 1}] | (:C) | + | (:B{b: 1}) | [:Y{y: 1}] | (:C) | + + Scenario: Merge relationship test08 + Given an empty graph + And having executed: + """ + CREATE (a:A:X), (b:B:Y) + """ + And having executed: + """ + MATCH (a:A), (b:B) + MERGE (a)-[:R]-(b) + """ + When executing query: + """ + MATCH (a)-[r]-(b) RETURN * + """ + Then the result should be: + | a | r | b | + | (:A:X) | [:R] | (:B:Y) | + | (:B:Y) | [:R] | (:A:X) | + + Scenario: Merge relationship test09 + Given an empty graph + And having executed: + """ + CREATE (a:A{a: 1}), (b:B{a: 2}), (c:C{a: 2}) + """ + When executing query: + """ + MATCH (n) + MERGE (m:X{a: n.a}) + MERGE (n)-[r:R]->(m) + RETURN * + """ + Then the result should be: + | n | r | m | + | (:A{a: 1}) | [:R] | (:X{a: 1}) | + | (:B{a: 2}) | [:R] | (:X{a: 2}) | + | (:C{a: 2}) | [:R] | (:X{a: 2}) | + + Scenario: Merge relationship test10 + Given an empty graph + And having executed: + """ + CREATE (a:A{a: 1}), (b:B{a: 2}), (c:C{a: 2}) + """ + When executing query: + """ + MATCH (n) + MERGE (n)-[r:R]->(m:X{a: n.a}) + RETURN * + """ + Then the result should be: + | n | r | m | + | (:A{a: 1}) | [:R] | (:X{a: 1}) | + | (:B{a: 2}) | [:R] | (:X{a: 2}) | + | (:C{a: 2}) | [:R] | (:X{a: 2}) | + + Scenario: Merge OnCreate test01 + Given an empty graph + When executing query: + """ + MERGE (a:X) + ON CREATE SET a.a = 1 + RETURN * + """ + Then the result should be: + | a | + | (:X{a: 1}) | + + Scenario: Merge OnMatch test01 + Given an empty graph + And having executed: + """ + CREATE(:A), (:A), (:B) + """ + And having executed: + """ + MERGE (a:A) + ON MATCH SET a.a = 1 + """ + When executing query: + """ + MATCH (n) RETURN * + """ + Then the result should be: + | n | + | (:A{a: 1}) | + | (:A{a: 1}) | + | (:B) | + + Scenario: Merge with Unwind test01 + Given an empty graph + And having executed: + """ + CREATE ({a: 1}) + """ + And having executed: + """ + UNWIND [1, 2, 3] AS a + MERGE({a: a}) + """ + When executing query: + """ + MATCH (n) RETURN * + """ + Then the result should be: + | n | + | ({a: 1}) | + | ({a: 2}) | + | ({a: 3)) | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/parameters.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/parameters.feature new file mode 100644 index 000000000..908507e43 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/parameters.feature @@ -0,0 +1,68 @@ +Feature: Parameters + + Scenario: Simple parameter names: + Given an empty graph + And parameters are: + | y | 2 | + | x | 1 | + When executing query: + """ + RETURN $x, $y, 5 + """ + Then the result should be: + | $x | $y | 5 | + | 1 | 2 | 5 | + + Scenario: Integers as parameter names: + Given an empty graph + And parameters are: + | 0 | 5 | + | 2 | 6 | + When executing query: + """ + RETURN $0, $2 + """ + Then the result should be: + | $0 | $2 | + | 5 | 6 | + + Scenario: Escaped symbolic names as parameter names: + Given an empty graph + And parameters are: + | a b | 2 | + | a `b | 3 | + When executing query: + """ + RETURN $`a b`, $`a ``b` + """ + Then the result should be: + | $`a b` | $`a ``b` | + | 2 | 3 | + + Scenario: Lists as parameters: + Given an empty graph + And parameters are: + | a | [1, 2, 3] | + When executing query: + """ + RETURN $a + """ + Then the result should be: + | $a | + | [1, 2, 3] | + + Scenario: Parameters in match: + Given an empty graph + And having executed: + """ + CREATE (a {x : 10}) + """ + And parameters are: + | a | 10 | + When executing query: + """ + MATCH (a {x : $a}) RETURN a.x + """ + Then the result should be: + | a.x | + | 10 | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/string_operators.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/string_operators.feature new file mode 100644 index 000000000..fba0e49ad --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/string_operators.feature @@ -0,0 +1,185 @@ +Feature: String operators + + Scenario: StartsWith test1 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name STARTS WITH 'aim' + return n.name + """ + Then the result should be: + | n.name | + | 'aime' | + + Scenario: StartsWith test2 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name STARTS WITH "ai'M" + return n.name + """ + Then the result should be: + | n.name | + | 'ai'M'e' | + + Scenario: StartsWith test3 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name STARTS WITH 1 + return n.name + """ + Then an error should be raised + + Scenario: StartsWith test5 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name STARTS WITH true + return n.name + """ + Then an error should be raised + + + Scenario: EndsWith test1 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'E"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name ENDS WITH 'e' + return n.name + """ + Then the result should be: + | n.name | + | 'AiMe' | + | 'aime' | + + Scenario: EndsWith test2 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name ENDS WITH "M'e" + return n.name + """ + Then the result should be: + | n.name | + | 'ai'M'e' | + + Scenario: EndsWith test3 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name ENDS WITH 1 + return n.name + """ + Then an error should be raised + + Scenario: EndsWith test5 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name ENDS WITH true + return n.name + """ + Then an error should be raised + + + Scenario: Contains test1 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name CONTAINS 'iM' + return n.name + """ + Then the result should be: + | n.name | + | 'AiMe' | + + Scenario: Contains test2 + Given an empty graph + And having executed + """ + CREATE(a{name: "ai'M'e"}), (b{name: "AiMe"}), (c{name: "aime"}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name CONTAINS "i'M" + return n.name + """ + Then the result should be: + | n.name | + | 'ai'M'e' | + + Scenario: Contains test3 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name CONTAINS 1 + return n.name + """ + Then an error should be raised + + + Scenario: Contains test5 + Given an empty graph + And having executed + """ + CREATE(a{name: 1}), (b{name: 2}), (c{name: null}) + """ + When executing query: + """ + MATCH (n) + WHERE n.name CONTAINS true + return n.name + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/subqueries.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/subqueries.feature new file mode 100644 index 000000000..17b3b22e3 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/subqueries.feature @@ -0,0 +1,408 @@ +# Copyright 2023 Memgraph Ltd. +# +# Use of this software is governed by the Business Source License +# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +# License, and you may not use this file except in compliance with the Business Source License. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0, included in the file +# licenses/APL.txt. + +Feature: Subqueries + Behaviour tests for memgraph CALL clause which contains a subquery + + Scenario: Subquery without bounded symbols + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + MATCH (n:Label1)-[:TYPE]->(m:Label2) + RETURN m + } + RETURN m.prop; + """ + Then the result should be: + | m.prop | + | 2 | + + Scenario: Subquery without bounded symbols and without match + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + CALL { + MATCH (n:Label1)-[:TYPE]->(m:Label2) + RETURN m + } + RETURN m.prop; + """ + Then the result should be: + | m.prop | + | 2 | + + Scenario: Subquery returning primitive + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + CALL { + MATCH (n:Label1)-[:TYPE]->(m:Label2) + RETURN m.prop AS prop + } + RETURN prop; + """ + Then the result should be: + | prop | + | 2 | + + Scenario: Subquery returning 2 values + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + MATCH (m:Label1)-[:TYPE]->(o:Label2) + RETURN m, o + } + RETURN m.prop, o.prop; + """ + Then the result should be: + | m.prop | o.prop | + | 1 | 2 | + + Scenario: Subquery returning nothing because match did not find any results + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label3) + CALL { + MATCH (m:Label1)-[:TYPE]->(:Label2) + RETURN m + } + RETURN m.prop; + """ + Then the result should be empty + + Scenario: Subquery returning a multiple of results since we join elements from basic query and the subquery + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + MATCH (m) + RETURN m + } + RETURN n.prop, m.prop + ORDER BY n.prop, m.prop; + """ + Then the result should be: + | n.prop | m.prop | + | 1 | 1 | + | 1 | 2 | + + Scenario: Subquery returning a cartesian product + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n) + CALL { + MATCH (m) + RETURN m + } + RETURN n.prop, m.prop + ORDER BY n.prop, m.prop; + """ + Then the result should be: + | n.prop | m.prop | + | 1 | 1 | + | 1 | 2 | + | 2 | 1 | + | 2 | 2 | + + Scenario: Subquery with bounded symbols + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + WITH n + MATCH (n)-[:TYPE]->(m:Label2) + RETURN m + } + RETURN m.prop; + """ + Then the result should be: + | m.prop | + | 2 | + + Scenario: Subquery with invalid bounded symbols + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + WITH o + MATCH (o)-[:TYPE]->(m:Label2) + RETURN m + } + RETURN m.prop; + """ + Then an error should be raised + + Scenario: Subquery returning primitive but not aliased + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + WITH n + MATCH (n)-[:TYPE]->(m:Label2) + RETURN m.prop + } + RETURN n; + """ + Then an error should be raised + + Scenario: Subquery returning one primitive and others aliased + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1) + CALL { + WITH n + MATCH (o)-[:TYPE]->(m:Label2) + RETURN m.prop, o + } + RETURN n; + """ + Then an error should be raised + + Scenario: Subquery returning already declared variable in outer scope + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n:Label1), (m:Label2) + CALL { + WITH n + MATCH (n:Label1)-[:TYPE]->(m:Label2) + RETURN m + } + RETURN n; + """ + Then an error should be raised + + Scenario: Subquery after subquery + Given an empty graph + And having executed + """ + CREATE (:Label1 {prop: 1})-[:TYPE]->(:Label2 {prop: 2}) + """ + When executing query: + """ + MATCH (n) + CALL { + MATCH (m) + RETURN m + } + CALL { + MATCH (o) + RETURN o + } + RETURN n.prop, m.prop, o.prop + ORDER BY n.prop, m.prop, o.prop; + """ + Then the result should be: + | n.prop | m.prop | o.prop | + | 1 | 1 | 1 | + | 1 | 1 | 2 | + | 1 | 2 | 1 | + | 1 | 2 | 2 | + | 2 | 1 | 1 | + | 2 | 1 | 2 | + | 2 | 2 | 1 | + | 2 | 2 | 2 | + + Scenario: Subquery with union + Given an empty graph + And having executed + """ + CREATE (:Person {figure: "grandpa"})<-[:CHILD_OF]-(:Person {figure: "dad"})-[:PARENT_OF]->(:Person {figure: "child"}) + """ + When executing query: + """ + MATCH (p:Person {figure: "dad"}) + CALL { + WITH p + OPTIONAL MATCH (p)-[:CHILD_OF]->(other:Person) + RETURN other + UNION + WITH p + OPTIONAL MATCH (p)-[:PARENT_OF]->(other:Person) + RETURN other + } RETURN DISTINCT p.figure, count(other) as cnt; + """ + Then the result should be: + | p.figure | cnt | + | 'dad' | 2 | + + Scenario: Subquery cloning nodes + Given an empty graph + And having executed + """ + CREATE (:Person {name: "Alen"}), (:Person {name: "Bruce"}) + """ + When executing query: + """ + MATCH (p:Person) + CALL { + WITH p + UNWIND range (1, 3) AS i + CREATE (n:Person {name: p.name}) + RETURN n + } + RETURN n; + """ + Then the result should be: + | n | + | (:Person {name: 'Alen'}) | + | (:Person {name: 'Alen'}) | + | (:Person {name: 'Alen'}) | + | (:Person {name: 'Bruce'}) | + | (:Person {name: 'Bruce'}) | + | (:Person {name: 'Bruce'}) | + + Scenario: Subquery in subquery + Given an empty graph + And having executed + """ + CREATE (:Label {id: 1}), (:Label {id: 2}) + """ + When executing query: + """ + MATCH (p:Label) + CALL { + MATCH (r:Label) + CALL { + MATCH (s:Label) + RETURN s + } + RETURN r, s + } + RETURN p.id, r.id, s.id; + """ + Then the result should be: + | p.id | r.id | s.id | + | 1 | 1 | 1 | + | 1 | 1 | 2 | + | 1 | 2 | 1 | + | 1 | 2 | 2 | + | 2 | 1 | 1 | + | 2 | 1 | 2 | + | 2 | 2 | 1 | + | 2 | 2 | 2 | + + Scenario: Counter inside subquery + Given an empty graph + And having executed + """ + CREATE (:Counter {count: 0}) + """ + When executing query: + """ + UNWIND [0, 1, 2] AS x + CALL { + MATCH (n:Counter) + SET n.count = n.count + 1 + RETURN n.count AS innerCount + } + WITH innerCount + MATCH (n:Counter) + RETURN innerCount, n.count AS totalCount + """ + Then the result should be: + | innerCount | totalCount | + | 1 | 3 | + | 2 | 3 | + | 3 | 3 | + + Scenario: Advance command on multiple subqueries + Given an empty graph + When executing query: + """ + CALL { + CREATE (create_node:Movie {title: "Forrest Gump"}) + } + CALL { + MATCH (n) RETURN n + } + RETURN n.title AS title; + """ + Then the result should be: + | title | + | 'Forrest Gump' | + + Scenario: Advance command on multiple subqueries with manual accumulate + Given an empty graph + When executing query: + """ + CALL { + CREATE (create_node:Movie {title: "Forrest Gump"}) + RETURN create_node + } + WITH create_node + CALL { + MATCH (n) RETURN n + } + RETURN n.title AS title; + """ + Then the result should be: + | title | + | 'Forrest Gump' | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/union.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/union.feature new file mode 100644 index 000000000..d2449d9f9 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/union.feature @@ -0,0 +1,135 @@ +Feature: Union + + Scenario: Test return * + Given an empty graph + When executing query: + """ + WITH 5 AS X, 3 AS Y RETURN * UNION WITH 9 AS X, 4 AS Y RETURN *; + """ + Then the result should be: + | X | Y | + | 5 | 3 | + | 9 | 4 | + + Scenario: Test return * with swapped parameters + Given an empty graph + When executing query: + """ + WITH 5 AS X, 3 AS Y RETURN * UNION WITH 9 AS Y, 4 AS X RETURN *; + """ + Then the result should be: + | X | Y | + | 5 | 3 | + | 4 | 9 | + + Scenario: Test return * and named parameters + Given an empty graph + When executing query: + """ + WITH 5 AS X, 3 AS Y RETURN * UNION WITH 9 AS Y, 4 AS X RETURN Y, X; + """ + Then the result should be: + | X | Y | + | 5 | 3 | + | 4 | 9 | + + Scenario: Test distinct single elements are returned + Given an empty graph + And having executed + """ + CREATE (a: A{x: 1, y: 1}), (b: A{x: 1, y: 2}), (c: A{x: 2, y: 1}), (d: A{x: 1, y:1}) + """ + When executing query: + """ + MATCH (a: A{x:1}) RETURN a.x AS X UNION MATCH (a: A{y:1}) RETURN a.x AS X; + """ + Then the result should be: + | X | + | 1 | + | 2 | + + Scenario: Test all single elements are returned + Given an empty graph + And having executed + """ + CREATE (a: A{x: 1, y: 1}), (b: A{x: 1, y: 2}), (c: A{x: 2, y: 1}), (d: A{x: 1, y:1}) + """ + When executing query: + """ + MATCH (a: A{x:1}) RETURN a.x AS X UNION ALL MATCH (a: A{y:1}) RETURN a.x AS X; + """ + Then the result should be: + | X | + | 1 | + | 1 | + | 1 | + | 1 | + | 2 | + | 1 | + + Scenario: Test distinct elements are returned + Given an empty graph + And having executed + """ + CREATE (a: A{x: 1, y: 1}), (b: A{x: 1, y: 2}), (c: A{x: 2, y: 1}), (d: A{x: 1, y:1}) + """ + When executing query: + """ + MATCH (a: A{x:1}) RETURN a.x AS X, a.y AS Y UNION MATCH (a: A{y:1}) RETURN a.x AS X, a.y AS Y; + """ + Then the result should be: + | X | Y | + | 1 | 1 | + | 1 | 2 | + | 2 | 1 | + + Scenario: Test all elements are returned + Given an empty graph + And having executed + """ + CREATE (a: A{x: 1, y: 1}), (b: A{x: 1, y: 2}), (c: A{x: 2, y: 1}), (d: A{x: 1, y:1}) + """ + When executing query: + """ + MATCH (a: A{x:1}) RETURN a.x AS X, a.y AS Y UNION ALL MATCH (a: A{y:1}) RETURN a.x AS X, a.y AS Y; + """ + Then the result should be: + | X | Y | + | 1 | 1 | + | 1 | 2 | + | 1 | 1 | + | 1 | 1 | + | 2 | 1 | + | 1 | 1 | + + Scenario: Test union combinator 1 + Given an empty graph + When executing query: + """ + MATCH (a) RETURN a.x as V UNION ALL MATCH (a) RETURN a.y AS V UNION MATCH (a) RETURN a.y + a.x AS V; + """ + Then an error should be raised + + Scenario: Test union combinator 2 + Given an empty graph + When executing query: + """ + MATCH (a) RETURN a.x as V UNION MATCH (a) RETURN a.y AS V UNION ALL MATCH (a) RETURN a.y + a.x AS V; + """ + Then an error should be raised + + Scenario: Different names of return expressions + Given an empty graph + When executing query: + """ + RETURN 10 as x UNION RETURN 11 as y + """ + Then an error should be raised + + Scenario: Different number of return expressions + Given an empty graph + When executing query: + """ + RETURN 10 as x, 11 as y UNION RETURN 11 as y + """ + Then an error should be raised diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/update_clauses.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/update_clauses.feature new file mode 100644 index 000000000..ddd204cdd --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/update_clauses.feature @@ -0,0 +1,292 @@ +Feature: Update clauses + + Scenario: Match create return test + Given an empty graph + And having executed + """ + CREATE (:x_1), (:z2_), (:qw34) + """ + When executing query: + """ + MATCH (a:x_1), (b:z2_), (c:qw34) + CREATE (a)-[x:X]->(b) CREATE (b)<-[y:Y]-(c) + RETURN x, y + """ + Then the result should be: + | x | y | + | [:X] | [:Y] | + + Scenario: Multiple matches in one query + Given an empty graph + And having executed + """ + CREATE (:x{age: 5}), (:y{age: 4}), (:z), (:x), (:y) + """ + When executing query: + """ + MATCH (a:x), (b:y), (c:z) + WHERE a.age=5 + MATCH (b{age: 4}) + RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:x{age: 5}) | (:y{age: 4}) | (:z) | + + Scenario: Match set one property return test + Given an empty graph + And having executed + """ + CREATE (:q)-[:X]->() + """ + When executing query: + """ + MATCH (a:q)-[b]-(c) + SET a.name='Sinisa' + RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:q{name: 'Sinisa'}) | [:X] | () | + + Scenario: Match set properties from node to node return test + Given an empty graph + And having executed + """ + CREATE (:q{name: 'Sinisa', x: 'y'})-[:X]->({name: 'V', o: 'Traktor'}) + """ + When executing query: + """ + MATCH (a:q)-[b]-(c) + SET c=a + RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:q{name: 'Sinisa', x: 'y'}) | [:X] | ({name: 'Sinisa', x: 'y'}) | + + Scenario: Match set properties from node to relationship return test + Given an empty graph + And having executed + """ + CREATE (:q{x: 'y'})-[:X]->({y: 't'}) + """ + When executing query: + """ + MATCH (a:q)-[b]-(c) + SET b=a + RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:q{x: 'y'}) | [:X{x: 'y'}] | ({y: 't'}) | + + Scenario: Match set properties from relationship to node return test + Given an empty graph + And having executed + """ + CREATE (:q)-[:X{x: 'y'}]->({y: 't'}) + """ + When executing query: + """ + MATCH (a:q)-[b]-(c) + SET a=b + RETURN a, b, c + """ + Then the result should be: + | a | b | c | + | (:q{x: 'y'}) | [:X{x: 'y'}] | ({y: 't'}) | + + Scenario: Match node set properties without return + Given an empty graph + And having executed + """ + CREATE (n1:Node {test: 1}) + CREATE (n2:Node {test: 2}) + CREATE (n3:Node {test: 3}) + """ + When executing query: + """ + MATCH (n:Node) + SET n.test = 4 + """ + Then the result should be empty + + + Scenario: Match, set properties from relationship to relationship, return test + Given an empty graph + When executing query: + """ + CREATE ()-[b:X{x: 'y'}]->()-[a:Y]->() + SET a=b + RETURN a, b + """ + Then the result should be: + | a | b | + | [:Y{x: 'y'}] | [:X{x: 'y'}] | + + Scenario: Create, set adding properties, return test + Given an empty graph + When executing query: + """ + CREATE ()-[b:X{x: 'y', y: 'z'}]->()-[a:Y{x: 'z'}]->() + SET a += b + RETURN a, b + """ + Then the result should be: + | a | b | + | [:Y{x: 'y', y: 'z'}] | [:X{x: 'y', y: 'z'}] | + + Scenario: Create node and add labels using set, return test + Given an empty graph + When executing query: + """ + CREATE (a) + SET a :sinisa:vu + RETURN a + """ + Then the result should be: + | a | + | (:sinisa:vu) | + + Scenario: Create node and delete it + Given an empty graph + And having executed: + """ + CREATE (n) + DELETE (n) + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be empty + + Scenario: Create node with relationships and delete it, check for relationships + Given an empty graph + And having executed: + """ + CREATE (n)-[:X]->() + CREATE (n)-[:Y]->() + DETACH DELETE (n) + """ + When executing query: + """ + MATCH ()-[n]->() + RETURN n + """ + Then the result should be empty + + Scenario: Create node with relationships and delete it, check for nodes + Given an empty graph + And having executed: + """ + CREATE (n:l{a: 1})-[:X]->() + CREATE (n)-[:Y]->() + DETACH DELETE (n) + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be: + | n | + |( )| + |( )| + + Scenario: Create node with relationships and delete it (without parentheses), check for nodes + Given an empty graph + And having executed: + """ + CREATE (n:l{a: 1})-[:X]->() + CREATE (n)-[:Y]->() + DETACH DELETE n + """ + When executing query: + """ + MATCH (n) + RETURN n + """ + Then the result should be: + | n | + |( )| + |( )| + + Scenario: Set test: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (a)-[:T]->(b), (b)-[:T]->(c), (c)-[:T]->(a) + """ + And having executed: + """ + MATCH (d)--(e) WHERE abs(d.x - e.x)<=1 SET d.x=d.x+2, e.x=e.x+2 + """ + When executing query: + """ + MATCH(x) RETURN x + """ + Then the result should be: + | x | + | (:A{x: 5}) | + | (:B{x: 10}) | + | (:C{x: 7}) | + + Scenario: Remove 01 + Given an empty graph + And having executed + """ + CREATE (a:A:B:C) + """ + When executing query: + """ + MATCH (n) REMOVE n:A:B:C RETURN n + """ + Then the result should be: + | n | + | () | + + Scenario: Remove 02 + Given an empty graph + And having executed + """ + CREATE (a:A:B:C) + """ + When executing query: + """ + MATCH (n) REMOVE n:B:C RETURN n + """ + Then the result should be: + | n | + | (:A) | + + Scenario: Remove 03 + Given an empty graph + And having executed + """ + CREATE (a{a: 1, b: 1.0, c: 's', d: false}) + """ + When executing query: + """ + MATCH (n) REMOVE n:A:B, n.a REMOVE n.b, n.c, n.d RETURN n + """ + Then the result should be: + | n | + | () | + + Scenario: Remove 04 + Given an empty graph + And having executed + """ + CREATE (a:A:B{a: 1, b: 's', c: 1.0, d: true}) + """ + When executing query: + """ + MATCH (n) REMOVE n:B, n.a, n.d RETURN n + + """ + Then the result should be: + | n | + | (:A{b: 's', c: 1.0}) | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/features/with.feature b/tests/gql_behave/tests/memgraph_V1_on_disk/features/with.feature new file mode 100644 index 000000000..1541df75e --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/features/with.feature @@ -0,0 +1,251 @@ +Feature: With + + Scenario: With test 01: + Given an empty graph + And having executed: + """ + CREATE (a:A), (b:B), (c:C), (d:D), (e:E), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (:A)--(a)-->() WITH a, COUNT(*) AS n WHERE n > 1 RETURN a + """ + Then the result should be: + | a | + | (:B) | + + Scenario: With test 02: + Given an empty graph + And having executed + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (a)-[:R]->(b), (b)-[:R]->(c), (c)-[:R]->(d), (d)-[:R]->(a) + """ + When executing query: + """ + MATCH (a)--(b) + WITH a, MAX(b.x) AS s + RETURN a, s + """ + Then the result should be: + | a | s | + | (:A{x: 1}) | 4 | + | (:B{x: 2}) | 3 | + | (:C{x: 3}) | 4 | + | (:D{x: 4}) | 3 | + + Scenario: With test 03: + Given an empty graph + And having executed + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (a)-[:R]->(b), (a)-[:R]->(b), (b)-[:R]->(a), (b)-[:R]->(a) + """ + When executing query: + """ + MATCH (b)--(a)--(c) + WITH a, (SUM(b.x)+SUM(c.x)) AS s + RETURN a, s + """ + Then the result should be: + | a | s | + | (:A{x: 1}) | 48 | + | (:B{x: 2}) | 24 | + + Scenario: With test 04: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (e:E{x: 5}), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (c)--(a:B)--(b)--(d) + WITH a, b, SUM(c.x)+SUM(d.x) AS n RETURN a, b, n + """ + Then the result should be: + | a | b | n | + | (:B{x: 2}) | (:A{x: 1}) | 13 | + | (:B{x: 2}) | (:C{x: 3}) | 22 | + | (:B{x: 2}) | (:D{x: 4}) | 14 | + + Scenario: With test 05: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (e:E{x: 5}), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (c)--(a:B)--(b)--(d) + WITH a, b, AVG(c.x + d.x) AS n RETURN a, b, n + """ + Then the result should be: + | a | b | n | + | (:B{x: 2}) | (:A{x: 1}) | 6.5 | + | (:B{x: 2}) | (:C{x: 3}) | 5.5 | + | (:B{x: 2}) | (:D{x: 4}) | 7.0 | + + Scenario: With test 06: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (e:E{x: 5}), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (c)--(a:B)--(b)--(d) + WITH a, b, AVG(c.x + d.x) AS n RETURN MAX(n) AS n + """ + Then the result should be: + | n | + | 7.0 | + + Scenario: With test 07: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (e:E{x: 5}), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (c)--(a:B)--(b)--(d) + WITH a, b, AVG(c.x + d.x) AS n + WITH a, MAX(n) AS n RETURN a, n + """ + Then the result should be: + | a | n | + | (:B{x: 2}) | 7.0 | + + Scenario: With test 08: + Given an empty graph + When executing query: + """ + CREATE (a), (b) WITH a, b CREATE (a)-[r:R]->(b) RETURN r + """ + Then the result should be: + | r | + | [:R] | + + Scenario: With test 09: + Given an empty graph + When executing query: + """ + CREATE (a), (b) WITH a, b SET a:X SET b:Y WITH a, b MATCH(x:X) RETURN x + """ + Then the result should be: + | x | + | (:X) | + + Scenario: With test 10: + Given an empty graph + When executing query: + """ + CREATE (a), (b), (a)-[:R]->(b) WITH a, b SET a:X SET b:Y + WITH a MATCH(x:X)--(b) RETURN x, x AS y + """ + Then the result should be: + | x | y | + | (:X) | (:X) | + + Scenario: With test 10: + Given an empty graph + And having executed: + """ + CREATE (a:A{x: 1}), (b:B{x: 2}), (c:C{x: 3}), (d:D{x: 4}), (e:E{x: 5}), (a)-[:R]->(b), (b)-[:R]->(c), (b)-[:R]->(d), (c)-[:R]->(a), (c)-[:R]->(e), (d)-[:R]->(e) + """ + When executing query: + """ + MATCH (c)--(a:B)--(b)--(d) WITH a, b, AVG(c.x + d.x) AS av WITH AVG(av) AS avg + MATCH (c)--(a:B)--(b)--(d) WITH a, b, avg, AVG(c.x + d.x) AS av WHERE av>avg RETURN av + """ + Then the result should be: + | av | + | 6.5 | + | 7.0 | + + Scenario: With test 11: + Given an empty graph + And having executed: + """ + CREATE(:A{a: 1}), (:B{a: 1}), (:C{a: 1}), (:D{a: 4}), (:E{a: 5}) + """ + When executing query: + """ + MATCH(n) WITH n.a AS a + ORDER BY a LIMIT 4 + RETURN a + """ + Then the result should be, in order: + | a | + | 1 | + | 1 | + | 1 | + | 4 | + + Scenario: With test 12: + Given an empty graph + And having executed: + """ + CREATE(:A{a: 1}), (:B{a: 5}), (:C{a: 2}), (:D{a: 3}), (:E{a: 5}) + """ + When executing query: + """ + MATCH(n) WITH n.a AS a + ORDER BY a SKIP 2 + RETURN a + """ + Then the result should be, in order: + | a | + | 3 | + | 5 | + | 5 | + + Scenario: With test 13: + Given an empty graph + And having executed: + """ + CREATE(:A{a: 1}), (:B{a: 5}), (:C{a: 2}), (:D{a: 3}), (:E{a: 5}) + """ + When executing query: + """ + MATCH(n) WITH n.a AS a + ORDER BY a + RETURN a + """ + Then the result should be, in order: + | a | + | 1 | + | 2 | + | 3 | + | 5 | + | 5 | + + Scenario: With test 14: + Given an empty graph + And having executed: + """ + CREATE(:A{a: 1}), (:B{a: 5}), (:C{a: 1}), (:D{a: 3}), (:E{a: 5}) + """ + When executing query: + """ + MATCH(n) WITH DISTINCT n.a AS a + RETURN a + """ + Then the result should be: + | a | + | 1 | + | 3 | + | 5 | + + Scenario: With test 15: + Given an empty graph + And having executed: + """ + CREATE ({id: 0}) + """ + When executing query: + """ + MATCH (n) WITH n RETURN * + """ + Then the result should be: + | n | + | ({id: 0}) | diff --git a/tests/gql_behave/tests/memgraph_V1_on_disk/graphs/graph_01.cypher b/tests/gql_behave/tests/memgraph_V1_on_disk/graphs/graph_01.cypher new file mode 100644 index 000000000..4f1ea3056 --- /dev/null +++ b/tests/gql_behave/tests/memgraph_V1_on_disk/graphs/graph_01.cypher @@ -0,0 +1,4 @@ +CREATE (n:Person {age: 20}); +CREATE (n:Person:Student {age: 20}); +CREATE (n:Person {age: 21}); +CREATE (n:Student {age:21})