81289d0663
Reviewers: buda Reviewed By: buda Subscribers: matej.gradicek Differential Revision: https://phabricator.memgraph.io/D122
1846 lines
45 KiB
Gherkin
1846 lines
45 KiB
Gherkin
#
|
|
# 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
|