# # Copyright 2017 "Neo Technology", # Network Engine for Objects in Lund AB (http://neotechnology.com) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Feature: MatchAcceptance2 Scenario: Do not return non-existent nodes Given an empty graph When executing query: """ MATCH (n) RETURN n """ Then the result should be: | n | And no side effects Scenario: Do not return non-existent relationships Given an empty graph When executing query: """ MATCH ()-[r]->() RETURN r """ Then the result should be: | r | And no side effects Scenario: Do not fail when evaluating predicates with illegal operations if the AND'ed predicate evaluates to false Given an empty graph And having executed: """ CREATE (root:Root {name: 'x'}), (child1:TextNode {id: 'text'}), (child2:IntNode {id: 0}) CREATE (root)-[:T]->(child1), (root)-[:T]->(child2) """ When executing query: """ MATCH (:Root {name: 'x'})-->(i:TextNode) WHERE i.id > 'te' RETURN i """ Then the result should be: | i | | (:TextNode {id: 'text'}) | And no side effects Scenario: Do not fail when evaluating predicates with illegal operations if the OR'd predicate evaluates to true Given an empty graph And having executed: """ CREATE (root:Root {name: 'x'}), (child1:TextNode {id: 'text'}), (child2:IntNode {id: 0}) CREATE (root)-[:T]->(child1), (root)-[:T]->(child2) """ When executing query: """ MATCH (:Root {name: 'x'})-->(i) WHERE exists(i.id) OR i.id > 'te' RETURN i """ Then the result should be: | i | | (:TextNode {id: 'text'}) | | (:IntNode {id: 0}) | And no side effects Scenario: Aggregation with named paths Given an empty graph And having executed: """ CREATE (n1 {num: 1}), (n2 {num: 2}), (n3 {num: 3}), (n4 {num: 4}) CREATE (n1)-[:T]->(n2), (n3)-[:T]->(n4) """ When executing query: """ MATCH p = ()-[*]->() WITH count(*) AS count, p AS p WITH nodes(p) AS nodes RETURN * """ Then the result should be: | nodes | | [({num: 1}), ({num: 2})] | | [({num: 3}), ({num: 4})] | And no side effects Scenario: Zero-length variable length pattern in the middle of the pattern Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}), ({name: 'D'}), ({name: 'E'}) CREATE (a)-[:CONTAINS]->(b), (b)-[:FRIEND]->(c) """ When executing query: """ MATCH (a {name: 'A'})-[:CONTAINS*0..1]->(b)-[:FRIEND*0..1]->(c) RETURN a, b, c """ Then the result should be: | a | b | c | | ({name: 'A'}) | ({name: 'A'}) | ({name: 'A'}) | | ({name: 'A'}) | ({name: 'B'}) | ({name: 'B'}) | | ({name: 'A'}) | ({name: 'B'}) | ({name: 'C'}) | And no side effects Scenario: Simple variable length pattern Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}), (d {name: 'D'}) CREATE (a)-[:CONTAINS]->(b), (b)-[:CONTAINS]->(c), (c)-[:CONTAINS]->(d) """ When executing query: """ MATCH (a {name: 'A'})-[*]->(x) RETURN x """ Then the result should be: | x | | ({name: 'B'}) | | ({name: 'C'}) | | ({name: 'D'}) | And no side effects Scenario: Variable length relationship without lower bound Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(c) """ When executing query: """ MATCH p = ({name: 'A'})-[:KNOWS*..2]->() RETURN p """ Then the result should be: | p | | <({name: 'A'})-[:KNOWS]->({name: 'B'})> | | <({name: 'A'})-[:KNOWS]->({name: 'B'})-[:KNOWS]->({name: 'C'})> | And no side effects Scenario: Variable length relationship without bounds Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(c) """ When executing query: """ MATCH p = ({name: 'A'})-[:KNOWS*..]->() RETURN p """ Then the result should be: | p | | <({name: 'A'})-[:KNOWS]->({name: 'B'})> | | <({name: 'A'})-[:KNOWS]->({name: 'B'})-[:KNOWS]->({name: 'C'})> | And no side effects Scenario: Returning bound nodes that are not part of the pattern Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:KNOWS]->(b) """ When executing query: """ MATCH (a {name: 'A'}), (c {name: 'C'}) MATCH (a)-->(b) RETURN a, b, c """ Then the result should be: | a | b | c | | ({name: 'A'}) | ({name: 'B'}) | ({name: 'C'}) | And no side effects Scenario: Two bound nodes pointing to the same node Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (x1 {name: 'x1'}), (x2 {name: 'x2'}) CREATE (a)-[:KNOWS]->(x1), (a)-[:KNOWS]->(x2), (b)-[:KNOWS]->(x1), (b)-[:KNOWS]->(x2) """ When executing query: """ MATCH (a {name: 'A'}), (b {name: 'B'}) MATCH (a)-->(x)<-->(b) RETURN x """ Then the result should be: | x | | ({name: 'x1'}) | | ({name: 'x2'}) | And no side effects Scenario: Three bound nodes pointing to the same node Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}), (x1 {name: 'x1'}), (x2 {name: 'x2'}) CREATE (a)-[:KNOWS]->(x1), (a)-[:KNOWS]->(x2), (b)-[:KNOWS]->(x1), (b)-[:KNOWS]->(x2), (c)-[:KNOWS]->(x1), (c)-[:KNOWS]->(x2) """ When executing query: """ MATCH (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) MATCH (a)-->(x), (b)-->(x), (c)-->(x) RETURN x """ Then the result should be: | x | | ({name: 'x1'}) | | ({name: 'x2'}) | And no side effects Scenario: Three bound nodes pointing to the same node with extra connections Given an empty graph And having executed: """ CREATE (a {name: 'a'}), (b {name: 'b'}), (c {name: 'c'}), (d {name: 'd'}), (e {name: 'e'}), (f {name: 'f'}), (g {name: 'g'}), (h {name: 'h'}), (i {name: 'i'}), (j {name: 'j'}), (k {name: 'k'}) CREATE (a)-[:KNOWS]->(d), (a)-[:KNOWS]->(e), (a)-[:KNOWS]->(f), (a)-[:KNOWS]->(g), (a)-[:KNOWS]->(i), (b)-[:KNOWS]->(d), (b)-[:KNOWS]->(e), (b)-[:KNOWS]->(f), (b)-[:KNOWS]->(h), (b)-[:KNOWS]->(k), (c)-[:KNOWS]->(d), (c)-[:KNOWS]->(e), (c)-[:KNOWS]->(h), (c)-[:KNOWS]->(g), (c)-[:KNOWS]->(j) """ When executing query: """ MATCH (a {name: 'a'}), (b {name: 'b'}), (c {name: 'c'}) MATCH (a)-->(x), (b)-->(x), (c)-->(x) RETURN x """ Then the result should be: | x | | ({name: 'd'}) | | ({name: 'e'}) | And no side effects Scenario: MATCH with OPTIONAL MATCH in longer pattern Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:KNOWS]->(b), (b)-[:KNOWS]->(c) """ When executing query: """ MATCH (a {name: 'A'}) OPTIONAL MATCH (a)-[:KNOWS]->()-[:KNOWS]->(foo) RETURN foo """ Then the result should be: | foo | | ({name: 'C'}) | And no side effects Scenario: Optionally matching named paths Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:X]->(b) """ When executing query: """ MATCH (a {name: 'A'}), (x) WHERE x.name IN ['B', 'C'] OPTIONAL MATCH p = (a)-->(x) RETURN x, p """ Then the result should be: | x | p | | ({name: 'B'}) | <({name: 'A'})-[:X]->({name: 'B'})> | | ({name: 'C'}) | null | And no side effects Scenario: Optionally matching named paths with single and variable length patterns Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}) CREATE (a)-[:X]->(b) """ When executing query: """ MATCH (a {name: 'A'}) OPTIONAL MATCH p = (a)-->(b)-[*]->(c) RETURN p """ Then the result should be: | p | | null | And no side effects Scenario: Optionally matching named paths with variable length patterns Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b {name: 'B'}), (c {name: 'C'}) CREATE (a)-[:X]->(b) """ When executing query: """ MATCH (a {name: 'A'}), (x) WHERE x.name IN ['B', 'C'] OPTIONAL MATCH p = (a)-[r*]->(x) RETURN r, x, p """ Then the result should be: | r | x | p | | [[:X]] | ({name: 'B'}) | <({name: 'A'})-[:X]->({name: 'B'})> | | null | ({name: 'C'}) | null | And no side effects Scenario: Matching variable length patterns from a bound node Given an empty graph And having executed: """ CREATE (a:A), (b), (c) CREATE (a)-[:X]->(b), (b)-[:Y]->(c) """ When executing query: """ MATCH (a:A) MATCH (a)-[r*2]->() RETURN r """ Then the result should be (ignoring element order for lists): | r | | [[:X], [:Y]] | And no side effects Scenario: Excluding connected nodes Given an empty graph And having executed: """ CREATE (a:A), (b:B {id: 1}), (:B {id: 2}) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (a:A), (other:B) OPTIONAL MATCH (a)-[r]->(other) WITH other WHERE r IS NULL RETURN other """ Then the result should be: | other | | (:B {id: 2}) | And no side effects Scenario: Do not fail when predicates on optionally matched and missed nodes are invalid Given an empty graph And having executed: """ CREATE (a), (b {name: 'Mark'}) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (n)-->(x0) OPTIONAL MATCH (x0)-->(x1) WHERE x1.foo = 'bar' RETURN x0.name """ Then the result should be: | x0.name | | 'Mark' | And no side effects Scenario: MATCH and OPTIONAL MATCH on same pattern Given an empty graph And having executed: """ CREATE (a {name: 'A'}), (b:B {name: 'B'}), (c:C {name: 'C'}) CREATE (a)-[:T]->(b), (a)-[:T]->(c) """ When executing query: """ MATCH (a)-->(b) WHERE b:B OPTIONAL MATCH (a)-->(c) WHERE c:C RETURN a.name """ Then the result should be: | a.name | | 'A' | And no side effects Scenario: Matching using an undirected pattern Given an empty graph And having executed: """ CREATE (:A {id: 0})-[:ADMIN]->(:B {id: 1}) """ When executing query: """ MATCH (a)-[:ADMIN]-(b) WHERE a:A RETURN a.id, b.id """ Then the result should be: | a.id | b.id | | 0 | 1 | And no side effects Scenario: Matching all nodes Given an empty graph And having executed: """ CREATE (:A), (:B) """ When executing query: """ MATCH (n) RETURN n """ Then the result should be: | n | | (:A) | | (:B) | And no side effects Scenario: Comparing nodes for equality Given an empty graph And having executed: """ CREATE (:A), (:B) """ When executing query: """ MATCH (a), (b) WHERE a <> b RETURN a, b """ Then the result should be: | a | b | | (:A) | (:B) | | (:B) | (:A) | And no side effects Scenario: Matching using self-referencing pattern returns no result Given an empty graph And having executed: """ CREATE (a), (b), (c) CREATE (a)-[:T]->(b), (b)-[:T]->(c) """ When executing query: """ MATCH (a)-->(b), (b)-->(b) RETURN b """ Then the result should be: | b | And no side effects Scenario: Variable length relationship in OPTIONAL MATCH Given an empty graph And having executed: """ CREATE (:A), (:B) """ When executing query: """ MATCH (a:A), (b:B) OPTIONAL MATCH (a)-[r*]-(b) WHERE r IS NULL AND a <> b RETURN b """ Then the result should be: | b | | (:B) | And no side effects Scenario: Matching using relationship predicate with multiples of the same type Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (a)-[:T|:T]->(b) RETURN b """ Then the result should be: | b | | (:B) | And no side effects Scenario: ORDER BY with LIMIT Given an empty graph And having executed: """ CREATE (a:A), (n1 {x: 1}), (n2 {x: 2}), (m1), (m2) CREATE (a)-[:T]->(n1), (n1)-[:T]->(m1), (a)-[:T]->(n2), (n2)-[:T]->(m2) """ When executing query: """ MATCH (a:A)-->(n)-->(m) RETURN n.x, count(*) ORDER BY n.x LIMIT 1000 """ Then the result should be, in order: | n.x | count(*) | | 1 | 1 | | 2 | 1 | And no side effects Scenario: Simple node property predicate Given an empty graph And having executed: """ CREATE ({foo: 'bar'}) """ When executing query: """ MATCH (n) WHERE n.foo = 'bar' RETURN n """ Then the result should be: | n | | ({foo: 'bar'}) | And no side effects Scenario: Handling direction of named paths Given an empty graph And having executed: """ CREATE (a:A)-[:T]->(b:B) """ When executing query: """ MATCH p = (b)<--(a) RETURN p """ Then the result should be: | p | | <(:B)<-[:T]-(:A)> | And no side effects Scenario: Simple OPTIONAL MATCH on empty graph Given an empty graph When executing query: """ OPTIONAL MATCH (n) RETURN n """ Then the result should be: | n | | null | And no side effects Scenario: OPTIONAL MATCH with previously bound nodes Given an empty graph And having executed: """ CREATE () """ When executing query: """ MATCH (n) OPTIONAL MATCH (n)-[:NOT_EXIST]->(x) RETURN n, x """ Then the result should be: | n | x | | () | null | And no side effects Scenario: `collect()` filtering nulls Given an empty graph And having executed: """ CREATE () """ When executing query: """ MATCH (n) OPTIONAL MATCH (n)-[:NOT_EXIST]->(x) RETURN n, collect(x) """ Then the result should be: | n | collect(x) | | () | [] | And no side effects Scenario: Multiple anonymous nodes in a pattern Given an empty graph And having executed: """ CREATE (:A) """ When executing query: """ MATCH (a)<--()<--(b)-->()-->(c) WHERE a:A RETURN c """ Then the result should be: | c | And no side effects Scenario: Matching a relationship pattern using a label predicate Given an empty graph And having executed: """ CREATE (a), (b1:Foo), (b2) CREATE (a)-[:T]->(b1), (a)-[:T]->(b2) """ When executing query: """ MATCH (a)-->(b:Foo) RETURN b """ Then the result should be: | b | | (:Foo) | And no side effects Scenario: Matching a relationship pattern using a label predicate on both sides Given an empty graph And having executed: """ CREATE (:A)-[:T1]->(:B), (:B)-[:T2]->(:A), (:B)-[:T3]->(:B), (:A)-[:T4]->(:A) """ When executing query: """ MATCH (:A)-[r]->(:B) RETURN r """ Then the result should be: | r | | [:T1] | And no side effects Scenario: Matching nodes using multiple labels Given an empty graph And having executed: """ CREATE (:A:B:C), (:A:B), (:A:C), (:B:C), (:A), (:B), (:C) """ When executing query: """ MATCH (a:A:B:C) RETURN a """ Then the result should be: | a | | (:A:B:C) | And no side effects Scenario: Returning label predicate expression Given an empty graph And having executed: """ CREATE (), (:Foo) """ When executing query: """ MATCH (n) RETURN (n:Foo) """ Then the result should be: | (n:Foo) | | true | | false | And no side effects Scenario: Matching with many predicates and larger pattern Given an empty graph And having executed: """ CREATE (advertiser {name: 'advertiser1', id: 0}), (thing {name: 'Color', id: 1}), (red {name: 'red'}), (p1 {name: 'product1'}), (p2 {name: 'product4'}) CREATE (advertiser)-[:ADV_HAS_PRODUCT]->(p1), (advertiser)-[:ADV_HAS_PRODUCT]->(p2), (thing)-[:AA_HAS_VALUE]->(red), (p1)-[:AP_HAS_VALUE]->(red), (p2)-[:AP_HAS_VALUE]->(red) """ And parameters are: | 1 | 0 | | 2 | 1 | When executing query: """ MATCH (advertiser)-[:ADV_HAS_PRODUCT]->(out)-[:AP_HAS_VALUE]->(red)<-[:AA_HAS_VALUE]-(a) WHERE advertiser.id = $1 AND a.id = $2 AND red.name = 'red' AND out.name = 'product1' RETURN out.name """ Then the result should be: | out.name | | 'product1' | And no side effects Scenario: Matching using a simple pattern with label predicate Given an empty graph And having executed: """ CREATE (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'}), (c), (d) CREATE (a)-[:T]->(c), (b)-[:T]->(d) """ When executing query: """ MATCH (n:Person)-->() WHERE n.name = 'Bob' RETURN n """ Then the result should be: | n | | (:Person {name: 'Bob'}) | And no side effects Scenario: Matching disconnected patterns Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C) CREATE (a)-[:T]->(b), (a)-[:T]->(c) """ When executing query: """ MATCH (a)-->(b) MATCH (c)-->(d) RETURN a, b, c, d """ Then the result should be: | a | b | c | d | | (:A) | (:B) | (:A) | (:B) | | (:A) | (:B) | (:A) | (:C) | | (:A) | (:C) | (:A) | (:B) | | (:A) | (:C) | (:A) | (:C) | And no side effects Scenario: Non-optional matches should not return nulls Given an empty graph And having executed: """ CREATE (a:A), (b:B {id: 1}), (c:C {id: 2}), (d:D) CREATE (a)-[:T]->(b), (a)-[:T]->(c), (a)-[:T]->(d), (b)-[:T]->(c), (b)-[:T]->(d), (c)-[:T]->(d) """ When executing query: """ MATCH (a)--(b)--(c)--(d)--(a), (b)--(d) WHERE a.id = 1 AND c.id = 2 RETURN d """ Then the result should be: | d | | (:A) | | (:D) | And no side effects Scenario: Handling cyclic patterns Given an empty graph And having executed: """ CREATE (a {name: 'a'}), (b {name: 'b'}), (c {name: 'c'}) CREATE (a)-[:A]->(b), (b)-[:B]->(a), (b)-[:B]->(c) """ When executing query: """ MATCH (a)-[:A]->()-[:B]->(a) RETURN a.name """ Then the result should be: | a.name | | 'a' | And no side effects Scenario: Handling cyclic patterns when separated into two parts Given an empty graph And having executed: """ CREATE (a {name: 'a'}), (b {name: 'b'}), (c {name: 'c'}) CREATE (a)-[:A]->(b), (b)-[:B]->(a), (b)-[:B]->(c) """ When executing query: """ MATCH (a)-[:A]->(b), (b)-[:B]->(a) RETURN a.name """ Then the result should be: | a.name | | 'a' | And no side effects Scenario: Handling fixed-length variable length pattern Given an empty graph And having executed: """ CREATE ()-[:T]->() """ When executing query: """ MATCH (a)-[r*1..1]->(b) RETURN r """ Then the result should be: | r | | [[:T]] | And no side effects Scenario: Matching from null nodes should return no results owing to finding no matches Given an empty graph When executing query: """ OPTIONAL MATCH (a) WITH a MATCH (a)-->(b) RETURN b """ Then the result should be: | b | And no side effects Scenario: Matching from null nodes should return no results owing to matches being filtered out Given an empty graph And having executed: """ CREATE ()-[:T]->() """ When executing query: """ OPTIONAL MATCH (a:Label) WITH a MATCH (a)-->(b) RETURN b """ Then the result should be: | b | And no side effects Scenario: Optionally matching from null nodes should return null Given an empty graph When executing query: """ OPTIONAL MATCH (a) WITH a OPTIONAL MATCH (a)-->(b) RETURN b """ Then the result should be: | b | | null | And no side effects Scenario: OPTIONAL MATCH returns null Given an empty graph When executing query: """ OPTIONAL MATCH (a) RETURN a """ Then the result should be: | a | | null | And no side effects Scenario: Zero-length named path Given an empty graph And having executed: """ CREATE () """ When executing query: """ MATCH p = (a) RETURN p """ Then the result should be: | p | | <()> | And no side effects Scenario: Variable-length named path Given an empty graph And having executed: """ CREATE () """ When executing query: """ MATCH p = ()-[*0..]->() RETURN p """ Then the result should be: | p | | <()> | And no side effects Scenario: Matching with aggregation Given an empty graph And having executed: """ CREATE ({prop: 42}) """ When executing query: """ MATCH (n) RETURN n.prop AS n, count(n) AS count """ Then the result should be: | n | count | | 42 | 1 | And no side effects Scenario: Matching using a relationship that is already bound Given an empty graph And having executed: """ CREATE ()-[:T1]->(), ()-[:T2]->() """ When executing query: """ MATCH ()-[r1]->() WITH r1 AS r2 MATCH ()-[r2]->() RETURN r2 AS rel """ Then the result should be: | rel | | [:T1] | | [:T2] | And no side effects Scenario: Matching using a relationship that is already bound, in conjunction with aggregation Given an empty graph And having executed: """ CREATE ()-[:T1]->(), ()-[:T2]->() """ When executing query: """ MATCH ()-[r1]->() WITH r1 AS r2, count(*) AS c ORDER BY c MATCH ()-[r2]->() RETURN r2 AS rel """ Then the result should be: | rel | | [:T1] | | [:T2] | And no side effects Scenario: Matching using a relationship that is already bound, in conjunction with aggregation and ORDER BY Given an empty graph And having executed: """ CREATE ()-[:T1 {id: 0}]->(), ()-[:T2 {id: 1}]->() """ When executing query: """ MATCH (a)-[r]->(b) WITH a, r, b, count(*) AS c ORDER BY c MATCH (a)-[r]->(b) RETURN r AS rel ORDER BY rel.id """ Then the result should be, in order: | rel | | [:T1 {id: 0}] | | [:T2 {id: 1}] | And no side effects Scenario: Matching with LIMIT and optionally matching using a relationship that is already bound Given an empty graph And having executed: """ CREATE (:A)-[:T]->(:B) """ When executing query: """ MATCH ()-[r]->() WITH r LIMIT 1 OPTIONAL MATCH (a2)-[r]->(b2) RETURN a2, r, b2 """ Then the result should be: | a2 | r | b2 | | (:A) | [:T] | (:B) | And no side effects Scenario: Matching with LIMIT and optionally matching using a relationship and node that are both already bound Given an empty graph And having executed: """ CREATE (:A)-[:T]->(:B) """ When executing query: """ MATCH (a1)-[r]->() WITH r, a1 LIMIT 1 OPTIONAL MATCH (a1)-[r]->(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | | (:A) | [:T] | (:B) | And no side effects Scenario: Matching with LIMIT, then matching again using a relationship and node that are both already bound along with an additional predicate Given an empty graph And having executed: """ CREATE ()-[:T]->() """ When executing query: """ MATCH (a1)-[r]->() WITH r, a1 LIMIT 1 MATCH (a1:X)-[r]->(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | And no side effects Scenario: Matching with LIMIT and predicates, then matching again using a relationship and node that are both already bound along with a duplicate predicate Given an empty graph And having executed: """ CREATE (:X:Y)-[:T]->() """ When executing query: """ MATCH (a1:X:Y)-[r]->() WITH r, a1 LIMIT 1 MATCH (a1:Y)-[r]->(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | | (:X:Y) | [:T] | () | And no side effects Scenario: Matching twice with conflicting relationship types on same relationship Given an empty graph And having executed: """ CREATE ()-[:T]->() """ When executing query: """ MATCH (a1)-[r:T]->() WITH r, a1 LIMIT 1 MATCH (a1)-[r:Y]->(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | And no side effects Scenario: Matching twice with duplicate relationship types on same relationship Given an empty graph And having executed: """ CREATE (:A)-[:T]->(:B) """ When executing query: """ MATCH (a1)-[r:T]->() WITH r, a1 LIMIT 1 MATCH (a1)-[r:T]->(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | | (:A) | [:T] | (:B) | And no side effects Scenario: Matching relationships into a list and matching variable length using the list Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C) CREATE (a)-[:Y]->(b), (b)-[:Y]->(c) """ When executing query: """ MATCH ()-[r1]->()-[r2]->() WITH [r1, r2] AS rs LIMIT 1 MATCH (first)-[rs*]->(second) RETURN first, second """ Then the result should be: | first | second | | (:A) | (:C) | And no side effects Scenario: Matching relationships into a list and matching variable length using the list, with bound nodes Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C) CREATE (a)-[:Y]->(b), (b)-[:Y]->(c) """ When executing query: """ MATCH (a)-[r1]->()-[r2]->(b) WITH [r1, r2] AS rs, a AS first, b AS second LIMIT 1 MATCH (first)-[rs*]->(second) RETURN first, second """ Then the result should be: | first | second | | (:A) | (:C) | And no side effects Scenario: Matching relationships into a list and matching variable length using the list, with bound nodes, wrong direction Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C) CREATE (a)-[:Y]->(b), (b)-[:Y]->(c) """ When executing query: """ MATCH (a)-[r1]->()-[r2]->(b) WITH [r1, r2] AS rs, a AS second, b AS first LIMIT 1 MATCH (first)-[rs*]->(second) RETURN first, second """ Then the result should be: | first | second | And no side effects Scenario: Matching and optionally matching with bound nodes in reverse direction Given an empty graph And having executed: """ CREATE (:A)-[:T]->(:B) """ When executing query: """ MATCH (a1)-[r]->() WITH r, a1 LIMIT 1 OPTIONAL MATCH (a1)<-[r]-(b2) RETURN a1, r, b2 """ Then the result should be: | a1 | r | b2 | | (:A) | [:T] | null | And no side effects Scenario: Matching and optionally matching with unbound nodes and equality predicate in reverse direction Given an empty graph And having executed: """ CREATE (:A)-[:T]->(:B) """ When executing query: """ MATCH (a1)-[r]->() WITH r, a1 LIMIT 1 OPTIONAL MATCH (a2)<-[r]-(b2) WHERE a1 = a2 RETURN a1, r, b2, a2 """ Then the result should be: | a1 | r | b2 | a2 | | (:A) | [:T] | null | null | And no side effects Scenario: Fail when using property access on primitive type Given an empty graph And having executed: """ CREATE ({prop: 42}) """ When executing query: """ MATCH (n) WITH n.prop AS n2 RETURN n2.prop """ Then a TypeError should be raised at runtime: PropertyAccessOnNonMap Scenario: Matching and returning ordered results, with LIMIT Given an empty graph And having executed: """ CREATE ({bar: 1}), ({bar: 3}), ({bar: 2}) """ When executing query: """ MATCH (foo) RETURN foo.bar AS x ORDER BY x DESC LIMIT 4 """ Then the result should be, in order: | x | | 3 | | 2 | | 1 | And no side effects Scenario: Counting an empty graph Given an empty graph When executing query: """ MATCH (a) RETURN count(a) > 0 """ Then the result should be: | count(a) > 0 | | false | And no side effects Scenario: Matching variable length pattern with property predicate Given an empty graph And having executed: """ CREATE (a:Artist:A), (b:Artist:B), (c:Artist:C) CREATE (a)-[:WORKED_WITH {year: 1987}]->(b), (b)-[:WORKED_WITH {year: 1988}]->(c) """ When executing query: """ MATCH (a:Artist)-[:WORKED_WITH* {year: 1988}]->(b:Artist) RETURN * """ Then the result should be: | a | b | | (:Artist:B) | (:Artist:C) | And no side effects Scenario: Variable length pattern checking labels on endnodes Given an empty graph And having executed: """ CREATE (a:Label {id: 0}), (b:Label {id: 1}), (c:Label {id: 2}) CREATE (a)-[:T]->(b), (b)-[:T]->(c) """ When executing query: """ MATCH (a), (b) WHERE a.id = 0 AND (a)-[:T]->(b:Label) OR (a)-[:T*]->(b:MissingLabel) RETURN DISTINCT b """ Then the result should be: | b | | (:Label {id: 1}) | And no side effects Scenario: Variable length pattern with label predicate on both sides Given an empty graph And having executed: """ CREATE (a:Blue), (b:Red), (c:Green), (d:Yellow) CREATE (a)-[:T]->(b), (b)-[:T]->(c), (b)-[:T]->(d) """ When executing query: """ MATCH (a:Blue)-[r*]->(b:Green) RETURN count(r) """ Then the result should be: | count(r) | | 1 | And no side effects Scenario: Undirected named path Given an empty graph And having executed: """ CREATE (a:Movie), (b) CREATE (b)-[:T]->(a) """ When executing query: """ MATCH p = (n:Movie)--(m) RETURN p LIMIT 1 """ Then the result should be: | p | | <(:Movie)<-[:T]-()> | And no side effects Scenario: Named path with WITH Given an empty graph And having executed: """ CREATE () """ When executing query: """ MATCH p = (a) WITH p RETURN p """ Then the result should be: | p | | <()> | And no side effects Scenario: Named path with alternating directed/undirected relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C) CREATE (b)-[:T]->(a), (c)-[:T]->(b) """ When executing query: """ MATCH p = (n)-->(m)--(o) RETURN p """ Then the result should be: | p | | <(:C)-[:T]->(:B)-[:T]->(:A)> | And no side effects Scenario: Named path with multiple alternating directed/undirected relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B), (c:C), (d:D) CREATE (b)-[:T]->(a), (c)-[:T]->(b), (d)-[:T]->(c) """ When executing query: """ MATCH path = (n)-->(m)--(o)--(p) RETURN path """ Then the result should be: | path | | <(:D)-[:T]->(:C)-[:T]->(:B)-[:T]->(:A)> | And no side effects Scenario: Named path with undirected fixed variable length pattern Given an empty graph And having executed: """ CREATE (db1:Start), (db2:End), (mid), (other) CREATE (mid)-[:CONNECTED_TO]->(db1), (mid)-[:CONNECTED_TO]->(db2), (mid)-[:CONNECTED_TO]->(db2), (mid)-[:CONNECTED_TO]->(other), (mid)-[:CONNECTED_TO]->(other) """ When executing query: """ MATCH topRoute = (:Start)<-[:CONNECTED_TO]-()-[:CONNECTED_TO*3..3]-(:End) RETURN topRoute """ Then the result should be: | topRoute | | <(:Start)<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->()<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->(:End)> | | <(:Start)<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->()<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->(:End)> | | <(:Start)<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->()<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->(:End)> | | <(:Start)<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->()<-[:CONNECTED_TO]-()-[:CONNECTED_TO]->(:End)> | And no side effects Scenario: Returning a node property value Given an empty graph And having executed: """ CREATE ({prop: 1}) """ When executing query: """ MATCH (a) RETURN a.prop """ Then the result should be: | a.prop | | 1 | And no side effects Scenario: Returning a relationship property value Given an empty graph And having executed: """ CREATE ()-[:T {prop: 1}]->() """ When executing query: """ MATCH ()-[r]->() RETURN r.prop """ Then the result should be: | r.prop | | 1 | And no side effects Scenario: Projecting nodes and relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (a)-[r]->() RETURN a AS foo, r AS bar """ Then the result should be: | foo | bar | | (:A) | [:T] | And no side effects Scenario: Missing node property should become null Given an empty graph And having executed: """ CREATE ({foo: 1}) """ When executing query: """ MATCH (a) RETURN a.bar """ Then the result should be: | a.bar | | null | And no side effects Scenario: Missing relationship property should become null Given an empty graph And having executed: """ CREATE ()-[:T {foo: 1}]->() """ When executing query: """ MATCH ()-[r]->() RETURN r.bar """ Then the result should be: | r.bar | | null | And no side effects Scenario: Returning multiple node property values Given an empty graph And having executed: """ CREATE ({name: 'Philip J. Fry', age: 2046, seasons: [1, 2, 3, 4, 5, 6, 7]}) """ When executing query: """ MATCH (a) RETURN a.name, a.age, a.seasons """ Then the result should be: | a.name | a.age | a.seasons | | 'Philip J. Fry' | 2046 | [1, 2, 3, 4, 5, 6, 7] | And no side effects Scenario: Adding a property and a literal in projection Given an empty graph And having executed: """ CREATE ({prop: 1}) """ When executing query: """ MATCH (a) RETURN a.prop + 1 AS foo """ Then the result should be: | foo | | 2 | And no side effects Scenario: Adding list properties in projection Given an empty graph And having executed: """ CREATE ({prop1: [1, 2, 3], prop2: [4, 5]}) """ When executing query: """ MATCH (a) RETURN a.prop2 + a.prop1 AS foo """ Then the result should be: | foo | | [4, 5, 1, 2, 3] | And no side effects Scenario: Variable length relationship variables are lists of relationships Given an empty graph And having executed: """ CREATE (a), (b), (c) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH ()-[r*0..1]-() RETURN last(r) AS l """ Then the result should be: | l | | [:T] | | [:T] | | null | | null | | null | And no side effects Scenario: Variable length patterns and nulls Given an empty graph And having executed: """ CREATE (a:A), (b:B) """ When executing query: """ MATCH (a:A) OPTIONAL MATCH (a)-[:FOO]->(b:B) OPTIONAL MATCH (b)<-[:BAR*]-(c:B) RETURN a, b, c """ Then the result should be: | a | b | c | | (:A) | null | null | And no side effects Scenario: Projecting a list of nodes and relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (n)-[r]->(m) RETURN [n, r, m] AS r """ Then the result should be: | r | | [(:A), [:T], (:B)] | And no side effects Scenario: Projecting a map of nodes and relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (n)-[r]->(m) RETURN {node1: n, rel: r, node2: m} AS m """ Then the result should be: | m | | {node1: (:A), rel: [:T], node2: (:B)} | And no side effects Scenario: Respecting direction when matching existing path Given an empty graph And having executed: """ CREATE (a {prop: 'a'}), (b {prop: 'b'}) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH p = ({prop: 'a'})-->({prop: 'b'}) RETURN p """ Then the result should be: | p | | <({prop: 'a'})-[:T]->({prop: 'b'})> | And no side effects Scenario: Respecting direction when matching non-existent path Given an empty graph And having executed: """ CREATE (a {prop: 'a'}), (b {prop: 'b'}) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH p = ({prop: 'a'})<--({prop: 'b'}) RETURN p """ Then the result should be: | p | And no side effects Scenario: Respecting direction when matching non-existent path with multiple directions Given an empty graph And having executed: """ CREATE (a), (b) CREATE (a)-[:T]->(b), (b)-[:T]->(a) """ When executing query: """ MATCH p = (n)-->(k)<--(n) RETURN p """ Then the result should be: | p | And no side effects Scenario: Matching path with both directions should respect other directions Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T1]->(b), (b)-[:T2]->(a) """ When executing query: """ MATCH p = (n)<-->(k)<--(n) RETURN p """ Then the result should be: | p | | <(:A)<-[:T2]-(:B)<-[:T1]-(:A)> | | <(:B)<-[:T1]-(:A)<-[:T2]-(:B)> | And no side effects Scenario: Matching path with multiple bidirectional relationships Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T1]->(b), (b)-[:T2]->(a) """ When executing query: """ MATCH p=(n)<-->(k)<-->(n) RETURN p """ Then the result should be: | p | | <(:A)<-[:T2]-(:B)<-[:T1]-(:A)> | | <(:A)-[:T1]->(:B)-[:T2]->(:A)> | | <(:B)<-[:T1]-(:A)<-[:T2]-(:B)> | | <(:B)-[:T2]->(:A)-[:T1]->(:B)> | And no side effects Scenario: Matching nodes with many labels Given an empty graph And having executed: """ CREATE (a:A:B:C:D:E:F:G:H:I:J:K:L:M), (b:U:V:W:X:Y:Z) CREATE (a)-[:T]->(b) """ When executing query: """ MATCH (n:A:B:C:D:E:F:G:H:I:J:K:L:M)-[:T]->(m:Z:Y:X:W:V:U) RETURN n, m """ Then the result should be: | n | m | | (:A:B:C:D:E:F:G:H:I:J:K:L:M) | (:Z:Y:X:W:V:U) | And no side effects Scenario: Matching longer variable length paths Given an empty graph And having executed: """ CREATE (a {prop: 'start'}), (b {prop: 'end'}) WITH * UNWIND range(1, 20) AS i CREATE (n {prop: i}) WITH [a] + collect(n) + [b] AS nodeList UNWIND range(0, size(nodeList) - 2, 1) AS i WITH nodeList[i] AS n1, nodeList[i+1] AS n2 CREATE (n1)-[:T]->(n2) """ When executing query: """ MATCH (n {prop: 'start'})-[:T*]->(m {prop: 'end'}) RETURN m """ Then the result should be: | m | | ({prop: 'end'}) | And no side effects Scenario: Counting rows after MATCH, MERGE, OPTIONAL MATCH Given an empty graph And having executed: """ CREATE (a:A), (b:B) CREATE (a)-[:T1]->(b), (b)-[:T2]->(a) """ When executing query: """ MATCH (a) MERGE (b) WITH * OPTIONAL MATCH (a)--(b) RETURN count(*) """ Then the result should be: | count(*) | | 6 | And no side effects Scenario: Matching a self-loop Given an empty graph And having executed: """ CREATE (a) CREATE (a)-[:T]->(a) """ When executing query: """ MATCH ()-[r]-() RETURN type(r) AS r """ Then the result should be: | r | | 'T' | And no side effects