Add openCypher 09 tck tests

Summary:
Added latest tck openCypher tests. Made sure the file is parseable and that
it returns coverage.

Also bumped continuous integration configuration to use the 09 version.

Reviewers: mferencevic, teon.banek, buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1197
This commit is contained in:
Matija Santl 2018-02-14 14:53:41 +01:00
parent b8acefb1e3
commit f83cc31779
54 changed files with 15608 additions and 1 deletions

View File

@ -22,7 +22,7 @@ log = logging.getLogger(__name__)
# constants
memgraph_suite = "memgraph_V1"
extra_suites = ["openCypher_M06"]
extra_suites = ["openCypher_M09"]
results_folder = os.path.join("tck_engine", "results")
suite_suffix = "memgraph-{}.json"
qa_status_path = ".quality_assurance_status"

View File

@ -0,0 +1,162 @@
#
# Copyright (c) 2015-2018 "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: Aggregation
Scenario: `max()` over strings
Given any graph
When executing query:
"""
UNWIND ['a', 'b', 'B', null, 'abc', 'abc1'] AS i
RETURN max(i)
"""
Then the result should be:
| max(i) |
| 'b' |
And no side effects
Scenario: `min()` over strings
Given any graph
When executing query:
"""
UNWIND ['a', 'b', 'B', null, 'abc', 'abc1'] AS i
RETURN min(i)
"""
Then the result should be:
| min(i) |
| 'B' |
And no side effects
Scenario: `max()` over integers
Given any graph
When executing query:
"""
UNWIND [1, 2, 0, null, -1] AS x
RETURN max(x)
"""
Then the result should be:
| max(x) |
| 2 |
And no side effects
Scenario: `min()` over integers
Given any graph
When executing query:
"""
UNWIND [1, 2, 0, null, -1] AS x
RETURN min(x)
"""
Then the result should be:
| min(x) |
| -1 |
And no side effects
Scenario: `max()` over floats
Given any graph
When executing query:
"""
UNWIND [1.0, 2.0, 0.5, null] AS x
RETURN max(x)
"""
Then the result should be:
| max(x) |
| 2.0 |
And no side effects
Scenario: `min()` over floats
Given any graph
When executing query:
"""
UNWIND [1.0, 2.0, 0.5, null] AS x
RETURN min(x)
"""
Then the result should be:
| min(x) |
| 0.5 |
And no side effects
Scenario: `max()` over mixed numeric values
Given any graph
When executing query:
"""
UNWIND [1, 2.0, 5, null, 3.2, 0.1] AS x
RETURN max(x)
"""
Then the result should be:
| max(x) |
| 5 |
And no side effects
Scenario: `min()` over mixed numeric values
Given any graph
When executing query:
"""
UNWIND [1, 2.0, 5, null, 3.2, 0.1] AS x
RETURN min(x)
"""
Then the result should be:
| min(x) |
| 0.1 |
And no side effects
Scenario: `max()` over mixed values
Given any graph
When executing query:
"""
UNWIND [1, 'a', null, [1, 2], 0.2, 'b'] AS x
RETURN max(x)
"""
Then the result should be:
| max(x) |
| 1 |
And no side effects
Scenario: `min()` over mixed values
Given any graph
When executing query:
"""
UNWIND [1, 'a', null, [1, 2], 0.2, 'b'] AS x
RETURN min(x)
"""
Then the result should be:
| min(x) |
| [1, 2] |
And no side effects
Scenario: `max()` over list values
Given any graph
When executing query:
"""
UNWIND [[1], [2], [2, 1]] AS x
RETURN max(x)
"""
Then the result should be:
| max(x) |
| [2, 1] |
And no side effects
Scenario: `min()` over list values
Given any graph
When executing query:
"""
UNWIND [[1], [2], [2, 1]] AS x
RETURN min(x)
"""
Then the result should be:
| min(x) |
| [1] |
And no side effects

View File

@ -0,0 +1,473 @@
#
# Copyright (c) 2015-2018 "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: AggregationAcceptance
Scenario: Support multiple divisions in aggregate function
Given an empty graph
And having executed:
"""
UNWIND range(0, 7250) AS i
CREATE ()
"""
When executing query:
"""
MATCH (n)
RETURN count(n) / 60 / 60 AS count
"""
Then the result should be:
| count |
| 2 |
And no side effects
Scenario: Support column renaming for aggregates as well
Given an empty graph
And having executed:
"""
UNWIND range(0, 10) AS i
CREATE ()
"""
When executing query:
"""
MATCH ()
RETURN count(*) AS columnName
"""
Then the result should be:
| columnName |
| 11 |
And no side effects
Scenario: Aggregates inside normal functions
Given an empty graph
And having executed:
"""
UNWIND range(0, 10) AS i
CREATE ()
"""
When executing query:
"""
MATCH (a)
RETURN size(collect(a))
"""
Then the result should be:
| size(collect(a)) |
| 11 |
And no side effects
Scenario: Handle aggregates inside non-aggregate expressions
Given an empty graph
When executing query:
"""
MATCH (a {name: 'Andres'})<-[:FATHER]-(child)
RETURN {foo: a.name='Andres', kids: collect(child.name)}
"""
Then the result should be:
| {foo: a.name='Andres', kids: collect(child.name)} |
And no side effects
Scenario: Count nodes
Given an empty graph
And having executed:
"""
CREATE (a:L), (b1), (b2)
CREATE (a)-[:A]->(b1), (a)-[:A]->(b2)
"""
When executing query:
"""
MATCH (a:L)-[rel]->(b)
RETURN a, count(*)
"""
Then the result should be:
| a | count(*) |
| (:L) | 2 |
And no side effects
Scenario: Sort on aggregate function and normal property
Given an empty graph
And having executed:
"""
CREATE ({division: 'Sweden'})
CREATE ({division: 'Germany'})
CREATE ({division: 'England'})
CREATE ({division: 'Sweden'})
"""
When executing query:
"""
MATCH (n)
RETURN n.division, count(*)
ORDER BY count(*) DESC, n.division ASC
"""
Then the result should be, in order:
| n.division | count(*) |
| 'Sweden' | 2 |
| 'England' | 1 |
| 'Germany' | 1 |
And no side effects
Scenario: Aggregate on property
Given an empty graph
And having executed:
"""
CREATE ({x: 33})
CREATE ({x: 33})
CREATE ({x: 42})
"""
When executing query:
"""
MATCH (n)
RETURN n.x, count(*)
"""
Then the result should be:
| n.x | count(*) |
| 42 | 1 |
| 33 | 2 |
And no side effects
Scenario: Count non-null values
Given an empty graph
And having executed:
"""
CREATE ({y: 'a', x: 33})
CREATE ({y: 'a'})
CREATE ({y: 'b', x: 42})
"""
When executing query:
"""
MATCH (n)
RETURN n.y, count(n.x)
"""
Then the result should be:
| n.y | count(n.x) |
| 'a' | 1 |
| 'b' | 1 |
And no side effects
Scenario: Sum non-null values
Given an empty graph
And having executed:
"""
CREATE ({y: 'a', x: 33})
CREATE ({y: 'a'})
CREATE ({y: 'a', x: 42})
"""
When executing query:
"""
MATCH (n)
RETURN n.y, sum(n.x)
"""
Then the result should be:
| n.y | sum(n.x) |
| 'a' | 75 |
And no side effects
Scenario: Handle aggregation on functions
Given an empty graph
And having executed:
"""
CREATE (a:L), (b1), (b2)
CREATE (a)-[:A]->(b1), (a)-[:A]->(b2)
"""
When executing query:
"""
MATCH p=(a:L)-[*]->(b)
RETURN b, avg(length(p))
"""
Then the result should be:
| b | avg(length(p)) |
| () | 1.0 |
| () | 1.0 |
And no side effects
Scenario: Distinct on unbound node
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a)
RETURN count(DISTINCT a)
"""
Then the result should be:
| count(DISTINCT a) |
| 0 |
And no side effects
Scenario: Distinct on null
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (a)
RETURN count(DISTINCT a.foo)
"""
Then the result should be:
| count(DISTINCT a.foo) |
| 0 |
And no side effects
Scenario: Collect distinct nulls
Given any graph
When executing query:
"""
UNWIND [null, null] AS x
RETURN collect(DISTINCT x) AS c
"""
Then the result should be:
| c |
| [] |
And no side effects
Scenario: Collect distinct values mixed with nulls
Given any graph
When executing query:
"""
UNWIND [null, 1, null] AS x
RETURN collect(DISTINCT x) AS c
"""
Then the result should be:
| c |
| [1] |
And no side effects
Scenario: Aggregate on list values
Given an empty graph
And having executed:
"""
CREATE ({color: ['red']})
CREATE ({color: ['blue']})
CREATE ({color: ['red']})
"""
When executing query:
"""
MATCH (a)
RETURN DISTINCT a.color, count(*)
"""
Then the result should be:
| a.color | count(*) |
| ['red'] | 2 |
| ['blue'] | 1 |
And no side effects
Scenario: Aggregates in aggregates
Given any graph
When executing query:
"""
RETURN count(count(*))
"""
Then a SyntaxError should be raised at compile time: NestedAggregation
Scenario: Aggregates with arithmetics
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH ()
RETURN count(*) * 10 AS c
"""
Then the result should be:
| c |
| 10 |
And no side effects
Scenario: Aggregates ordered by arithmetics
Given an empty graph
And having executed:
"""
CREATE (:A), (:X), (:X)
"""
When executing query:
"""
MATCH (a:A), (b:X)
RETURN count(a) * 10 + count(b) * 5 AS x
ORDER BY x
"""
Then the result should be, in order:
| x |
| 30 |
And no side effects
Scenario: Multiple aggregates on same variable
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
RETURN count(n), collect(n)
"""
Then the result should be:
| count(n) | collect(n) |
| 1 | [()] |
And no side effects
Scenario: Simple counting of nodes
Given an empty graph
And having executed:
"""
UNWIND range(1, 100) AS i
CREATE ()
"""
When executing query:
"""
MATCH ()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 100 |
And no side effects
Scenario: Aggregation of named paths
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B), (c:C), (d:D), (e:E), (f:F)
CREATE (a)-[:R]->(b)
CREATE (c)-[:R]->(d)
CREATE (d)-[:R]->(e)
CREATE (e)-[:R]->(f)
"""
When executing query:
"""
MATCH p = (a)-[*]->(b)
RETURN collect(nodes(p)) AS paths, length(p) AS l
ORDER BY l
"""
Then the result should be, in order:
| paths | l |
| [[(:A), (:B)], [(:C), (:D)], [(:D), (:E)], [(:E), (:F)]] | 1 |
| [[(:C), (:D), (:E)], [(:D), (:E), (:F)]] | 2 |
| [[(:C), (:D), (:E), (:F)]] | 3 |
And no side effects
Scenario: Aggregation with `min()`
Given an empty graph
And having executed:
"""
CREATE (a:T {name: 'a'}), (b:T {name: 'b'}), (c:T {name: 'c'})
CREATE (a)-[:R]->(b)
CREATE (a)-[:R]->(c)
CREATE (c)-[:R]->(b)
"""
When executing query:
"""
MATCH p = (a:T {name: 'a'})-[:R*]->(other:T)
WHERE other <> a
WITH a, other, min(length(p)) AS len
RETURN a.name AS name, collect(other.name) AS others, len
"""
Then the result should be (ignoring element order for lists):
| name | others | len |
| 'a' | ['c', 'b'] | 1 |
And no side effects
Scenario: Handle subexpression in aggregation also occurring as standalone expression with nested aggregation in a literal map
Given an empty graph
And having executed:
"""
CREATE (:A), (:B {prop: 42})
"""
When executing query:
"""
MATCH (a:A), (b:B)
RETURN coalesce(a.prop, b.prop) AS foo,
b.prop AS bar,
{y: count(b)} AS baz
"""
Then the result should be:
| foo | bar | baz |
| 42 | 42 | {y: 1} |
And no side effects
Scenario: Projection during aggregation in WITH before MERGE and after WITH with predicate
Given an empty graph
And having executed:
"""
CREATE (:A {prop: 42})
"""
When executing query:
"""
UNWIND [42] AS props
WITH props WHERE props > 32
WITH DISTINCT props AS p
MERGE (a:A {prop: p})
RETURN a.prop AS prop
"""
Then the result should be:
| prop |
| 42 |
And no side effects
Scenario: No overflow during summation
Given any graph
When executing query:
"""
UNWIND range(1000000, 2000000) AS i
WITH i
LIMIT 3000
RETURN sum(i)
"""
Then the result should be:
| sum(i) |
| 3004498500 |
And no side effects
Scenario: Counting with loops
Given an empty graph
And having executed:
"""
CREATE (a), (a)-[:R]->(a)
"""
When executing query:
"""
MATCH ()-[r]-()
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: `max()` should aggregate strings
Given any graph
When executing query:
"""
UNWIND ['a', 'b', 'B', null, 'abc', 'abc1'] AS i
RETURN max(i)
"""
Then the result should be:
| max(i) |
| 'b' |
And no side effects
Scenario: `min()` should aggregate strings
Given any graph
When executing query:
"""
UNWIND ['a', 'b', 'B', null, 'abc', 'abc1'] AS i
RETURN min(i)
"""
Then the result should be:
| min(i) |
| 'B' |
And no side effects

View File

@ -0,0 +1,68 @@
#
# Copyright (c) 2015-2018 "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: ColumnNameAcceptance
Background:
Given an empty graph
And having executed:
"""
CREATE ()
"""
Scenario: Keeping used expression 1
When executing query:
"""
MATCH (n)
RETURN cOuNt( * )
"""
Then the result should be:
| cOuNt( * ) |
| 1 |
And no side effects
Scenario: Keeping used expression 2
When executing query:
"""
MATCH p = (n)-->(b)
RETURN nOdEs( p )
"""
Then the result should be:
| nOdEs( p ) |
And no side effects
Scenario: Keeping used expression 3
When executing query:
"""
MATCH p = (n)-->(b)
RETURN coUnt( dIstInct p )
"""
Then the result should be:
| coUnt( dIstInct p ) |
| 0 |
And no side effects
Scenario: Keeping used expression 4
When executing query:
"""
MATCH p = (n)-->(b)
RETURN aVg( n.aGe )
"""
Then the result should be:
| aVg( n.aGe ) |
| null |
And no side effects

View File

@ -0,0 +1,87 @@
#
# Copyright (c) 2015-2018 "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: Comparability
Scenario: Comparing strings and integers using > in an AND'd predicate
Given an empty graph
And having executed:
"""
CREATE (root:Root)-[:T]->(:Child {id: 0}),
(root)-[:T]->(:Child {id: 'xx'}),
(root)-[:T]->(:Child)
"""
When executing query:
"""
MATCH (:Root)-->(i:Child)
WHERE exists(i.id) AND i.id > 'x'
RETURN i.id
"""
Then the result should be:
| i.id |
| 'xx' |
And no side effects
Scenario: Comparing strings and integers using > in a OR'd predicate
Given an empty graph
And having executed:
"""
CREATE (root:Root)-[:T]->(:Child {id: 0}),
(root)-[:T]->(:Child {id: 'xx'}),
(root)-[:T]->(:Child)
"""
When executing query:
"""
MATCH (:Root)-->(i:Child)
WHERE NOT exists(i.id) OR i.id > 'x'
RETURN i.id
"""
Then the result should be:
| i.id |
| 'xx' |
| null |
And no side effects
Scenario Outline: Comparing across types yields null, except numbers
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH p = (n)-[r]->()
WITH [n, r, p, '', 1, 3.14, true, null, [], {}] AS types
UNWIND range(0, size(types) - 1) AS i
UNWIND range(0, size(types) - 1) AS j
WITH types[i] AS lhs, types[j] AS rhs
WHERE i <> j
WITH lhs, rhs, lhs <operator> rhs AS result
WHERE result
RETURN lhs, rhs
"""
Then the result should be:
| lhs | rhs |
| <lhs> | <rhs> |
And no side effects
Examples:
| operator | lhs | rhs |
| < | 1 | 3.14 |
| <= | 1 | 3.14 |
| >= | 3.14 | 1 |
| > | 3.14 | 1 |

View File

@ -0,0 +1,208 @@
#
# Copyright (c) 2015-2018 "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: ComparisonOperatorAcceptance
Scenario: Handling numerical ranges 1
Given an empty graph
And having executed:
"""
UNWIND [1, 2, 3] AS i
CREATE ({value: i})
"""
When executing query:
"""
MATCH (n)
WHERE 1 < n.value < 3
RETURN n.value
"""
Then the result should be:
| n.value |
| 2 |
And no side effects
Scenario: Handling numerical ranges 2
Given an empty graph
And having executed:
"""
UNWIND [1, 2, 3] AS i
CREATE ({value: i})
"""
When executing query:
"""
MATCH (n)
WHERE 1 < n.value <= 3
RETURN n.value
"""
Then the result should be:
| n.value |
| 2 |
| 3 |
And no side effects
Scenario: Handling numerical ranges 3
Given an empty graph
And having executed:
"""
UNWIND [1, 2, 3] AS i
CREATE ({value: i})
"""
When executing query:
"""
MATCH (n)
WHERE 1 <= n.value < 3
RETURN n.value
"""
Then the result should be:
| n.value |
| 1 |
| 2 |
And no side effects
Scenario: Handling numerical ranges 4
Given an empty graph
And having executed:
"""
UNWIND [1, 2, 3] AS i
CREATE ({value: i})
"""
When executing query:
"""
MATCH (n)
WHERE 1 <= n.value <= 3
RETURN n.value
"""
Then the result should be:
| n.value |
| 1 |
| 2 |
| 3 |
And no side effects
Scenario: Handling string ranges 1
Given an empty graph
And having executed:
"""
UNWIND ['a', 'b', 'c'] AS c
CREATE ({value: c})
"""
When executing query:
"""
MATCH (n)
WHERE 'a' < n.value < 'c'
RETURN n.value
"""
Then the result should be:
| n.value |
| 'b' |
And no side effects
Scenario: Handling string ranges 2
Given an empty graph
And having executed:
"""
UNWIND ['a', 'b', 'c'] AS c
CREATE ({value: c})
"""
When executing query:
"""
MATCH (n)
WHERE 'a' < n.value <= 'c'
RETURN n.value
"""
Then the result should be:
| n.value |
| 'b' |
| 'c' |
And no side effects
Scenario: Handling string ranges 3
Given an empty graph
And having executed:
"""
UNWIND ['a', 'b', 'c'] AS c
CREATE ({value: c})
"""
When executing query:
"""
MATCH (n)
WHERE 'a' <= n.value < 'c'
RETURN n.value
"""
Then the result should be:
| n.value |
| 'a' |
| 'b' |
And no side effects
Scenario: Handling string ranges 4
Given an empty graph
And having executed:
"""
UNWIND ['a', 'b', 'c'] AS c
CREATE ({value: c})
"""
When executing query:
"""
MATCH (n)
WHERE 'a' <= n.value <= 'c'
RETURN n.value
"""
Then the result should be:
| n.value |
| 'a' |
| 'b' |
| 'c' |
And no side effects
Scenario: Handling empty range
Given an empty graph
And having executed:
"""
CREATE ({value: 3})
"""
When executing query:
"""
MATCH (n)
WHERE 10 < n.value <= 3
RETURN n.value
"""
Then the result should be:
| n.value |
And no side effects
Scenario: Handling long chains of operators
Given an empty graph
And having executed:
"""
CREATE (a:A {prop1: 3, prop2: 4})
CREATE (b:B {prop1: 4, prop2: 5})
CREATE (c:C {prop1: 4, prop2: 4})
CREATE (a)-[:R]->(b)
CREATE (b)-[:R]->(c)
CREATE (c)-[:R]->(a)
"""
When executing query:
"""
MATCH (n)-->(m)
WHERE n.prop1 < m.prop1 = n.prop2 <> m.prop2
RETURN labels(m)
"""
Then the result should be:
| labels(m) |
| ['B'] |
And no side effects

View File

@ -0,0 +1,71 @@
#
# Copyright (c) 2015-2018 "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: Create
Scenario: Creating a node
Given any graph
When executing query:
"""
CREATE ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
Scenario: Creating two nodes
Given any graph
When executing query:
"""
CREATE (), ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
Scenario: Creating two nodes and a relationship
Given any graph
When executing query:
"""
CREATE ()-[:TYPE]->()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: Creating a node with a label
Given an empty graph
When executing query:
"""
CREATE (:Label)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
Scenario: Creating a node with a property
Given any graph
When executing query:
"""
CREATE ({created: true})
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |

View File

@ -0,0 +1,522 @@
#
# Copyright (c) 2015-2018 "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: CreateAcceptance
Scenario: Create a single node with multiple labels
Given an empty graph
When executing query:
"""
CREATE (:A:B:C:D)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +labels | 4 |
Scenario: Combine MATCH and CREATE
Given an empty graph
And having executed:
"""
CREATE (), ()
"""
When executing query:
"""
MATCH ()
CREATE ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
Scenario: Combine MATCH, WITH and CREATE
Given an empty graph
And having executed:
"""
CREATE (), ()
"""
When executing query:
"""
MATCH ()
CREATE ()
WITH *
MATCH ()
CREATE ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 10 |
Scenario: Newly-created nodes not visible to preceding MATCH
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH ()
CREATE ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
Scenario: Create a single node with properties
Given any graph
When executing query:
"""
CREATE (n {prop: 'foo'})
RETURN n.prop AS p
"""
Then the result should be:
| p |
| 'foo' |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Creating a node with null properties should not return those properties
Given any graph
When executing query:
"""
CREATE (n {id: 12, property: null})
RETURN n.id AS id
"""
Then the result should be:
| id |
| 12 |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Creating a relationship with null properties should not return those properties
Given any graph
When executing query:
"""
CREATE ()-[r:X {id: 12, property: null}]->()
RETURN r.id
"""
Then the result should be:
| r.id |
| 12 |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +properties | 1 |
Scenario: Create a simple pattern
Given any graph
When executing query:
"""
CREATE ()-[:R]->()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: Create a self loop
Given an empty graph
When executing query:
"""
CREATE (root:R)-[:LINK]->(root)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +relationships | 1 |
| +labels | 1 |
Scenario: Create a self loop using MATCH
Given an empty graph
And having executed:
"""
CREATE (:R)
"""
When executing query:
"""
MATCH (root:R)
CREATE (root)-[:LINK]->(root)
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
Scenario: Create nodes and relationships
Given any graph
When executing query:
"""
CREATE (a), (b),
(a)-[:R]->(b)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: Create a relationship with a property
Given any graph
When executing query:
"""
CREATE ()-[:R {prop: 42}]->()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +properties | 1 |
Scenario: Create a relationship with the correct direction
Given an empty graph
And having executed:
"""
CREATE (:X)
CREATE (:Y)
"""
When executing query:
"""
MATCH (x:X), (y:Y)
CREATE (x)<-[:TYPE]-(y)
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
When executing control query:
"""
MATCH (x:X)<-[:TYPE]-(y:Y)
RETURN x, y
"""
Then the result should be:
| x | y |
| (:X) | (:Y) |
Scenario: Create a relationship and an end node from a matched starting node
Given an empty graph
And having executed:
"""
CREATE (:Begin)
"""
When executing query:
"""
MATCH (x:Begin)
CREATE (x)-[:TYPE]->(:End)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +relationships | 1 |
| +labels | 1 |
When executing control query:
"""
MATCH (x:Begin)-[:TYPE]->()
RETURN x
"""
Then the result should be:
| x |
| (:Begin) |
Scenario: Create a single node after a WITH
Given an empty graph
And having executed:
"""
CREATE (), ()
"""
When executing query:
"""
MATCH ()
CREATE ()
WITH *
CREATE ()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 4 |
Scenario: Create a relationship with a reversed direction
Given an empty graph
When executing query:
"""
CREATE (:A)<-[:R]-(:B)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +labels | 2 |
When executing control query:
"""
MATCH (a:A)<-[:R]-(b:B)
RETURN a, b
"""
Then the result should be:
| a | b |
| (:A) | (:B) |
Scenario: Create a pattern with multiple hops
Given an empty graph
When executing query:
"""
CREATE (:A)-[:R]->(:B)-[:R]->(:C)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 3 |
| +relationships | 2 |
| +labels | 3 |
When executing control query:
"""
MATCH (a:A)-[:R]->(b:B)-[:R]->(c:C)
RETURN a, b, c
"""
Then the result should be:
| a | b | c |
| (:A) | (:B) | (:C) |
Scenario: Create a pattern with multiple hops in the reverse direction
Given an empty graph
When executing query:
"""
CREATE (:A)<-[:R]-(:B)<-[:R]-(:C)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 3 |
| +relationships | 2 |
| +labels | 3 |
When executing control query:
"""
MATCH (a)<-[:R]-(b)<-[:R]-(c)
RETURN a, b, c
"""
Then the result should be:
| a | b | c |
| (:A) | (:B) | (:C) |
Scenario: Create a pattern with multiple hops in varying directions
Given an empty graph
When executing query:
"""
CREATE (:A)-[:R]->(:B)<-[:R]-(:C)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 3 |
| +relationships | 2 |
| +labels | 3 |
When executing control query:
"""
MATCH (a:A)-[r1:R]->(b:B)<-[r2:R]-(c:C)
RETURN a, b, c
"""
Then the result should be:
| a | b | c |
| (:A) | (:B) | (:C) |
Scenario: Create a pattern with multiple hops with multiple types and varying directions
Given any graph
When executing query:
"""
CREATE ()-[:R1]->()<-[:R2]-()-[:R3]->()
"""
Then the result should be empty
And the side effects should be:
| +nodes | 4 |
| +relationships | 3 |
When executing query:
"""
MATCH ()-[r1:R1]->()<-[r2:R2]-()-[r3:R3]->()
RETURN r1, r2, r3
"""
Then the result should be:
| r1 | r2 | r3 |
| [:R1] | [:R2] | [:R3] |
Scenario: Nodes are not created when aliases are applied to variable names
Given an empty graph
And having executed:
"""
CREATE ({foo: 1})
"""
When executing query:
"""
MATCH (n)
MATCH (m)
WITH n AS a, m AS b
CREATE (a)-[:T]->(b)
RETURN a, b
"""
Then the result should be:
| a | b |
| ({foo: 1}) | ({foo: 1}) |
And the side effects should be:
| +relationships | 1 |
Scenario: Only a single node is created when an alias is applied to a variable name
Given an empty graph
And having executed:
"""
CREATE (:X)
"""
When executing query:
"""
MATCH (n)
WITH n AS a
CREATE (a)-[:T]->()
RETURN a
"""
Then the result should be:
| a |
| (:X) |
And the side effects should be:
| +nodes | 1 |
| +relationships | 1 |
Scenario: Nodes are not created when aliases are applied to variable names multiple times
Given an empty graph
And having executed:
"""
CREATE ({foo: 'A'})
"""
When executing query:
"""
MATCH (n)
MATCH (m)
WITH n AS a, m AS b
CREATE (a)-[:T]->(b)
WITH a AS x, b AS y
CREATE (x)-[:T]->(y)
RETURN x, y
"""
Then the result should be:
| x | y |
| ({foo: 'A'}) | ({foo: 'A'}) |
And the side effects should be:
| +relationships | 2 |
Scenario: Only a single node is created when an alias is applied to a variable name multiple times
Given an empty graph
And having executed:
"""
CREATE ({foo: 5})
"""
When executing query:
"""
MATCH (n)
WITH n AS a
CREATE (a)-[:T]->()
WITH a AS x
CREATE (x)-[:T]->()
RETURN x
"""
Then the result should be:
| x |
| ({foo: 5}) |
And the side effects should be:
| +nodes | 2 |
| +relationships | 2 |
Scenario: A bound node should be recognized after projection with WITH + WITH
Given any graph
When executing query:
"""
CREATE (a)
WITH a
WITH *
CREATE (b)
CREATE (a)<-[:T]-(b)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: A bound node should be recognized after projection with WITH + UNWIND
Given any graph
When executing query:
"""
CREATE (a)
WITH a
UNWIND [0] AS i
CREATE (b)
CREATE (a)<-[:T]-(b)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: A bound node should be recognized after projection with WITH + MERGE node
Given an empty graph
When executing query:
"""
CREATE (a)
WITH a
MERGE ()
CREATE (b)
CREATE (a)<-[:T]-(b)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: A bound node should be recognized after projection with WITH + MERGE pattern
Given an empty graph
When executing query:
"""
CREATE (a)
WITH a
MERGE (x)
MERGE (y)
MERGE (x)-[:T]->(y)
CREATE (b)
CREATE (a)<-[:T]-(b)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 2 |
Scenario: Fail when trying to create using an undirected relationship pattern
Given any graph
When executing query:
"""
CREATE ({id: 2})-[r:KNOWS]-({id: 1})
RETURN r
"""
Then a SyntaxError should be raised at compile time: RequiresDirectedRelationship
Scenario: Creating a pattern with multiple hops and changing directions
Given an empty graph
When executing query:
"""
CREATE (:A)<-[:R1]-(:B)-[:R2]->(:C)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 3 |
| +relationships | 2 |
| +labels | 3 |
When executing control query:
"""
MATCH (a:A)<-[r1:R1]-(b:B)-[r2:R2]->(c:C)
RETURN *
"""
Then the result should be:
| a | b | c | r1 | r2 |
| (:A) | (:B) | (:C) | [:R1] | [:R2] |

View File

@ -0,0 +1,375 @@
#
# Copyright (c) 2015-2018 "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: DeleteAcceptance
Scenario: Delete nodes
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
DELETE n
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
Scenario: Detach delete node
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
DETACH DELETE n
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
Scenario: Delete relationships
Given an empty graph
And having executed:
"""
UNWIND range(0, 2) AS i
CREATE ()-[:R]->()
"""
When executing query:
"""
MATCH ()-[r]-()
DELETE r
"""
Then the result should be empty
And the side effects should be:
| -relationships | 3 |
Scenario: Deleting connected nodes
Given an empty graph
And having executed:
"""
CREATE (x:X)
CREATE (x)-[:R]->()
CREATE (x)-[:R]->()
CREATE (x)-[:R]->()
"""
When executing query:
"""
MATCH (n:X)
DELETE n
"""
Then a ConstraintVerificationFailed should be raised at runtime: DeleteConnectedNode
Scenario: Detach deleting connected nodes and relationships
Given an empty graph
And having executed:
"""
CREATE (x:X)
CREATE (x)-[:R]->()
CREATE (x)-[:R]->()
CREATE (x)-[:R]->()
"""
When executing query:
"""
MATCH (n:X)
DETACH DELETE n
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
| -relationships | 3 |
| -labels | 1 |
Scenario: Detach deleting paths
Given an empty graph
And having executed:
"""
CREATE (x:X), (n1), (n2), (n3)
CREATE (x)-[:R]->(n1)
CREATE (n1)-[:R]->(n2)
CREATE (n2)-[:R]->(n3)
"""
When executing query:
"""
MATCH p = (:X)-->()-->()-->()
DETACH DELETE p
"""
Then the result should be empty
And the side effects should be:
| -nodes | 4 |
| -relationships | 3 |
| -labels | 1 |
Scenario: Undirected expand followed by delete and count
Given an empty graph
And having executed:
"""
CREATE ()-[:R]->()
"""
When executing query:
"""
MATCH (a)-[r]-(b)
DELETE r, a, b
RETURN count(*) AS c
"""
Then the result should be:
| c |
| 2 |
And the side effects should be:
| -nodes | 2 |
| -relationships | 1 |
Scenario: Undirected variable length expand followed by delete and count
Given an empty graph
And having executed:
"""
CREATE (n1), (n2), (n3)
CREATE (n1)-[:R]->(n2)
CREATE (n2)-[:R]->(n3)
"""
When executing query:
"""
MATCH (a)-[*]-(b)
DETACH DELETE a, b
RETURN count(*) AS c
"""
Then the result should be:
| c |
| 6 |
And the side effects should be:
| -nodes | 3 |
| -relationships | 2 |
Scenario: Create and delete in same query
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH ()
CREATE (n)
DELETE n
"""
Then the result should be empty
And no side effects
Scenario: Delete optionally matched relationship
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
OPTIONAL MATCH (n)-[r]-()
DELETE n, r
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
Scenario: Delete on null node
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (n)
DELETE n
"""
Then the result should be empty
And no side effects
Scenario: Detach delete on null node
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (n)
DETACH DELETE n
"""
Then the result should be empty
And no side effects
Scenario: Delete on null path
Given an empty graph
When executing query:
"""
OPTIONAL MATCH p = ()-->()
DETACH DELETE p
"""
Then the result should be empty
And no side effects
Scenario: Delete node from a list
Given an empty graph
And having executed:
"""
CREATE (u:User)
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
"""
And parameters are:
| friendIndex | 1 |
When executing query:
"""
MATCH (:User)-[:FRIEND]->(n)
WITH collect(n) AS friends
DETACH DELETE friends[$friendIndex]
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
| -relationships | 1 |
Scenario: Delete relationship from a list
Given an empty graph
And having executed:
"""
CREATE (u:User)
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
CREATE (u)-[:FRIEND]->()
"""
And parameters are:
| friendIndex | 1 |
When executing query:
"""
MATCH (:User)-[r:FRIEND]->()
WITH collect(r) AS friendships
DETACH DELETE friendships[$friendIndex]
"""
Then the result should be empty
And the side effects should be:
| -relationships | 1 |
Scenario: Delete nodes from a map
Given an empty graph
And having executed:
"""
CREATE (:User), (:User)
"""
When executing query:
"""
MATCH (u:User)
WITH {key: u} AS nodes
DELETE nodes.key
"""
Then the result should be empty
And the side effects should be:
| -nodes | 2 |
| -labels | 1 |
Scenario: Delete relationships from a map
Given an empty graph
And having executed:
"""
CREATE (a:User), (b:User)
CREATE (a)-[:R]->(b)
CREATE (b)-[:R]->(a)
"""
When executing query:
"""
MATCH (:User)-[r]->(:User)
WITH {key: r} AS rels
DELETE rels.key
"""
Then the result should be empty
And the side effects should be:
| -relationships | 2 |
Scenario: Detach delete nodes from nested map/list
Given an empty graph
And having executed:
"""
CREATE (a:User), (b:User)
CREATE (a)-[:R]->(b)
CREATE (b)-[:R]->(a)
"""
When executing query:
"""
MATCH (u:User)
WITH {key: collect(u)} AS nodeMap
DETACH DELETE nodeMap.key[0]
"""
Then the result should be empty
And the side effects should be:
| -nodes | 1 |
| -relationships | 2 |
Scenario: Delete relationships from nested map/list
Given an empty graph
And having executed:
"""
CREATE (a:User), (b:User)
CREATE (a)-[:R]->(b)
CREATE (b)-[:R]->(a)
"""
When executing query:
"""
MATCH (:User)-[r]->(:User)
WITH {key: {key: collect(r)}} AS rels
DELETE rels.key.key[0]
"""
Then the result should be empty
And the side effects should be:
| -relationships | 1 |
Scenario: Delete paths from nested map/list
Given an empty graph
And having executed:
"""
CREATE (a:User), (b:User)
CREATE (a)-[:R]->(b)
CREATE (b)-[:R]->(a)
"""
When executing query:
"""
MATCH p = (:User)-[r]->(:User)
WITH {key: collect(p)} AS pathColls
DELETE pathColls.key[0], pathColls.key[1]
"""
Then the result should be empty
And the side effects should be:
| -nodes | 2 |
| -relationships | 2 |
| -labels | 1 |
Scenario: Delete relationship with bidirectional matching
Given an empty graph
And having executed:
"""
CREATE ()-[:T {id: 42}]->()
"""
When executing query:
"""
MATCH p = ()-[r:T]-()
WHERE r.id = 42
DELETE r
"""
Then the result should be empty
And the side effects should be:
| -relationships | 1 |
| -properties | 1 |

View File

@ -0,0 +1,111 @@
#
# Copyright (c) 2015-2018 "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: EqualsAcceptance
Scenario: Number-typed integer comparison
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
WITH collect([0, 0.0]) AS numbers
UNWIND numbers AS arr
WITH arr[0] AS expected
MATCH (n) WHERE toInteger(n.id) = expected
RETURN n
"""
Then the result should be:
| n |
| ({id: 0}) |
And no side effects
Scenario: Number-typed float comparison
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
WITH collect([0.5, 0]) AS numbers
UNWIND numbers AS arr
WITH arr[0] AS expected
MATCH (n) WHERE toInteger(n.id) = expected
RETURN n
"""
Then the result should be:
| n |
And no side effects
Scenario: Any-typed string comparison
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
WITH collect(['0', 0]) AS things
UNWIND things AS arr
WITH arr[0] AS expected
MATCH (n) WHERE toInteger(n.id) = expected
RETURN n
"""
Then the result should be:
| n |
And no side effects
Scenario: Comparing nodes to nodes
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (a)
WITH a
MATCH (b)
WHERE a = b
RETURN count(b)
"""
Then the result should be:
| count(b) |
| 1 |
And no side effects
Scenario: Comparing relationships to relationships
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH ()-[a]->()
WITH a
MATCH ()-[b]->()
WHERE a = b
RETURN count(b)
"""
Then the result should be:
| count(b) |
| 1 |
And no side effects

View File

@ -0,0 +1,248 @@
#
# Copyright (c) 2015-2018 "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: ExpressionAcceptance
Background:
Given any graph
Scenario: IN should work with nested list subscripting
When executing query:
"""
WITH [[1, 2, 3]] AS list
RETURN 3 IN list[0] AS r
"""
Then the result should be:
| r |
| true |
And no side effects
Scenario: IN should work with nested literal list subscripting
When executing query:
"""
RETURN 3 IN [[1, 2, 3]][0] AS r
"""
Then the result should be:
| r |
| true |
And no side effects
Scenario: IN should work with list slices
When executing query:
"""
WITH [1, 2, 3] AS list
RETURN 3 IN list[0..1] AS r
"""
Then the result should be:
| r |
| false |
And no side effects
Scenario: IN should work with literal list slices
When executing query:
"""
RETURN 3 IN [1, 2, 3][0..1] AS r
"""
Then the result should be:
| r |
| false |
And no side effects
Scenario: Execute n[0]
When executing query:
"""
RETURN [1, 2, 3][0] AS value
"""
Then the result should be:
| value |
| 1 |
And no side effects
Scenario: Execute n['name'] in read queries
And having executed:
"""
CREATE ({name: 'Apa'})
"""
When executing query:
"""
MATCH (n {name: 'Apa'})
RETURN n['nam' + 'e'] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Execute n['name'] in update queries
When executing query:
"""
CREATE (n {name: 'Apa'})
RETURN n['nam' + 'e'] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Use dynamic property lookup based on parameters when there is no type information
And parameters are:
| expr | {name: 'Apa'} |
| idx | 'name' |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Use dynamic property lookup based on parameters when there is lhs type information
And parameters are:
| idx | 'name' |
When executing query:
"""
CREATE (n {name: 'Apa'})
RETURN n[$idx] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Use dynamic property lookup based on parameters when there is rhs type information
And parameters are:
| expr | {name: 'Apa'} |
| idx | 'name' |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[toString(idx)] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Use collection lookup based on parameters when there is no type information
And parameters are:
| expr | ['Apa'] |
| idx | 0 |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Use collection lookup based on parameters when there is lhs type information
And parameters are:
| idx | 0 |
When executing query:
"""
WITH ['Apa'] AS expr
RETURN expr[$idx] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Use collection lookup based on parameters when there is rhs type information
And parameters are:
| expr | ['Apa'] |
| idx | 0 |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[toInteger(idx)] AS value
"""
Then the result should be:
| value |
| 'Apa' |
And no side effects
Scenario: Fail at runtime when attempting to index with an Int into a Map
And parameters are:
| expr | {name: 'Apa'} |
| idx | 0 |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx]
"""
Then a TypeError should be raised at runtime: MapElementAccessByNonString
Scenario: Fail at runtime when trying to index into a map with a non-string
And parameters are:
| expr | {name: 'Apa'} |
| idx | 12.3 |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx]
"""
Then a TypeError should be raised at runtime: MapElementAccessByNonString
Scenario: Fail at runtime when attempting to index with a String into a Collection
And parameters are:
| expr | ['Apa'] |
| idx | 'name' |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx]
"""
Then a TypeError should be raised at runtime: ListElementAccessByNonInteger
Scenario: Fail at runtime when trying to index into a list with a list
And parameters are:
| expr | ['Apa'] |
| idx | ['Apa'] |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx]
"""
Then a TypeError should be raised at runtime: ListElementAccessByNonInteger
Scenario: Fail at compile time when attempting to index with a non-integer into a list
When executing query:
"""
WITH [1, 2, 3, 4, 5] AS list, 3.14 AS idx
RETURN list[idx]
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Fail at runtime when trying to index something which is not a map or collection
And parameters are:
| expr | 100 |
| idx | 0 |
When executing query:
"""
WITH $expr AS expr, $idx AS idx
RETURN expr[idx]
"""
Then a TypeError should be raised at runtime: InvalidElementAccess

View File

@ -0,0 +1,485 @@
#
# Copyright (c) 2015-2018 "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: FunctionsAcceptance
Scenario: Run coalesce
Given an empty graph
And having executed:
"""
CREATE ({name: 'Emil Eifrem', title: 'CEO'}), ({name: 'Nobody'})
"""
When executing query:
"""
MATCH (a)
RETURN coalesce(a.title, a.name)
"""
Then the result should be:
| coalesce(a.title, a.name) |
| 'CEO' |
| 'Nobody' |
And no side effects
Scenario: Functions should return null if they get path containing unbound
Given any graph
When executing query:
"""
WITH null AS a
OPTIONAL MATCH p = (a)-[r]->()
RETURN length(nodes(p)), type(r), nodes(p), relationships(p)
"""
Then the result should be:
| length(nodes(p)) | type(r) | nodes(p) | relationships(p) |
| null | null | null | null |
And no side effects
Scenario: `split()`
Given any graph
When executing query:
"""
UNWIND split('one1two', '1') AS item
RETURN count(item) AS item
"""
Then the result should be:
| item |
| 2 |
And no side effects
Scenario: `properties()` on a node
Given an empty graph
And having executed:
"""
CREATE (n:Person {name: 'Popeye', level: 9001})
"""
When executing query:
"""
MATCH (p:Person)
RETURN properties(p) AS m
"""
Then the result should be:
| m |
| {name: 'Popeye', level: 9001} |
And no side effects
Scenario: `properties()` on a relationship
Given an empty graph
And having executed:
"""
CREATE (n)-[:R {name: 'Popeye', level: 9001}]->(n)
"""
When executing query:
"""
MATCH ()-[r:R]->()
RETURN properties(r) AS m
"""
Then the result should be:
| m |
| {name: 'Popeye', level: 9001} |
And no side effects
Scenario: `properties()` on a map
Given any graph
When executing query:
"""
RETURN properties({name: 'Popeye', level: 9001}) AS m
"""
Then the result should be:
| m |
| {name: 'Popeye', level: 9001} |
And no side effects
Scenario: `properties()` failing on an integer literal
Given any graph
When executing query:
"""
RETURN properties(1)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: `properties()` failing on a string literal
Given any graph
When executing query:
"""
RETURN properties('Cypher')
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: `properties()` failing on a list of booleans
Given any graph
When executing query:
"""
RETURN properties([true, false])
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: `properties()` on null
Given any graph
When executing query:
"""
RETURN properties(null)
"""
Then the result should be:
| properties(null) |
| null |
And no side effects
Scenario: `reverse()`
Given any graph
When executing query:
"""
RETURN reverse('raksO')
"""
Then the result should be:
| reverse('raksO') |
| 'Oskar' |
And no side effects
Scenario: `exists()` with dynamic property lookup
Given an empty graph
And having executed:
"""
CREATE (:Person {prop: 'foo'}),
(:Person)
"""
When executing query:
"""
MATCH (n:Person)
WHERE exists(n['prop'])
RETURN n
"""
Then the result should be:
| n |
| (:Person {prop: 'foo'}) |
And no side effects
Scenario Outline: `exists()` with literal maps
Given any graph
When executing query:
"""
WITH <map> AS map
RETURN exists(map.name) AS result
"""
Then the result should be:
| result |
| <result> |
And no side effects
Examples:
| map | result |
| {name: 'Mats', name2: 'Pontus'} | true |
| {name: null} | false |
| {notName: 0, notName2: null} | false |
Scenario Outline: IS NOT NULL with literal maps
Given any graph
When executing query:
"""
WITH <map> AS map
RETURN map.name IS NOT NULL
"""
Then the result should be:
| map.name IS NOT NULL |
| <result> |
And no side effects
Examples:
| map | result |
| {name: 'Mats', name2: 'Pontus'} | true |
| {name: null} | false |
| {notName: 0, notName2: null} | false |
Scenario Outline: `percentileDisc()`
Given an empty graph
And having executed:
"""
CREATE ({prop: 10.0}),
({prop: 20.0}),
({prop: 30.0})
"""
And parameters are:
| percentile | <p> |
When executing query:
"""
MATCH (n)
RETURN percentileDisc(n.prop, $percentile) AS p
"""
Then the result should be:
| p |
| <result> |
And no side effects
Examples:
| p | result |
| 0.0 | 10.0 |
| 0.5 | 20.0 |
| 1.0 | 30.0 |
Scenario Outline: `percentileCont()`
Given an empty graph
And having executed:
"""
CREATE ({prop: 10.0}),
({prop: 20.0}),
({prop: 30.0})
"""
And parameters are:
| percentile | <p> |
When executing query:
"""
MATCH (n)
RETURN percentileCont(n.prop, $percentile) AS p
"""
Then the result should be:
| p |
| <result> |
And no side effects
Examples:
| p | result |
| 0.0 | 10.0 |
| 0.5 | 20.0 |
| 1.0 | 30.0 |
Scenario Outline: `percentileCont()` failing on bad arguments
Given an empty graph
And having executed:
"""
CREATE ({prop: 10.0})
"""
And parameters are:
| param | <percentile> |
When executing query:
"""
MATCH (n)
RETURN percentileCont(n.prop, $param)
"""
Then a ArgumentError should be raised at runtime: NumberOutOfRange
Examples:
| percentile |
| 1000 |
| -1 |
| 1.1 |
Scenario Outline: `percentileDisc()` failing on bad arguments
Given an empty graph
And having executed:
"""
CREATE ({prop: 10.0})
"""
And parameters are:
| param | <percentile> |
When executing query:
"""
MATCH (n)
RETURN percentileDisc(n.prop, $param)
"""
Then a ArgumentError should be raised at runtime: NumberOutOfRange
Examples:
| percentile |
| 1000 |
| -1 |
| 1.1 |
Scenario: `percentileDisc()` failing in more involved query
Given an empty graph
And having executed:
"""
UNWIND range(0, 10) AS i
CREATE (s:S)
WITH s, i
UNWIND range(0, i) AS j
CREATE (s)-[:REL]->()
"""
When executing query:
"""
MATCH (n:S)
WITH n, size([(n)-->() | 1]) AS deg
WHERE deg > 2
WITH deg
LIMIT 100
RETURN percentileDisc(0.90, deg), deg
"""
Then a ArgumentError should be raised at runtime: NumberOutOfRange
Scenario: `type()`
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH ()-[r]->()
RETURN type(r)
"""
Then the result should be:
| type(r) |
| 'T' |
And no side effects
Scenario: `type()` on two relationships
Given an empty graph
And having executed:
"""
CREATE ()-[:T1]->()-[:T2]->()
"""
When executing query:
"""
MATCH ()-[r1]->()-[r2]->()
RETURN type(r1), type(r2)
"""
Then the result should be:
| type(r1) | type(r2) |
| 'T1' | 'T2' |
And no side effects
Scenario: `type()` on null relationship
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (a)
OPTIONAL MATCH (a)-[r:NOT_THERE]->()
RETURN type(r)
"""
Then the result should be:
| type(r) |
| null |
And no side effects
Scenario: `type()` on mixed null and non-null relationships
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH (a)
OPTIONAL MATCH (a)-[r:T]->()
RETURN type(r)
"""
Then the result should be:
| type(r) |
| 'T' |
| null |
And no side effects
Scenario: `type()` handling Any type
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH (a)-[r]->()
WITH [r, 1] AS list
RETURN type(list[0])
"""
Then the result should be:
| type(list[0]) |
| 'T' |
And no side effects
Scenario Outline: `type()` failing on invalid arguments
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH p = (n)-[r:T]->()
RETURN [x IN [r, <invalid>] | type(x) ] AS list
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Examples:
| invalid |
| 0 |
| 1.0 |
| true |
| '' |
| [] |
Scenario: `labels()` should accept type Any
Given an empty graph
And having executed:
"""
CREATE (:Foo), (:Foo:Bar)
"""
When executing query:
"""
MATCH (a)
WITH [a, 1] AS list
RETURN labels(list[0]) AS l
"""
Then the result should be (ignoring element order for lists):
| l |
| ['Foo'] |
| ['Foo', 'Bar'] |
And no side effects
Scenario: `labels()` failing on a path
Given an empty graph
And having executed:
"""
CREATE (:Foo), (:Foo:Bar)
"""
When executing query:
"""
MATCH p = (a)
RETURN labels(p) AS l
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: `labels()` failing on invalid arguments
Given an empty graph
And having executed:
"""
CREATE (:Foo), (:Foo:Bar)
"""
When executing query:
"""
MATCH (a)
WITH [a, 1] AS list
RETURN labels(list[1]) AS l
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Scenario: `exists()` is case insensitive
Given an empty graph
And having executed:
"""
CREATE (a:X {prop: 42}), (:X)
"""
When executing query:
"""
MATCH (n:X)
RETURN n, EXIsTS(n.prop) AS b
"""
Then the result should be:
| n | b |
| (:X {prop: 42}) | true |
| (:X) | false |
And no side effects

View File

@ -0,0 +1,66 @@
#
# Copyright (c) 2015-2018 "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: ValueHashJoinAcceptance
Scenario: Find friends of others
Given an empty graph
And having executed:
"""
CREATE (:A {id: 1}),
(:A {id: 2}),
(:B {id: 2}),
(:B {id: 3})
"""
When executing query:
"""
MATCH (a:A), (b:B)
WHERE a.id = b.id
RETURN a, b
"""
Then the result should be:
| a | b |
| (:A {id: 2}) | (:B {id: 2}) |
And no side effects
Scenario: Should only join when matching
Given an empty graph
And having executed:
"""
UNWIND range(0, 1000) AS i
CREATE (:A {id: i})
MERGE (:B {id: i % 10})
"""
When executing query:
"""
MATCH (a:A), (b:B)
WHERE a.id = b.id
RETURN a, b
"""
Then the result should be:
| a | b |
| (:A {id: 0}) | (:B {id: 0}) |
| (:A {id: 1}) | (:B {id: 1}) |
| (:A {id: 2}) | (:B {id: 2}) |
| (:A {id: 3}) | (:B {id: 3}) |
| (:A {id: 4}) | (:B {id: 4}) |
| (:A {id: 5}) | (:B {id: 5}) |
| (:A {id: 6}) | (:B {id: 6}) |
| (:A {id: 7}) | (:B {id: 7}) |
| (:A {id: 8}) | (:B {id: 8}) |
| (:A {id: 9}) | (:B {id: 9}) |
And no side effects

View File

@ -0,0 +1,163 @@
#
# Copyright (c) 2015-2018 "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: KeysAcceptance
Scenario: Using `keys()` on a single node, non-empty result
Given an empty graph
And having executed:
"""
CREATE ({name: 'Andres', surname: 'Lopez'})
"""
When executing query:
"""
MATCH (n)
UNWIND keys(n) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
| 'name' |
| 'surname' |
And no side effects
Scenario: Using `keys()` on multiple nodes, non-empty result
Given an empty graph
And having executed:
"""
CREATE ({name: 'Andres', surname: 'Lopez'}),
({otherName: 'Andres', otherSurname: 'Lopez'})
"""
When executing query:
"""
MATCH (n)
UNWIND keys(n) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
| 'name' |
| 'surname' |
| 'otherName' |
| 'otherSurname' |
And no side effects
Scenario: Using `keys()` on a single node, empty result
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
UNWIND keys(n) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
And no side effects
Scenario: Using `keys()` on an optionally matched node
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
OPTIONAL MATCH (n)
UNWIND keys(n) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
And no side effects
Scenario: Using `keys()` on a relationship, non-empty result
Given an empty graph
And having executed:
"""
CREATE ()-[:KNOWS {level: 'bad', year: '2015'}]->()
"""
When executing query:
"""
MATCH ()-[r:KNOWS]-()
UNWIND keys(r) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
| 'level' |
| 'year' |
And no side effects
Scenario: Using `keys()` on a relationship, empty result
Given an empty graph
And having executed:
"""
CREATE ()-[:KNOWS]->()
"""
When executing query:
"""
MATCH ()-[r:KNOWS]-()
UNWIND keys(r) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
And no side effects
Scenario: Using `keys()` on an optionally matched relationship
Given an empty graph
And having executed:
"""
CREATE ()-[:KNOWS]->()
"""
When executing query:
"""
OPTIONAL MATCH ()-[r:KNOWS]-()
UNWIND keys(r) AS x
RETURN DISTINCT x AS theProps
"""
Then the result should be:
| theProps |
And no side effects
Scenario: Using `keys()` on a literal map
Given any graph
When executing query:
"""
RETURN keys({name: 'Alice', age: 38, address: {city: 'London', residential: true}}) AS k
"""
Then the result should be:
| k |
| ['name', 'age', 'address'] |
And no side effects
Scenario: Using `keys()` on a parameter map
Given any graph
And parameters are:
| param | {name: 'Alice', age: 38, address: {city: 'London', residential: true}} |
When executing query:
"""
RETURN keys($param) AS k
"""
Then the result should be (ignoring element order for lists):
| k |
| ['address', 'name', 'age'] |
And no side effects

View File

@ -0,0 +1,247 @@
#
# Copyright (c) 2015-2018 "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: LabelsAcceptance
Background:
Given an empty graph
Scenario: Adding a single label
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n:Foo
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo'] |
And the side effects should be:
| +labels | 1 |
Scenario: Ignore space before colon
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n :Foo
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo'] |
And the side effects should be:
| +labels | 1 |
Scenario: Adding multiple labels
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n:Foo:Bar
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo', 'Bar'] |
And the side effects should be:
| +labels | 2 |
Scenario: Ignoring intermediate whitespace 1
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n :Foo :Bar
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo', 'Bar'] |
And the side effects should be:
| +labels | 2 |
Scenario: Ignoring intermediate whitespace 2
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n :Foo:Bar
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo', 'Bar'] |
And the side effects should be:
| +labels | 2 |
Scenario: Creating node without label
When executing query:
"""
CREATE (node)
RETURN labels(node)
"""
Then the result should be:
| labels(node) |
| [] |
And the side effects should be:
| +nodes | 1 |
Scenario: Creating node with two labels
When executing query:
"""
CREATE (node:Foo:Bar {name: 'Mattias'})
RETURN labels(node)
"""
Then the result should be:
| labels(node) |
| ['Foo', 'Bar'] |
And the side effects should be:
| +nodes | 1 |
| +labels | 2 |
| +properties | 1 |
Scenario: Ignore space when creating node with labels
When executing query:
"""
CREATE (node :Foo:Bar)
RETURN labels(node)
"""
Then the result should be:
| labels(node) |
| ['Foo', 'Bar'] |
And the side effects should be:
| +nodes | 1 |
| +labels | 2 |
Scenario: Create node with label in pattern
When executing query:
"""
CREATE (n:Person)-[:OWNS]->(:Dog)
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Person'] |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +labels | 2 |
Scenario: Fail when adding a new label predicate on a node that is already bound 1
When executing query:
"""
CREATE (n:Foo)-[:T1]->(),
(n:Bar)-[:T2]->()
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Fail when adding new label predicate on a node that is already bound 2
When executing query:
"""
CREATE ()<-[:T2]-(n:Foo),
(n:Bar)<-[:T1]-()
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Fail when adding new label predicate on a node that is already bound 3
When executing query:
"""
CREATE (n:Foo)
CREATE (n:Bar)-[:OWNS]->(:Dog)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Fail when adding new label predicate on a node that is already bound 4
When executing query:
"""
CREATE (n {})
CREATE (n:Bar)-[:OWNS]->(:Dog)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Fail when adding new label predicate on a node that is already bound 5
When executing query:
"""
CREATE (n:Foo)
CREATE (n {})-[:OWNS]->(:Dog)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Using `labels()` in return clauses
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| [] |
And no side effects
Scenario: Removing a label
And having executed:
"""
CREATE (:Foo:Bar)
"""
When executing query:
"""
MATCH (n)
REMOVE n:Foo
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Bar'] |
And the side effects should be:
| -labels | 1 |
Scenario: Removing a non-existent label
And having executed:
"""
CREATE (:Foo)
"""
When executing query:
"""
MATCH (n)
REMOVE n:Bar
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['Foo'] |
And no side effects

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
#
# Copyright (c) 2015-2018 "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: LargeIntegerEquality
Background:
Given an empty graph
And having executed:
"""
CREATE (:Label {id: 4611686018427387905})
"""
Scenario: Does not lose precision
When executing query:
"""
MATCH (p:Label)
RETURN p.id
"""
Then the result should be:
| p.id |
| 4611686018427387905 |
And no side effects
Scenario: Handling inlined equality of large integer
When executing query:
"""
MATCH (p:Label {id: 4611686018427387905})
RETURN p.id
"""
Then the result should be:
| p.id |
| 4611686018427387905 |
And no side effects
Scenario: Handling explicit equality of large integer
When executing query:
"""
MATCH (p:Label)
WHERE p.id = 4611686018427387905
RETURN p.id
"""
Then the result should be:
| p.id |
| 4611686018427387905 |
And no side effects
Scenario: Handling inlined equality of large integer, non-equal values
When executing query:
"""
MATCH (p:Label {id : 4611686018427387900})
RETURN p.id
"""
Then the result should be:
| p.id |
And no side effects
Scenario: Handling explicit equality of large integer, non-equal values
When executing query:
"""
MATCH (p:Label)
WHERE p.id = 4611686018427387900
RETURN p.id
"""
Then the result should be:
| p.id |
And no side effects

View File

@ -0,0 +1,75 @@
#
# Copyright (c) 2015-2018 "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: ListComprehension
Scenario: Returning a list comprehension
Given an empty graph
And having executed:
"""
CREATE (a:A)
CREATE (a)-[:T]->(:B),
(a)-[:T]->(:C)
"""
When executing query:
"""
MATCH p = (n)-->()
RETURN [x IN collect(p) | head(nodes(x))] AS p
"""
Then the result should be:
| p |
| [(:A), (:A)] |
And no side effects
Scenario: Using a list comprehension in a WITH
Given an empty graph
And having executed:
"""
CREATE (a:A)
CREATE (a)-[:T]->(:B),
(a)-[:T]->(:C)
"""
When executing query:
"""
MATCH p = (n:A)-->()
WITH [x IN collect(p) | head(nodes(x))] AS p, count(n) AS c
RETURN p, c
"""
Then the result should be:
| p | c |
| [(:A), (:A)] | 2 |
And no side effects
Scenario: Using a list comprehension in a WHERE
Given an empty graph
And having executed:
"""
CREATE (a:A {prop: 'c'})
CREATE (a)-[:T]->(:B),
(a)-[:T]->(:C)
"""
When executing query:
"""
MATCH (n)-->(b)
WHERE n.prop IN [x IN labels(b) | lower(x)]
RETURN b
"""
Then the result should be:
| b |
| (:C) |
And no side effects

View File

@ -0,0 +1,131 @@
#
# Copyright (c) 2015-2018 "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: Literals
Background:
Given any graph
Scenario: Return an integer
When executing query:
"""
RETURN 1 AS literal
"""
Then the result should be:
| literal |
| 1 |
And no side effects
Scenario: Return a float
When executing query:
"""
RETURN 1.0 AS literal
"""
Then the result should be:
| literal |
| 1.0 |
And no side effects
Scenario: Return a float in exponent form
When executing query:
"""
RETURN -1e-9 AS literal
"""
Then the result should be:
| literal |
| -.000000001 |
And no side effects
Scenario: Return a boolean
When executing query:
"""
RETURN true AS literal
"""
Then the result should be:
| literal |
| true |
And no side effects
Scenario: Return a single-quoted string
When executing query:
"""
RETURN '' AS literal
"""
Then the result should be:
| literal |
| '' |
And no side effects
Scenario: Return a double-quoted string
When executing query:
"""
RETURN "" AS literal
"""
Then the result should be:
| literal |
| '' |
And no side effects
Scenario: Return null
When executing query:
"""
RETURN null AS literal
"""
Then the result should be:
| literal |
| null |
And no side effects
Scenario: Return an empty list
When executing query:
"""
RETURN [] AS literal
"""
Then the result should be:
| literal |
| [] |
And no side effects
Scenario: Return a nonempty list
When executing query:
"""
RETURN [0, 1, 2] AS literal
"""
Then the result should be:
| literal |
| [0, 1, 2] |
And no side effects
Scenario: Return an empty map
When executing query:
"""
RETURN {} AS literal
"""
Then the result should be:
| literal |
| {} |
And no side effects
Scenario: Return a nonempty map
When executing query:
"""
RETURN {k1: 0, k2: 'string'} AS literal
"""
Then the result should be:
| literal |
| {k1: 0, k2: 'string'} |
And no side effects

View File

@ -0,0 +1,552 @@
#
# Copyright (c) 2015-2018 "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: MatchAcceptance
Scenario: Path query should return results in written order
Given an empty graph
And having executed:
"""
CREATE (:Label1)<-[:TYPE]-(:Label2)
"""
When executing query:
"""
MATCH p = (a:Label1)<--(:Label2)
RETURN p
"""
Then the result should be:
| p |
| <(:Label1)<-[:TYPE]-(:Label2)> |
And no side effects
Scenario: Longer path query should return results in written order
Given an empty graph
And having executed:
"""
CREATE (:Label1)<-[:T1]-(:Label2)-[:T2]->(:Label3)
"""
When executing query:
"""
MATCH p = (a:Label1)<--(:Label2)--()
RETURN p
"""
Then the result should be:
| p |
| <(:Label1)<-[:T1]-(:Label2)-[:T2]->(:Label3)> |
And no side effects
Scenario: Use multiple MATCH clauses to do a Cartesian product
Given an empty graph
And having executed:
"""
CREATE ({value: 1}),
({value: 2}),
({value: 3})
"""
When executing query:
"""
MATCH (n), (m)
RETURN n.value AS n, m.value AS m
"""
Then the result should be:
| n | m |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 3 |
| 3 | 1 |
| 3 | 2 |
And no side effects
Scenario: Use params in pattern matching predicates
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T {foo: 'bar'}]->(:B {name: 'me'})
"""
And parameters are:
| param | 'bar' |
When executing query:
"""
MATCH (a)-[r]->(b)
WHERE r.foo = $param
RETURN b
"""
Then the result should be:
| b |
| (:B {name: 'me'}) |
And no side effects
Scenario: Filter out based on node prop name
Given an empty graph
And having executed:
"""
CREATE ({name: 'Someone'})<-[:X]-()-[:X]->({name: 'Andres'})
"""
When executing query:
"""
MATCH ()-[rel:X]-(a)
WHERE a.name = 'Andres'
RETURN a
"""
Then the result should be:
| a |
| ({name: 'Andres'}) |
And no side effects
Scenario: Honour the column name for RETURN items
Given an empty graph
And having executed:
"""
CREATE ({name: 'Someone'})
"""
When executing query:
"""
MATCH (a)
WITH a.name AS a
RETURN a
"""
Then the result should be:
| a |
| 'Someone' |
And no side effects
Scenario: Filter based on rel prop name
Given an empty graph
And having executed:
"""
CREATE (:A)<-[:KNOWS {name: 'monkey'}]-()-[:KNOWS {name: 'woot'}]->(:B)
"""
When executing query:
"""
MATCH (node)-[r:KNOWS]->(a)
WHERE r.name = 'monkey'
RETURN a
"""
Then the result should be:
| a |
| (:A) |
And no side effects
Scenario: Cope with shadowed variables
Given an empty graph
And having executed:
"""
CREATE ({value: 1, name: 'King Kong'}),
({value: 2, name: 'Ann Darrow'})
"""
When executing query:
"""
MATCH (n)
WITH n.name AS n
RETURN n
"""
Then the result should be:
| n |
| 'Ann Darrow' |
| 'King Kong' |
And no side effects
Scenario: Get neighbours
Given an empty graph
And having executed:
"""
CREATE (a:A {value: 1})-[:KNOWS]->(b:B {value: 2})
"""
When executing query:
"""
MATCH (n1)-[rel:KNOWS]->(n2)
RETURN n1, n2
"""
Then the result should be:
| n1 | n2 |
| (:A {value: 1}) | (:B {value: 2}) |
And no side effects
Scenario: Get two related nodes
Given an empty graph
And having executed:
"""
CREATE (a:A {value: 1}),
(a)-[:KNOWS]->(b:B {value: 2}),
(a)-[:KNOWS]->(c:C {value: 3})
"""
When executing query:
"""
MATCH ()-[rel:KNOWS]->(x)
RETURN x
"""
Then the result should be:
| x |
| (:B {value: 2}) |
| (:C {value: 3}) |
And no side effects
Scenario: Get related to related to
Given an empty graph
And having executed:
"""
CREATE (a:A {value: 1})-[:KNOWS]->(b:B {value: 2})-[:FRIEND]->(c:C {value: 3})
"""
When executing query:
"""
MATCH (n)-->(a)-->(b)
RETURN b
"""
Then the result should be:
| b |
| (:C {value: 3}) |
And no side effects
Scenario: Handle comparison between node properties
Given an empty graph
And having executed:
"""
CREATE (a:A {animal: 'monkey'}),
(b:B {animal: 'cow'}),
(c:C {animal: 'monkey'}),
(d:D {animal: 'cow'}),
(a)-[:KNOWS]->(b),
(a)-[:KNOWS]->(c),
(d)-[:KNOWS]->(b),
(d)-[:KNOWS]->(c)
"""
When executing query:
"""
MATCH (n)-[rel]->(x)
WHERE n.animal = x.animal
RETURN n, x
"""
Then the result should be:
| n | x |
| (:A {animal: 'monkey'}) | (:C {animal: 'monkey'}) |
| (:D {animal: 'cow'}) | (:B {animal: 'cow'}) |
And no side effects
Scenario: Return two subgraphs with bound undirected relationship
Given an empty graph
And having executed:
"""
CREATE (a:A {value: 1})-[:REL {name: 'r'}]->(b:B {value: 2})
"""
When executing query:
"""
MATCH (a)-[r {name: 'r'}]-(b)
RETURN a, b
"""
Then the result should be:
| a | b |
| (:B {value: 2}) | (:A {value: 1}) |
| (:A {value: 1}) | (:B {value: 2}) |
And no side effects
Scenario: Return two subgraphs with bound undirected relationship and optional relationship
Given an empty graph
And having executed:
"""
CREATE (a:A {value: 1})-[:REL {name: 'r1'}]->(b:B {value: 2})-[:REL {name: 'r2'}]->(c:C {value: 3})
"""
When executing query:
"""
MATCH (a)-[r {name: 'r1'}]-(b)
OPTIONAL MATCH (b)-[r2]-(c)
WHERE r <> r2
RETURN a, b, c
"""
Then the result should be:
| a | b | c |
| (:A {value: 1}) | (:B {value: 2}) | (:C {value: 3}) |
| (:B {value: 2}) | (:A {value: 1}) | null |
And no side effects
Scenario: Rel type function works as expected
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'}),
(b:B {name: 'B'}),
(c:C {name: 'C'}),
(a)-[:KNOWS]->(b),
(a)-[:HATES]->(c)
"""
When executing query:
"""
MATCH (n {name: 'A'})-[r]->(x)
WHERE type(r) = 'KNOWS'
RETURN x
"""
Then the result should be:
| x |
| (:B {name: 'B'}) |
And no side effects
Scenario: Walk alternative relationships
Given an empty graph
And having executed:
"""
CREATE (a {name: 'A'}),
(b {name: 'B'}),
(c {name: 'C'}),
(a)-[:KNOWS]->(b),
(a)-[:HATES]->(c),
(a)-[:WONDERS]->(c)
"""
When executing query:
"""
MATCH (n)-[r]->(x)
WHERE type(r) = 'KNOWS' OR type(r) = 'HATES'
RETURN r
"""
Then the result should be:
| r |
| [:KNOWS] |
| [:HATES] |
And no side effects
Scenario: Handle OR in the WHERE clause
Given an empty graph
And having executed:
"""
CREATE (a:A {p1: 12}),
(b:B {p2: 13}),
(c:C)
"""
When executing query:
"""
MATCH (n)
WHERE n.p1 = 12 OR n.p2 = 13
RETURN n
"""
Then the result should be:
| n |
| (:A {p1: 12}) |
| (:B {p2: 13}) |
And no side effects
Scenario: Return a simple path
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS]->(b:B {name: 'B'})
"""
When executing query:
"""
MATCH p = (a {name: 'A'})-->(b)
RETURN p
"""
Then the result should be:
| p |
| <(:A {name: 'A'})-[:KNOWS]->(:B {name: 'B'})> |
And no side effects
Scenario: Return a three node path
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS]->(b:B {name: 'B'})-[:KNOWS]->(c:C {name: 'C'})
"""
When executing query:
"""
MATCH p = (a {name: 'A'})-[rel1]->(b)-[rel2]->(c)
RETURN p
"""
Then the result should be:
| p |
| <(:A {name: 'A'})-[:KNOWS]->(:B {name: 'B'})-[:KNOWS]->(:C {name: 'C'})> |
And no side effects
Scenario: Do not return anything because path length does not match
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS]->(b:B {name: 'B'})
"""
When executing query:
"""
MATCH p = (n)-->(x)
WHERE length(p) = 10
RETURN x
"""
Then the result should be:
| x |
And no side effects
Scenario: Pass the path length test
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS]->(b:B {name: 'B'})
"""
When executing query:
"""
MATCH p = (n)-->(x)
WHERE length(p) = 1
RETURN x
"""
Then the result should be:
| x |
| (:B {name: 'B'}) |
And no side effects
Scenario: Return relationships by fetching them from the path - starting from the end
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(e:End)
"""
When executing query:
"""
MATCH p = (a)-[:REL*2..2]->(b:End)
RETURN relationships(p)
"""
Then the result should be:
| relationships(p) |
| [[:REL {value: 1}], [:REL {value: 2}]] |
And no side effects
Scenario: Return relationships by fetching them from the path
Given an empty graph
And having executed:
"""
CREATE (s:Start)-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(c:C)
"""
When executing query:
"""
MATCH p = (a:Start)-[:REL*2..2]->(b)
RETURN relationships(p)
"""
Then the result should be:
| relationships(p) |
| [[:REL {value: 1}], [:REL {value: 2}]] |
And no side effects
Scenario: Return relationships by collecting them as a list - directed, one way
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(e:End)
"""
When executing query:
"""
MATCH (a)-[r:REL*2..2]->(b:End)
RETURN r
"""
Then the result should be:
| r |
| [[:REL {value: 1}], [:REL {value: 2}]] |
And no side effects
Scenario: Return relationships by collecting them as a list - undirected, starting from two extremes
Given an empty graph
And having executed:
"""
CREATE (a:End)-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(c:End)
"""
When executing query:
"""
MATCH (a)-[r:REL*2..2]-(b:End)
RETURN r
"""
Then the result should be:
| r |
| [[:REL {value:1}], [:REL {value:2}]] |
| [[:REL {value:2}], [:REL {value:1}]] |
And no side effects
Scenario: Return relationships by collecting them as a list - undirected, starting from one extreme
Given an empty graph
And having executed:
"""
CREATE (s:Start)-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(c:C)
"""
When executing query:
"""
MATCH (a:Start)-[r:REL*2..2]-(b)
RETURN r
"""
Then the result should be:
| r |
| [[:REL {value: 1}], [:REL {value: 2}]] |
And no side effects
Scenario: Return a var length path
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS {value: 1}]->(b:B {name: 'B'})-[:KNOWS {value: 2}]->(c:C {name: 'C'})
"""
When executing query:
"""
MATCH p = (n {name: 'A'})-[:KNOWS*1..2]->(x)
RETURN p
"""
Then the result should be:
| p |
| <(:A {name: 'A'})-[:KNOWS {value: 1}]->(:B {name: 'B'})> |
| <(:A {name: 'A'})-[:KNOWS {value: 1}]->(:B {name: 'B'})-[:KNOWS {value: 2}]->(:C {name: 'C'})> |
And no side effects
Scenario: Return a var length path of length zero
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:REL]->(b:B)
"""
When executing query:
"""
MATCH p = (a)-[*0..1]->(b)
RETURN a, b, length(p) AS l
"""
Then the result should be:
| a | b | l |
| (:A) | (:A) | 0 |
| (:B) | (:B) | 0 |
| (:A) | (:B) | 1 |
And no side effects
Scenario: Return a named var length path of length zero
Given an empty graph
And having executed:
"""
CREATE (a:A {name: 'A'})-[:KNOWS]->(b:B {name: 'B'})-[:FRIEND]->(c:C {name: 'C'})
"""
When executing query:
"""
MATCH p = (a {name: 'A'})-[:KNOWS*0..1]->(b)-[:FRIEND*0..1]->(c)
RETURN p
"""
Then the result should be:
| p |
| <(:A {name: 'A'})> |
| <(:A {name: 'A'})-[:KNOWS]->(:B {name: 'B'})> |
| <(:A {name: 'A'})-[:KNOWS]->(:B {name: 'B'})-[:FRIEND]->(:C {name: 'C'})> |
And no side effects
Scenario: Accept skip zero
Given any graph
When executing query:
"""
MATCH (n)
WHERE 1 = 0
RETURN n SKIP 0
"""
Then the result should be:
| n |
And no side effects

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,338 @@
#
# Copyright (c) 2015-2018 "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: MatchingSelfRelationships
Scenario: Undirected match in self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (a)-[r]-(b)
RETURN a, r, b
"""
Then the result should be:
| a | r | b |
| (:A) | [:LOOP] | (:A) |
And no side effects
Scenario: Undirected match in self-relationship graph, count
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH ()--()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Undirected match of self-relationship in self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)-[r]-(n)
RETURN n, r
"""
Then the result should be:
| n | r |
| (:A) | [:LOOP] |
And no side effects
Scenario: Undirected match of self-relationship in self-relationship graph, count
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)--(n)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Undirected match on simple relationship graph
Given an empty graph
And having executed:
"""
CREATE (:A)-[:LOOP]->(:B)
"""
When executing query:
"""
MATCH (a)-[r]-(b)
RETURN a, r, b
"""
Then the result should be:
| a | r | b |
| (:A) | [:LOOP] | (:B) |
| (:B) | [:LOOP] | (:A) |
And no side effects
Scenario: Undirected match on simple relationship graph, count
Given an empty graph
And having executed:
"""
CREATE (:A)-[:LOOP]->(:B)
"""
When executing query:
"""
MATCH ()--()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 2 |
And no side effects
Scenario: Directed match on self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (a)-[r]->(b)
RETURN a, r, b
"""
Then the result should be:
| a | r | b |
| (:A) | [:LOOP] | (:A) |
And no side effects
Scenario: Directed match on self-relationship graph, count
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH ()-->()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Directed match of self-relationship on self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)-[r]->(n)
RETURN n, r
"""
Then the result should be:
| n | r |
| (:A) | [:LOOP] |
And no side effects
Scenario: Directed match of self-relationship on self-relationship graph, count
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)-->(n)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Counting undirected self-relationships in self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)-[r]-(n)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: Counting distinct undirected self-relationships in self-relationship graph
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a)
"""
When executing query:
"""
MATCH (n)-[r]-(n)
RETURN count(DISTINCT r)
"""
Then the result should be:
| count(DISTINCT r) |
| 1 |
And no side effects
Scenario: Directed match of a simple relationship
Given an empty graph
And having executed:
"""
CREATE (:A)-[:LOOP]->(:B)
"""
When executing query:
"""
MATCH (a)-[r]->(b)
RETURN a, r, b
"""
Then the result should be:
| a | r | b |
| (:A) | [:LOOP] | (:B) |
And no side effects
Scenario: Directed match of a simple relationship, count
Given an empty graph
And having executed:
"""
CREATE (:A)-[:LOOP]->(:B)
"""
When executing query:
"""
MATCH ()-->()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Counting directed self-relationships
Given an empty graph
And having executed:
"""
CREATE (a:A)-[:LOOP]->(a),
()-[:T]->()
"""
When executing query:
"""
MATCH (n)-[r]->(n)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: Mixing directed and undirected pattern parts with self-relationship, simple
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T1]->(l:Looper),
(l)-[:LOOP]->(l),
(l)-[:T2]->(:B)
"""
When executing query:
"""
MATCH (x:A)-[r1]->(y)-[r2]-(z)
RETURN x, r1, y, r2, z
"""
Then the result should be:
| x | r1 | y | r2 | z |
| (:A) | [:T1] | (:Looper) | [:LOOP] | (:Looper) |
| (:A) | [:T1] | (:Looper) | [:T2] | (:B) |
And no side effects
Scenario: Mixing directed and undirected pattern parts with self-relationship, count
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T1]->(l:Looper),
(l)-[:LOOP]->(l),
(l)-[:T2]->(:B)
"""
When executing query:
"""
MATCH (:A)-->()--()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 2 |
And no side effects
Scenario: Mixing directed and undirected pattern parts with self-relationship, undirected
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T1]->(l:Looper),
(l)-[:LOOP]->(l),
(l)-[:T2]->(:B)
"""
When executing query:
"""
MATCH (x)-[r1]-(y)-[r2]-(z)
RETURN x, r1, y, r2, z
"""
Then the result should be:
| x | r1 | y | r2 | z |
| (:A) | [:T1] | (:Looper) | [:LOOP] | (:Looper) |
| (:A) | [:T1] | (:Looper) | [:T2] | (:B) |
| (:Looper) | [:LOOP] | (:Looper) | [:T1] | (:A) |
| (:Looper) | [:LOOP] | (:Looper) | [:T2] | (:B) |
| (:B) | [:T2] | (:Looper) | [:LOOP] | (:Looper) |
| (:B) | [:T2] | (:Looper) | [:T1] | (:A) |
And no side effects
Scenario: Mixing directed and undirected pattern parts with self-relationship, undirected count
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T1]->(l:Looper),
(l)-[:LOOP]->(l),
(l)-[:T2]->(:B)
"""
When executing query:
"""
MATCH ()-[]-()-[]-()
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 6 |
And no side effects

View File

@ -0,0 +1,154 @@
#
# Copyright (c) 2015-2018 "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: MergeIntoAcceptance
Background:
Given an empty graph
And having executed:
"""
CREATE (:A {name: 'A'}), (:B {name: 'B'})
"""
Scenario: Updating one property with ON CREATE
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r.name = 'foo'
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be:
| keyValue |
| ['name->foo'] |
Scenario: Null-setting one property with ON CREATE
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r.name = null
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be:
| keyValue |
| [] |
Scenario: Copying properties from node with ON CREATE
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r = a
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be:
| keyValue |
| ['name->A'] |
Scenario: Copying properties from node with ON MATCH
And having executed:
"""
MATCH (a:A), (b:B)
CREATE (a)-[:TYPE {foo: 'bar'}]->(b)
"""
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON MATCH SET r = a
"""
Then the result should be empty
And the side effects should be:
| +properties | 1 |
| -properties | 1 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be:
| keyValue |
| ['name->A'] |
Scenario: Copying properties from literal map with ON CREATE
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r += {foo: 'bar', bar: 'baz'}
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
| +properties | 2 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be (ignoring element order for lists):
| keyValue |
| ['foo->bar', 'bar->baz'] |
Scenario: Copying properties from literal map with ON MATCH
And having executed:
"""
MATCH (a:A), (b:B)
CREATE (a)-[:TYPE {foo: 'bar'}]->(b)
"""
When executing query:
"""
MATCH (a {name: 'A'}), (b {name: 'B'})
MERGE (a)-[r:TYPE]->(b)
ON MATCH SET r += {foo: 'baz', bar: 'baz'}
"""
Then the result should be empty
And the side effects should be:
| +properties | 2 |
| -properties | 1 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be (ignoring element order for lists):
| keyValue |
| ['foo->baz', 'bar->baz'] |

View File

@ -0,0 +1,483 @@
#
# Copyright (c) 2015-2018 "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: MergeNodeAcceptance
Scenario: Merge node when no nodes exist
Given an empty graph
When executing query:
"""
MERGE (a)
RETURN count(*) AS n
"""
Then the result should be:
| n |
| 1 |
And the side effects should be:
| +nodes | 1 |
Scenario: Merge node with label
Given an empty graph
When executing query:
"""
MERGE (a:Label)
RETURN labels(a)
"""
Then the result should be:
| labels(a) |
| ['Label'] |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
Scenario: Merge node with label add label on create
Given an empty graph
When executing query:
"""
MERGE (a:Label)
ON CREATE SET a:Foo
RETURN labels(a)
"""
Then the result should be:
| labels(a) |
| ['Label', 'Foo'] |
And the side effects should be:
| +nodes | 1 |
| +labels | 2 |
Scenario: Merge node with label add property on create
Given an empty graph
When executing query:
"""
MERGE (a:Label)
ON CREATE SET a.prop = 42
RETURN a.prop
"""
Then the result should be:
| a.prop |
| 42 |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Merge node with label when it exists
Given an empty graph
And having executed:
"""
CREATE (:Label {id: 1})
"""
When executing query:
"""
MERGE (a:Label)
RETURN a.id
"""
Then the result should be:
| a.id |
| 1 |
And no side effects
Scenario: Merge node should create when it doesn't match, properties
Given an empty graph
And having executed:
"""
CREATE ({prop: 42})
"""
When executing query:
"""
MERGE (a {prop: 43})
RETURN a.prop
"""
Then the result should be:
| a.prop |
| 43 |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Merge node should create when it doesn't match, properties and label
Given an empty graph
And having executed:
"""
CREATE (:Label {prop: 42})
"""
When executing query:
"""
MERGE (a:Label {prop: 43})
RETURN a.prop
"""
Then the result should be:
| a.prop |
| 43 |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Merge node with prop and label
Given an empty graph
And having executed:
"""
CREATE (:Label {prop: 42})
"""
When executing query:
"""
MERGE (a:Label {prop: 42})
RETURN a.prop
"""
Then the result should be:
| a.prop |
| 42 |
And no side effects
Scenario: Merge node with label add label on match when it exists
Given an empty graph
And having executed:
"""
CREATE (:Label)
"""
When executing query:
"""
MERGE (a:Label)
ON MATCH SET a:Foo
RETURN labels(a)
"""
Then the result should be:
| labels(a) |
| ['Label', 'Foo'] |
And the side effects should be:
| +labels | 1 |
Scenario: Merge node with label add property on update when it exists
Given an empty graph
And having executed:
"""
CREATE (:Label)
"""
When executing query:
"""
MERGE (a:Label)
ON CREATE SET a.prop = 42
RETURN a.prop
"""
Then the result should be:
| a.prop |
| null |
And no side effects
Scenario: Merge node and set property on match
Given an empty graph
And having executed:
"""
CREATE (:Label)
"""
When executing query:
"""
MERGE (a:Label)
ON MATCH SET a.prop = 42
RETURN a.prop
"""
Then the result should be:
| a.prop |
| 42 |
And the side effects should be:
| +properties | 1 |
Scenario: Should work when finding multiple elements
Given an empty graph
When executing query:
"""
CREATE (:X)
CREATE (:X)
MERGE (:X)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +labels | 1 |
Scenario: Should handle argument properly
Given an empty graph
And having executed:
"""
CREATE ({x: 42}),
({x: 'not42'})
"""
When executing query:
"""
WITH 42 AS x
MERGE (c:N {x: x})
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Should handle arguments properly with only write clauses
Given an empty graph
When executing query:
"""
CREATE (a {p: 1})
MERGE ({v: a.p})
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +properties | 2 |
Scenario: Should be able to merge using property from match
Given an empty graph
And having executed:
"""
CREATE (:Person {name: 'A', bornIn: 'New York'})
CREATE (:Person {name: 'B', bornIn: 'Ohio'})
CREATE (:Person {name: 'C', bornIn: 'New Jersey'})
CREATE (:Person {name: 'D', bornIn: 'New York'})
CREATE (:Person {name: 'E', bornIn: 'Ohio'})
CREATE (:Person {name: 'F', bornIn: 'New Jersey'})
"""
When executing query:
"""
MATCH (person:Person)
MERGE (city:City {name: person.bornIn})
"""
Then the result should be empty
And the side effects should be:
| +nodes | 3 |
| +labels | 1 |
| +properties | 3 |
Scenario: Should be able to use properties from match in ON CREATE
Given an empty graph
And having executed:
"""
CREATE (:Person {bornIn: 'New York'}),
(:Person {bornIn: 'Ohio'})
"""
When executing query:
"""
MATCH (person:Person)
MERGE (city:City)
ON CREATE SET city.name = person.bornIn
RETURN person.bornIn
"""
Then the result should be:
| person.bornIn |
| 'New York' |
| 'Ohio' |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Should be able to use properties from match in ON MATCH
Given an empty graph
And having executed:
"""
CREATE (:Person {bornIn: 'New York'}),
(:Person {bornIn: 'Ohio'})
"""
When executing query:
"""
MATCH (person:Person)
MERGE (city:City)
ON MATCH SET city.name = person.bornIn
RETURN person.bornIn
"""
Then the result should be:
| person.bornIn |
| 'New York' |
| 'Ohio' |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Should be able to use properties from match in ON MATCH and ON CREATE
Given an empty graph
And having executed:
"""
CREATE (:Person {bornIn: 'New York'}),
(:Person {bornIn: 'Ohio'})
"""
When executing query:
"""
MATCH (person:Person)
MERGE (city:City)
ON MATCH SET city.name = person.bornIn
ON CREATE SET city.name = person.bornIn
RETURN person.bornIn
"""
Then the result should be:
| person.bornIn |
| 'New York' |
| 'Ohio' |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Should be able to set labels on match
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MERGE (a)
ON MATCH SET a:L
"""
Then the result should be empty
And the side effects should be:
| +labels | 1 |
Scenario: Should be able to set labels on match and on create
Given an empty graph
And having executed:
"""
CREATE (), ()
"""
When executing query:
"""
MATCH ()
MERGE (a:L)
ON MATCH SET a:M1
ON CREATE SET a:M2
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +labels | 3 |
Scenario: Should support updates while merging
Given an empty graph
And having executed:
"""
UNWIND [0, 1, 2] AS x
UNWIND [0, 1, 2] AS y
CREATE ({x: x, y: y})
"""
When executing query:
"""
MATCH (foo)
WITH foo.x AS x, foo.y AS y
MERGE (:N {x: x, y: y + 1})
MERGE (:N {x: x, y: y})
MERGE (:N {x: x + 1, y: y})
RETURN x, y
"""
Then the result should be:
| x | y |
| 0 | 0 |
| 0 | 1 |
| 0 | 2 |
| 1 | 0 |
| 1 | 1 |
| 1 | 2 |
| 2 | 0 |
| 2 | 1 |
| 2 | 2 |
And the side effects should be:
| +nodes | 15 |
| +labels | 1 |
| +properties | 30 |
Scenario: Merge must properly handle multiple labels
Given an empty graph
And having executed:
"""
CREATE (:L:A {prop: 42})
"""
When executing query:
"""
MERGE (test:L:B {prop: 42})
RETURN labels(test) AS labels
"""
Then the result should be:
| labels |
| ['L', 'B'] |
And the side effects should be:
| +nodes | 1 |
| +labels | 1 |
| +properties | 1 |
Scenario: Merge followed by multiple creates
Given an empty graph
When executing query:
"""
MERGE (t:T {id: 42})
CREATE (f:R)
CREATE (t)-[:REL]->(f)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +labels | 2 |
| +properties | 1 |
Scenario: Unwind combined with merge
Given an empty graph
When executing query:
"""
UNWIND [1, 2, 3, 4] AS int
MERGE (n {id: int})
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 4 |
And the side effects should be:
| +nodes | 4 |
| +properties | 4 |
Scenario: Merges should not be able to match on deleted nodes
Given an empty graph
And having executed:
"""
CREATE (:A {value: 1}),
(:A {value: 2})
"""
When executing query:
"""
MATCH (a:A)
DELETE a
MERGE (a2:A)
RETURN a2.value
"""
Then the result should be:
| a2.value |
| null |
| null |
And the side effects should be:
| +nodes | 1 |
| -nodes | 2 |
| -properties | 2 |
Scenario: ON CREATE on created nodes
Given an empty graph
When executing query:
"""
MERGE (b)
ON CREATE SET b.created = 1
"""
Then the result should be empty
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |

View File

@ -0,0 +1,599 @@
#
# Copyright (c) 2015-2018 "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: MergeRelationshipAcceptance
Scenario: Creating a relationship
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And the side effects should be:
| +relationships | 1 |
Scenario: Matching a relationship
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:TYPE]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: Matching two relationships
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:TYPE]->(b)
CREATE (a)-[:TYPE]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 2 |
And no side effects
Scenario: Filtering relationships
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:TYPE {name: 'r1'}]->(b)
CREATE (a)-[:TYPE {name: 'r2'}]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE {name: 'r2'}]->(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: Creating relationship when all matches filtered out
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:TYPE {name: 'r1'}]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE {name: 'r2'}]->(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
Scenario: Matching incoming relationship
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (b)-[:TYPE]->(a)
CREATE (a)-[:TYPE]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)<-[r:TYPE]-(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And no side effects
Scenario: Creating relationship with property
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE {name: 'Lola'}]->(b)
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
Scenario: Using ON CREATE on a node
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[:KNOWS]->(b)
ON CREATE SET b.created = 1
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
Scenario: Using ON CREATE on a relationship
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r.name = 'Lola'
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And the side effects should be:
| +relationships | 1 |
| +properties | 1 |
Scenario: Using ON MATCH on created node
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[:KNOWS]->(b)
ON MATCH SET b.created = 1
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
Scenario: Using ON MATCH on created relationship
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:KNOWS]->(b)
ON MATCH SET r.created = 1
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
Scenario: Using ON MATCH on a relationship
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:TYPE]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
ON MATCH SET r.name = 'Lola'
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 1 |
And the side effects should be:
| +properties | 1 |
Scenario: Using ON CREATE and ON MATCH
Given an empty graph
And having executed:
"""
CREATE (a:A {id: 1}), (b:B {id: 2})
CREATE (a)-[:TYPE]->(b)
CREATE (:A {id: 3}), (:B {id: 4})
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:TYPE]->(b)
ON CREATE SET r.name = 'Lola'
ON MATCH SET r.name = 'RUN'
RETURN count(r)
"""
Then the result should be:
| count(r) |
| 4 |
And the side effects should be:
| +relationships | 3 |
| +properties | 4 |
Scenario: Creating relationship using merged nodes
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
"""
When executing query:
"""
MERGE (a:A)
MERGE (b:B)
MERGE (a)-[:FOO]->(b)
"""
Then the result should be empty
And the side effects should be:
| +relationships | 1 |
Scenario: Mixing MERGE with CREATE
Given an empty graph
When executing query:
"""
CREATE (a:A), (b:B)
MERGE (a)-[:KNOWS]->(b)
CREATE (b)-[:KNOWS]->(c:C)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And the side effects should be:
| +nodes | 3 |
| +relationships | 2 |
| +labels | 3 |
Scenario: Introduce named paths 1
Given an empty graph
When executing query:
"""
MERGE (a {x: 1})
MERGE (b {x: 2})
MERGE p = (a)-[:R]->(b)
RETURN p
"""
Then the result should be:
| p |
| <({x: 1})-[:R]->({x: 2})> |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +properties | 2 |
Scenario: Introduce named paths 2
Given an empty graph
When executing query:
"""
MERGE p = (a {x: 1})
RETURN p
"""
Then the result should be:
| p |
| <({x: 1})> |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Use outgoing direction when unspecified
Given an empty graph
When executing query:
"""
CREATE (a {id: 2}), (b {id: 1})
MERGE (a)-[r:KNOWS]-(b)
RETURN startNode(r).id AS s, endNode(r).id AS e
"""
Then the result should be:
| s | e |
| 2 | 1 |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +properties | 2 |
Scenario: Match outgoing relationship when direction unspecified
Given an empty graph
And having executed:
"""
CREATE (a {id: 1}), (b {id: 2})
CREATE (a)-[:KNOWS]->(b)
"""
When executing query:
"""
MATCH (a {id: 2}), (b {id: 1})
MERGE (a)-[r:KNOWS]-(b)
RETURN r
"""
Then the result should be:
| r |
| [:KNOWS] |
And no side effects
Scenario: Match both incoming and outgoing relationships when direction unspecified
Given an empty graph
And having executed:
"""
CREATE (a {id: 2}), (b {id: 1}), (c {id: 1}), (d {id: 2})
CREATE (a)-[:KNOWS {name: 'ab'}]->(b)
CREATE (c)-[:KNOWS {name: 'cd'}]->(d)
"""
When executing query:
"""
MATCH (a {id: 2})--(b {id: 1})
MERGE (a)-[r:KNOWS]-(b)
RETURN r
"""
Then the result should be:
| r |
| [:KNOWS {name: 'ab'}] |
| [:KNOWS {name: 'cd'}] |
And no side effects
Scenario: Fail when imposing new predicates on a variable that is already bound
Given any graph
When executing query:
"""
CREATE (a:Foo)
MERGE (a)-[r:KNOWS]->(a:Bar)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Using list properties via variable
Given an empty graph
When executing query:
"""
CREATE (a:Foo), (b:Bar)
WITH a, b
UNWIND ['a,b', 'a,b'] AS str
WITH a, b, split(str, ',') AS roles
MERGE (a)-[r:FB {foobar: roles}]->(b)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 2 |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
| +labels | 2 |
| +properties | 1 |
Scenario: Matching using list property
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:T {prop: [42, 43]}]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
MERGE (a)-[r:T {prop: [42, 43]}]->(b)
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Using bound variables from other updating clause
Given an empty graph
When executing query:
"""
CREATE (a), (b)
MERGE (a)-[:X]->(b)
RETURN count(a)
"""
Then the result should be:
| count(a) |
| 1 |
And the side effects should be:
| +nodes | 2 |
| +relationships | 1 |
Scenario: UNWIND with multiple merges
Given an empty graph
When executing query:
"""
UNWIND ['Keanu Reeves', 'Hugo Weaving', 'Carrie-Anne Moss', 'Laurence Fishburne'] AS actor
MERGE (m:Movie {name: 'The Matrix'})
MERGE (p:Person {name: actor})
MERGE (p)-[:ACTED_IN]->(m)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 5 |
| +relationships | 4 |
| +labels | 2 |
| +properties | 5 |
Scenario: Do not match on deleted entities
Given an empty graph
And having executed:
"""
CREATE (a:A)
CREATE (b1:B {value: 0}), (b2:B {value: 1})
CREATE (c1:C), (c2:C)
CREATE (a)-[:REL]->(b1),
(a)-[:REL]->(b2),
(b1)-[:REL]->(c1),
(b2)-[:REL]->(c2)
"""
When executing query:
"""
MATCH (a:A)-[ab]->(b:B)-[bc]->(c:C)
DELETE ab, bc, b, c
MERGE (newB:B {value: 1})
MERGE (a)-[:REL]->(newB)
MERGE (newC:C)
MERGE (newB)-[:REL]->(newC)
"""
Then the result should be empty
And the side effects should be:
| +nodes | 2 |
| -nodes | 4 |
| +relationships | 2 |
| -relationships | 4 |
| +properties | 1 |
| -properties | 2 |
Scenario: Do not match on deleted relationships
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:T {name: 'rel1'}]->(b),
(a)-[:T {name: 'rel2'}]->(b)
"""
When executing query:
"""
MATCH (a)-[t:T]->(b)
DELETE t
MERGE (a)-[t2:T {name: 'rel3'}]->(b)
RETURN t2.name
"""
Then the result should be:
| t2.name |
| 'rel3' |
| 'rel3' |
And the side effects should be:
| +relationships | 1 |
| -relationships | 2 |
| +properties | 1 |
| -properties | 2 |
Scenario: Aliasing of existing nodes 1
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
MATCH (n)
MATCH (m)
WITH n AS a, m AS b
MERGE (a)-[r:T]->(b)
RETURN a.id AS a, b.id AS b
"""
Then the result should be:
| a | b |
| 0 | 0 |
And the side effects should be:
| +relationships | 1 |
Scenario: Aliasing of existing nodes 2
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
MATCH (n)
WITH n AS a, n AS b
MERGE (a)-[r:T]->(b)
RETURN a.id AS a
"""
Then the result should be:
| a |
| 0 |
And the side effects should be:
| +relationships | 1 |
Scenario: Double aliasing of existing nodes 1
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
MATCH (n)
MATCH (m)
WITH n AS a, m AS b
MERGE (a)-[:T]->(b)
WITH a AS x, b AS y
MERGE (a)
MERGE (b)
MERGE (a)-[:T]->(b)
RETURN x.id AS x, y.id AS y
"""
Then the result should be:
| x | y |
| 0 | 0 |
And the side effects should be:
| +relationships | 1 |
Scenario: Double aliasing of existing nodes 2
Given an empty graph
And having executed:
"""
CREATE ({id: 0})
"""
When executing query:
"""
MATCH (n)
WITH n AS a
MERGE (c)
MERGE (a)-[:T]->(c)
WITH a AS x
MERGE (c)
MERGE (x)-[:T]->(c)
RETURN x.id AS x
"""
Then the result should be:
| x |
| 0 |
And the side effects should be:
| +relationships | 1 |

View File

@ -0,0 +1,210 @@
#
# Copyright (c) 2015-2018 "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: MiscellaneousErrorAcceptance
Background:
Given any graph
Scenario: Failing on incorrect unicode literal
When executing query:
"""
RETURN '\uH'
"""
Then a SyntaxError should be raised at compile time: InvalidUnicodeLiteral
Scenario: Failing on merging relationship with null property
When executing query:
"""
CREATE (a), (b)
MERGE (a)-[r:X {p: null}]->(b)
"""
Then a SemanticError should be raised at compile time: MergeReadOwnWrites
Scenario: Failing on merging node with null property
When executing query:
"""
MERGE ({p: null})
"""
Then a SemanticError should be raised at compile time: MergeReadOwnWrites
Scenario: Failing on aggregation in WHERE
When executing query:
"""
MATCH (a)
WHERE count(a) > 10
RETURN a
"""
Then a SyntaxError should be raised at compile time: InvalidAggregation
Scenario: Failing on aggregation in ORDER BY after RETURN
When executing query:
"""
MATCH (n)
RETURN n.prop1
ORDER BY max(n.prop2)
"""
Then a SyntaxError should be raised at compile time: InvalidAggregation
Scenario: Failing on aggregation in ORDER BY after WITH
When executing query:
"""
MATCH (n)
WITH n.prop1 AS foo
ORDER BY max(n.prop2)
RETURN foo AS foo
"""
Then a SyntaxError should be raised at compile time: InvalidAggregation
Scenario: Failing when not aliasing expressions in WITH
When executing query:
"""
MATCH (a)
WITH a, count(*)
RETURN a
"""
Then a SyntaxError should be raised at compile time: NoExpressionAlias
Scenario: Failing when using undefined variable in pattern
When executing query:
"""
MATCH (a)
CREATE (a)-[:KNOWS]->(b {name: missing})
RETURN b
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using undefined variable in SET
When executing query:
"""
MATCH (a)
SET a.name = missing
RETURN a
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using undefined variable in DELETE
When executing query:
"""
MATCH (a)
DELETE x
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using a variable that is already bound in CREATE
When executing query:
"""
MATCH (a)
CREATE (a {name: 'foo'})
RETURN a
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using a path variable that is already bound
When executing query:
"""
MATCH p = (a)
WITH p, a
MATCH p = (a)-->(b)
RETURN a
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using a list as a node
When executing query:
"""
MATCH (n)
WITH [n] AS users
MATCH (users)-->(messages)
RETURN messages
"""
Then a SyntaxError should be raised at compile time: VariableTypeConflict
Scenario: Failing when using a variable length relationship as a single relationship
When executing query:
"""
MATCH (n)
MATCH (n)-[r*]->()
WHERE r.foo = 'apa'
RETURN r
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when UNION has different columns
When executing query:
"""
RETURN 1 AS a
UNION
RETURN 2 AS b
"""
Then a SyntaxError should be raised at compile time: DifferentColumnsInUnion
Scenario: Failing when mixing UNION and UNION ALL
When executing query:
"""
RETURN 1 AS a
UNION
RETURN 2 AS a
UNION ALL
RETURN 3 AS a
"""
Then a SyntaxError should be raised at compile time: InvalidClauseComposition
Scenario: Failing when creating without direction
When executing query:
"""
CREATE (a)-[:FOO]-(b)
"""
Then a SyntaxError should be raised at compile time: RequiresDirectedRelationship
Scenario: Failing when creating with two directions
When executing query:
"""
CREATE (a)<-[:FOO]->(b)
"""
Then a SyntaxError should be raised at compile time: RequiresDirectedRelationship
Scenario: Failing when deleting a label
When executing query:
"""
MATCH (n)
DELETE n:Person
"""
Then a SyntaxError should be raised at compile time: InvalidDelete
Scenario: Failing when setting a list of maps as a property
When executing query:
"""
CREATE (a)
SET a.foo = [{x: 1}]
"""
Then a TypeError should be raised at compile time: InvalidPropertyType
Scenario: Failing when multiple columns have the same name
When executing query:
"""
RETURN 1 AS a, 2 AS a
"""
Then a SyntaxError should be raised at compile time: ColumnNameConflict
Scenario: Failing when using RETURN * without variables in scope
When executing query:
"""
MATCH ()
RETURN *
"""
Then a SyntaxError should be raised at compile time: NoVariablesInScope

View File

@ -0,0 +1,122 @@
#
# Copyright (c) 2015-2018 "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: NullAcceptance
Scenario: Ignore null when setting property
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
SET a.prop = 42
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when removing property
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
REMOVE a.prop
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when setting properties using an appending map
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
SET a += {prop: 42}
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when setting properties using an overriding map
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
SET a = {prop: 42}
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when setting label
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
SET a:L
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when removing label
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
REMOVE a:L
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when deleting node
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:DoesNotExist)
DELETE a
RETURN a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Ignore null when deleting relationship
Given an empty graph
When executing query:
"""
OPTIONAL MATCH ()-[r:DoesNotExist]-()
DELETE r
RETURN r
"""
Then the result should be:
| r |
| null |
And no side effects

View File

@ -0,0 +1,74 @@
#
# Copyright (c) 2015-2018 "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: OptionalMatch
Scenario: Satisfies the open world assumption, relationships between same nodes
Given an empty graph
And having executed:
"""
CREATE (a:Player), (b:Team)
CREATE (a)-[:PLAYS_FOR]->(b),
(a)-[:SUPPORTS]->(b)
"""
When executing query:
"""
MATCH (p:Player)-[:PLAYS_FOR]->(team:Team)
OPTIONAL MATCH (p)-[s:SUPPORTS]->(team)
RETURN count(*) AS matches, s IS NULL AS optMatch
"""
Then the result should be:
| matches | optMatch |
| 1 | false |
And no side effects
Scenario: Satisfies the open world assumption, single relationship
Given an empty graph
And having executed:
"""
CREATE (a:Player), (b:Team)
CREATE (a)-[:PLAYS_FOR]->(b)
"""
When executing query:
"""
MATCH (p:Player)-[:PLAYS_FOR]->(team:Team)
OPTIONAL MATCH (p)-[s:SUPPORTS]->(team)
RETURN count(*) AS matches, s IS NULL AS optMatch
"""
Then the result should be:
| matches | optMatch |
| 1 | true |
And no side effects
Scenario: Satisfies the open world assumption, relationships between different nodes
Given an empty graph
And having executed:
"""
CREATE (a:Player), (b:Team), (c:Team)
CREATE (a)-[:PLAYS_FOR]->(b),
(a)-[:SUPPORTS]->(c)
"""
When executing query:
"""
MATCH (p:Player)-[:PLAYS_FOR]->(team:Team)
OPTIONAL MATCH (p)-[s:SUPPORTS]->(team)
RETURN count(*) AS matches, s IS NULL AS optMatch
"""
Then the result should be:
| matches | optMatch |
| 1 | true |
And no side effects

View File

@ -0,0 +1,325 @@
#
# Copyright (c) 2015-2018 "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: OptionalMatchAcceptance
Background:
Given an empty graph
And having executed:
"""
CREATE (s:Single), (a:A {prop: 42}),
(b:B {prop: 46}), (c:C)
CREATE (s)-[:REL]->(a),
(s)-[:REL]->(b),
(a)-[:REL]->(c),
(b)-[:LOOP]->(b)
"""
Scenario: Return null when no matches due to inline label predicate
When executing query:
"""
MATCH (n:Single)
OPTIONAL MATCH (n)-[r]-(m:NonExistent)
RETURN r
"""
Then the result should be:
| r |
| null |
And no side effects
Scenario: Return null when no matches due to label predicate in WHERE
When executing query:
"""
MATCH (n:Single)
OPTIONAL MATCH (n)-[r]-(m)
WHERE m:NonExistent
RETURN r
"""
Then the result should be:
| r |
| null |
And no side effects
Scenario: Respect predicates on the OPTIONAL MATCH
When executing query:
"""
MATCH (n:Single)
OPTIONAL MATCH (n)-[r]-(m)
WHERE m.prop = 42
RETURN m
"""
Then the result should be:
| m |
| (:A {prop: 42}) |
And no side effects
Scenario: Returning label predicate on null node
When executing query:
"""
MATCH (n:Single)
OPTIONAL MATCH (n)-[r:TYPE]-(m)
RETURN m:TYPE
"""
Then the result should be:
| m:TYPE |
| null |
And no side effects
Scenario: MATCH after OPTIONAL MATCH
When executing query:
"""
MATCH (a:Single)
OPTIONAL MATCH (a)-->(b:NonExistent)
OPTIONAL MATCH (a)-->(c:NonExistent)
WITH coalesce(b, c) AS x
MATCH (x)-->(d)
RETURN d
"""
Then the result should be:
| d |
And no side effects
Scenario: WITH after OPTIONAL MATCH
When executing query:
"""
OPTIONAL MATCH (a:A)
WITH a AS a
MATCH (b:B)
RETURN a, b
"""
Then the result should be:
| a | b |
| (:A {prop: 42}) | (:B {prop: 46}) |
And no side effects
Scenario: Named paths in optional matches
When executing query:
"""
MATCH (a:A)
OPTIONAL MATCH p = (a)-[:X]->(b)
RETURN p
"""
Then the result should be:
| p |
| null |
And no side effects
Scenario: OPTIONAL MATCH and bound nodes
When executing query:
"""
MATCH (a:A), (b:C)
OPTIONAL MATCH (x)-->(b)
RETURN x
"""
Then the result should be:
| x |
| (:A {prop: 42}) |
And no side effects
Scenario: OPTIONAL MATCH with labels on the optional end node
And having executed:
"""
CREATE (:X), (x:X), (y1:Y), (y2:Y:Z)
CREATE (x)-[:REL]->(y1),
(x)-[:REL]->(y2)
"""
When executing query:
"""
MATCH (a:X)
OPTIONAL MATCH (a)-->(b:Y)
RETURN b
"""
Then the result should be:
| b |
| null |
| (:Y) |
| (:Y:Z) |
And no side effects
Scenario: Named paths inside optional matches with node predicates
When executing query:
"""
MATCH (a:A), (b:B)
OPTIONAL MATCH p = (a)-[:X]->(b)
RETURN p
"""
Then the result should be:
| p |
| null |
And no side effects
Scenario: Variable length optional relationships
When executing query:
"""
MATCH (a:Single)
OPTIONAL MATCH (a)-[*]->(b)
RETURN b
"""
Then the result should be:
| b |
| (:A {prop: 42}) |
| (:B {prop: 46}) |
| (:B {prop: 46}) |
| (:C) |
And no side effects
Scenario: Variable length optional relationships with length predicates
When executing query:
"""
MATCH (a:Single)
OPTIONAL MATCH (a)-[*3..]-(b)
RETURN b
"""
Then the result should be:
| b |
| null |
And no side effects
Scenario: Optionally matching self-loops
When executing query:
"""
MATCH (a:B)
OPTIONAL MATCH (a)-[r]-(a)
RETURN r
"""
Then the result should be:
| r |
| [:LOOP] |
And no side effects
Scenario: Optionally matching self-loops without matches
When executing query:
"""
MATCH (a)
WHERE NOT (a:B)
OPTIONAL MATCH (a)-[r]->(a)
RETURN r
"""
Then the result should be:
| r |
| null |
| null |
| null |
And no side effects
Scenario: Variable length optional relationships with bound nodes
When executing query:
"""
MATCH (a:Single), (x:C)
OPTIONAL MATCH (a)-[*]->(x)
RETURN x
"""
Then the result should be:
| x |
| (:C) |
And no side effects
Scenario: Variable length optional relationships with bound nodes, no matches
When executing query:
"""
MATCH (a:A), (b:B)
OPTIONAL MATCH p = (a)-[*]->(b)
RETURN p
"""
Then the result should be:
| p |
| null |
And no side effects
Scenario: Longer pattern with bound nodes
When executing query:
"""
MATCH (a:Single), (c:C)
OPTIONAL MATCH (a)-->(b)-->(c)
RETURN b
"""
Then the result should be:
| b |
| (:A {prop: 42}) |
And no side effects
Scenario: Longer pattern with bound nodes without matches
When executing query:
"""
MATCH (a:A), (c:C)
OPTIONAL MATCH (a)-->(b)-->(c)
RETURN b
"""
Then the result should be:
| b |
| null |
And no side effects
Scenario: Handling correlated optional matches; first does not match implies second does not match
When executing query:
"""
MATCH (a:A), (b:B)
OPTIONAL MATCH (a)-->(x)
OPTIONAL MATCH (x)-[r]->(b)
RETURN x, r
"""
Then the result should be:
| x | r |
| (:C) | null |
And no side effects
Scenario: Handling optional matches between optionally matched entities
When executing query:
"""
OPTIONAL MATCH (a:NotThere)
WITH a
MATCH (b:B)
WITH a, b
OPTIONAL MATCH (b)-[r:NOR_THIS]->(a)
RETURN a, b, r
"""
Then the result should be:
| a | b | r |
| null | (:B {prop: 46}) | null |
And no side effects
Scenario: Handling optional matches between nulls
When executing query:
"""
OPTIONAL MATCH (a:NotThere)
OPTIONAL MATCH (b:NotThere)
WITH a, b
OPTIONAL MATCH (b)-[r:NOR_THIS]->(a)
RETURN a, b, r
"""
Then the result should be:
| a | b | r |
| null | null | null |
And no side effects
Scenario: OPTIONAL MATCH and `collect()`
And having executed:
"""
CREATE (:DoesExist {property: 42})
CREATE (:DoesExist {property: 43})
CREATE (:DoesExist {property: 44})
"""
When executing query:
"""
OPTIONAL MATCH (f:DoesExist)
OPTIONAL MATCH (n:DoesNotExist)
RETURN collect(DISTINCT n.property) AS a, collect(DISTINCT f.property) AS b
"""
Then the result should be:
| a | b |
| [] | [42, 43, 44] |
And no side effects

View File

@ -0,0 +1,293 @@
#
# Copyright (c) 2015-2018 "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: OrderByAcceptance
Background:
Given an empty graph
Scenario: ORDER BY should return results in ascending order
And having executed:
"""
CREATE (n1 {prop: 1}),
(n2 {prop: 3}),
(n3 {prop: -5})
"""
When executing query:
"""
MATCH (n)
RETURN n.prop AS prop
ORDER BY n.prop
"""
Then the result should be, in order:
| prop |
| -5 |
| 1 |
| 3 |
And no side effects
Scenario: ORDER BY DESC should return results in descending order
And having executed:
"""
CREATE (n1 {prop: 1}),
(n2 {prop: 3}),
(n3 {prop: -5})
"""
When executing query:
"""
MATCH (n)
RETURN n.prop AS prop
ORDER BY n.prop DESC
"""
Then the result should be, in order:
| prop |
| 3 |
| 1 |
| -5 |
And no side effects
Scenario: ORDER BY of a column introduced in RETURN should return salient results in ascending order
When executing query:
"""
WITH [0, 1] AS prows, [[2], [3, 4]] AS qrows
UNWIND prows AS p
UNWIND qrows[p] AS q
WITH p, count(q) AS rng
RETURN p
ORDER BY rng
"""
Then the result should be, in order:
| p |
| 0 |
| 1 |
And no side effects
Scenario: Renaming columns before ORDER BY should return results in ascending order
And having executed:
"""
CREATE (n1 {prop: 1}),
(n2 {prop: 3}),
(n3 {prop: -5})
"""
When executing query:
"""
MATCH (n)
RETURN n.prop AS n
ORDER BY n + 2
"""
Then the result should be, in order:
| n |
| -5 |
| 1 |
| 3 |
And no side effects
Scenario: Handle projections with ORDER BY - GH#4937
And having executed:
"""
CREATE (c1:Crew {name: 'Neo', rank: 1}),
(c2:Crew {name: 'Neo', rank: 2}),
(c3:Crew {name: 'Neo', rank: 3}),
(c4:Crew {name: 'Neo', rank: 4}),
(c5:Crew {name: 'Neo', rank: 5})
"""
When executing query:
"""
MATCH (c:Crew {name: 'Neo'})
WITH c, 0 AS relevance
RETURN c.rank AS rank
ORDER BY relevance, c.rank
"""
Then the result should be, in order:
| rank |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
And no side effects
Scenario: ORDER BY should order booleans in the expected order
When executing query:
"""
UNWIND [true, false] AS bools
RETURN bools
ORDER BY bools
"""
Then the result should be, in order:
| bools |
| false |
| true |
And no side effects
Scenario: ORDER BY DESC should order booleans in the expected order
When executing query:
"""
UNWIND [true, false] AS bools
RETURN bools
ORDER BY bools DESC
"""
Then the result should be, in order:
| bools |
| true |
| false |
And no side effects
Scenario: ORDER BY should order strings in the expected order
When executing query:
"""
UNWIND ['.*', '', ' ', 'one'] AS strings
RETURN strings
ORDER BY strings
"""
Then the result should be, in order:
| strings |
| '' |
| ' ' |
| '.*' |
| 'one' |
And no side effects
Scenario: ORDER BY DESC should order strings in the expected order
When executing query:
"""
UNWIND ['.*', '', ' ', 'one'] AS strings
RETURN strings
ORDER BY strings DESC
"""
Then the result should be, in order:
| strings |
| 'one' |
| '.*' |
| ' ' |
| '' |
And no side effects
Scenario: ORDER BY should order ints in the expected order
When executing query:
"""
UNWIND [1, 3, 2] AS ints
RETURN ints
ORDER BY ints
"""
Then the result should be, in order:
| ints |
| 1 |
| 2 |
| 3 |
And no side effects
Scenario: ORDER BY DESC should order ints in the expected order
When executing query:
"""
UNWIND [1, 3, 2] AS ints
RETURN ints
ORDER BY ints DESC
"""
Then the result should be, in order:
| ints |
| 3 |
| 2 |
| 1 |
And no side effects
Scenario: ORDER BY should order floats in the expected order
When executing query:
"""
UNWIND [1.5, 1.3, 999.99] AS floats
RETURN floats
ORDER BY floats
"""
Then the result should be, in order:
| floats |
| 1.3 |
| 1.5 |
| 999.99 |
And no side effects
Scenario: ORDER BY DESC should order floats in the expected order
When executing query:
"""
UNWIND [1.5, 1.3, 999.99] AS floats
RETURN floats
ORDER BY floats DESC
"""
Then the result should be, in order:
| floats |
| 999.99 |
| 1.5 |
| 1.3 |
And no side effects
Scenario: Handle ORDER BY with LIMIT 1
And having executed:
"""
CREATE (s:Person {name: 'Steven'}),
(c:Person {name: 'Craig'})
"""
When executing query:
"""
MATCH (p:Person)
RETURN p.name AS name
ORDER BY p.name
LIMIT 1
"""
Then the result should be, in order:
| name |
| 'Craig' |
And no side effects
Scenario: ORDER BY with LIMIT 0 should not generate errors
When executing query:
"""
MATCH (p:Person)
RETURN p.name AS name
ORDER BY p.name
LIMIT 0
"""
Then the result should be, in order:
| name |
And no side effects
Scenario: ORDER BY with negative parameter for LIMIT should not generate errors
And parameters are:
| limit | -1 |
When executing query:
"""
MATCH (p:Person)
RETURN p.name AS name
ORDER BY p.name
LIMIT $`limit`
"""
Then the result should be, in order:
| name |
And no side effects
Scenario: ORDER BY with a negative LIMIT should fail with a syntax exception
And having executed:
"""
CREATE (s:Person {name: 'Steven'}),
(c:Person {name: 'Craig'})
"""
When executing query:
"""
MATCH (p:Person)
RETURN p.name AS name
ORDER BY p.name
LIMIT -1
"""
Then a SyntaxError should be raised at compile time: NegativeIntegerArgument

View File

@ -0,0 +1,35 @@
#
# Copyright (c) 2015-2018 "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: PathEquality
Scenario: Direction of traversed relationship is not significant for path equality, simple
Given an empty graph
And having executed:
"""
CREATE (n:A)-[:LOOP]->(n)
"""
When executing query:
"""
MATCH p1 = (:A)-->()
MATCH p2 = (:A)<--()
RETURN p1 = p2
"""
Then the result should be:
| p1 = p2 |
| true |
And no side effects

View File

@ -0,0 +1,302 @@
#
# Copyright (c) 2015-2018 "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: PatternComprehension
Scenario: Pattern comprehension and ORDER BY
Given an empty graph
And having executed:
"""
CREATE (a {time: 10}), (b {time: 20})
CREATE (a)-[:T]->(b)
"""
When executing query:
"""
MATCH (liker)
RETURN [p = (liker)--() | p] AS isNew
ORDER BY liker.time
"""
Then the result should be:
| isNew |
| [<({time: 10})-[:T]->({time: 20})>] |
| [<({time: 20})<-[:T]-({time: 10})>] |
And no side effects
Scenario: Returning a pattern comprehension
Given an empty graph
And having executed:
"""
CREATE (a:A)
CREATE (a)-[:T]->(:B),
(a)-[:T]->(:C)
"""
When executing query:
"""
MATCH (n)
RETURN [p = (n)-->() | p] AS ps
"""
Then the result should be:
| ps |
| [<(:A)-[:T]->(:C)>, <(:A)-[:T]->(:B)>] |
| [] |
| [] |
And no side effects
Scenario: Returning a pattern comprehension with label predicate
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B), (c:C), (d:D)
CREATE (a)-[:T]->(b),
(a)-[:T]->(c),
(a)-[:T]->(d)
"""
When executing query:
"""
MATCH (n:A)
RETURN [p = (n)-->(:B) | p] AS x
"""
Then the result should be:
| x |
| [<(:A)-[:T]->(:B)>] |
And no side effects
Scenario: Returning a pattern comprehension with bound nodes
Given an empty graph
And having executed:
"""
CREATE (a:A), (b:B)
CREATE (a)-[:T]->(b)
"""
When executing query:
"""
MATCH (a:A), (b:B)
RETURN [p = (a)-[*]->(b) | p] AS paths
"""
Then the result should be:
| paths |
| [<(:A)-[:T]->(:B)>] |
And no side effects
Scenario: Using a pattern comprehension in a WITH
Given an empty graph
And having executed:
"""
CREATE (a:A)
CREATE (a)-[:T]->(:B),
(a)-[:T]->(:C)
"""
When executing query:
"""
MATCH (n)-->(b)
WITH [p = (n)-->() | p] AS ps, count(b) AS c
RETURN ps, c
"""
Then the result should be:
| ps | c |
| [<(:A)-[:T]->(:C)>, <(:A)-[:T]->(:B)>] | 2 |
And no side effects
Scenario: Using a variable-length pattern comprehension in a WITH
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T]->(:B)
"""
When executing query:
"""
MATCH (a:A), (b:B)
WITH [p = (a)-[*]->(b) | p] AS paths, count(a) AS c
RETURN paths, c
"""
Then the result should be:
| paths | c |
| [<(:A)-[:T]->(:B)>] | 1 |
And no side effects
Scenario: Using pattern comprehension in RETURN
Given an empty graph
And having executed:
"""
CREATE (a:A), (:A), (:A)
CREATE (a)-[:HAS]->()
"""
When executing query:
"""
MATCH (n:A)
RETURN [p = (n)-[:HAS]->() | p] AS ps
"""
Then the result should be:
| ps |
| [<(:A)-[:HAS]->()>] |
| [] |
| [] |
And no side effects
Scenario: Aggregating on pattern comprehension
Given an empty graph
And having executed:
"""
CREATE (a:A), (:A), (:A)
CREATE (a)-[:HAS]->()
"""
When executing query:
"""
MATCH (n:A)
RETURN count([p = (n)-[:HAS]->() | p]) AS c
"""
Then the result should be:
| c |
| 3 |
And no side effects
Scenario: Using pattern comprehension to test existence
Given an empty graph
And having executed:
"""
CREATE (a:X {prop: 42}), (:X {prop: 43})
CREATE (a)-[:T]->()
"""
When executing query:
"""
MATCH (n:X)
RETURN n, size([(n)--() | 1]) > 0 AS b
"""
Then the result should be:
| n | b |
| (:X {prop: 42}) | true |
| (:X {prop: 43}) | false |
And no side effects
Scenario: Pattern comprehension inside list comprehension
Given an empty graph
And having executed:
"""
CREATE (n1:X {n: 1}), (m1:Y), (i1:Y), (i2:Y)
CREATE (n1)-[:T]->(m1),
(m1)-[:T]->(i1),
(m1)-[:T]->(i2)
CREATE (n2:X {n: 2}), (m2), (i3:L), (i4:Y)
CREATE (n2)-[:T]->(m2),
(m2)-[:T]->(i3),
(m2)-[:T]->(i4)
"""
When executing query:
"""
MATCH p = (n:X)-->(b)
RETURN n, [x IN nodes(p) | size([(x)-->(:Y) | 1])] AS list
"""
Then the result should be:
| n | list |
| (:X {n: 1}) | [1, 2] |
| (:X {n: 2}) | [0, 1] |
And no side effects
Scenario: Get node degree via size of pattern comprehension
Given an empty graph
And having executed:
"""
CREATE (x:X),
(x)-[:T]->(),
(x)-[:T]->(),
(x)-[:T]->()
"""
When executing query:
"""
MATCH (a:X)
RETURN size([(a)-->() | 1]) AS length
"""
Then the result should be:
| length |
| 3 |
And no side effects
Scenario: Get node degree via size of pattern comprehension that specifies a relationship type
Given an empty graph
And having executed:
"""
CREATE (x:X),
(x)-[:T]->(),
(x)-[:T]->(),
(x)-[:T]->(),
(x)-[:OTHER]->()
"""
When executing query:
"""
MATCH (a:X)
RETURN size([(a)-[:T]->() | 1]) AS length
"""
Then the result should be:
| length |
| 3 |
And no side effects
Scenario: Get node degree via size of pattern comprehension that specifies multiple relationship types
Given an empty graph
And having executed:
"""
CREATE (x:X),
(x)-[:T]->(),
(x)-[:T]->(),
(x)-[:T]->(),
(x)-[:OTHER]->()
"""
When executing query:
"""
MATCH (a:X)
RETURN size([(a)-[:T|OTHER]->() | 1]) AS length
"""
Then the result should be:
| length |
| 4 |
And no side effects
Scenario: Introducing new node variable in pattern comprehension
Given an empty graph
And having executed:
"""
CREATE (a), (b {prop: 'val'})
CREATE (a)-[:T]->(b)
"""
When executing query:
"""
MATCH (n)
RETURN [(n)-[:T]->(b) | b.prop] AS list
"""
Then the result should be:
| list |
| ['val'] |
| [] |
And no side effects
Scenario: Introducing new relationship variable in pattern comprehension
Given an empty graph
And having executed:
"""
CREATE (a), (b)
CREATE (a)-[:T {prop: 'val'}]->(b)
"""
When executing query:
"""
MATCH (n)
RETURN [(n)-[r:T]->() | r.prop] AS list
"""
Then the result should be:
| list |
| ['val'] |
| [] |
And no side effects

View File

@ -0,0 +1,517 @@
#
# Copyright (c) 2015-2018 "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: ProcedureCallAcceptance
Background:
Given an empty graph
Scenario: In-query call to procedure that takes arguments fails when trying to pass them implicitly
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc YIELD out
RETURN out
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentPassingMode
Scenario: Standalone call to procedure that takes no arguments
And there exists a procedure test.labels() :: (label :: STRING?):
| label |
| 'A' |
| 'B' |
| 'C' |
When executing query:
"""
CALL test.labels()
"""
Then the result should be, in order:
| label |
| 'A' |
| 'B' |
| 'C' |
And no side effects
Scenario: In-query call to procedure that takes no arguments
And there exists a procedure test.labels() :: (label :: STRING?):
| label |
| 'A' |
| 'B' |
| 'C' |
When executing query:
"""
CALL test.labels() YIELD label
RETURN label
"""
Then the result should be, in order:
| label |
| 'A' |
| 'B' |
| 'C' |
And no side effects
Scenario: Calling the same procedure twice using the same outputs in each call
And there exists a procedure test.labels() :: (label :: STRING?):
| label |
| 'A' |
| 'B' |
| 'C' |
When executing query:
"""
CALL test.labels() YIELD label
WITH count(*) AS c
CALL test.labels() YIELD label
RETURN *
"""
Then the result should be, in order:
| c | label |
| 3 | 'A' |
| 3 | 'B' |
| 3 | 'C' |
And no side effects
Scenario: Standalone call to VOID procedure that takes no arguments
And there exists a procedure test.doNothing() :: VOID:
|
When executing query:
"""
CALL test.doNothing()
"""
Then the result should be empty
And no side effects
Scenario: In-query call to VOID procedure that takes no arguments
And there exists a procedure test.doNothing() :: VOID:
|
When executing query:
"""
MATCH (n)
CALL test.doNothing()
RETURN n
"""
Then the result should be:
| n |
And no side effects
Scenario: In-query call to VOID procedure does not consume rows
And there exists a procedure test.doNothing() :: VOID:
|
And having executed:
"""
CREATE (:A {name: 'a'})
CREATE (:B {name: 'b'})
CREATE (:C {name: 'c'})
"""
When executing query:
"""
MATCH (n)
CALL test.doNothing()
RETURN n.name AS `name`
"""
Then the result should be:
| name |
| 'a' |
| 'b' |
| 'c' |
And no side effects
Scenario: Standalone call to VOID procedure that takes no arguments, called with implicit arguments
And there exists a procedure test.doNothing() :: VOID:
|
When executing query:
"""
CALL test.doNothing
"""
Then the result should be empty
And no side effects
Scenario: In-query call to procedure that takes no arguments and yields no results
And there exists a procedure test.doNothing() :: ():
|
When executing query:
"""
CALL test.doNothing() YIELD - RETURN 1
"""
Then the result should be:
| 1 |
And no side effects
Scenario: Standalone call to procedure that takes no arguments and yields no results
And there exists a procedure test.doNothing() :: ():
|
When executing query:
"""
CALL test.doNothing()
"""
Then the result should be empty
And no side effects
Scenario: Standalone call to procedure that takes no arguments and yields no results, called with implicit arguments
And there exists a procedure test.doNothing() :: ():
|
When executing query:
"""
CALL test.doNothing
"""
Then the result should be empty
And no side effects
Scenario: In-query call to procedure with explicit arguments
And there exists a procedure test.my.proc(name :: STRING?, id :: INTEGER?) :: (city :: STRING?, country_code :: INTEGER?):
| name | id | city | country_code |
| 'Andres' | 1 | 'Malmö' | 46 |
| 'Tobias' | 1 | 'Malmö' | 46 |
| 'Mats' | 1 | 'Malmö' | 46 |
| 'Stefan' | 1 | 'Berlin' | 49 |
| 'Stefan' | 2 | 'München' | 49 |
| 'Petra' | 1 | 'London' | 44 |
When executing query:
"""
CALL test.my.proc('Stefan', 1) YIELD city, country_code
RETURN city, country_code
"""
Then the result should be, in order:
| city | country_code |
| 'Berlin' | 49 |
And no side effects
Scenario: In-query call to procedure with explicit arguments that drops all result fields
And there exists a procedure test.my.proc(name :: STRING?, id :: INTEGER?) :: (city :: STRING?, country_code :: INTEGER?):
| name | id | city | country_code |
| 'Andres' | 1 | 'Malmö' | 46 |
| 'Tobias' | 1 | 'Malmö' | 46 |
| 'Mats' | 1 | 'Malmö' | 46 |
| 'Stefan' | 1 | 'Berlin' | 49 |
| 'Stefan' | 2 | 'München' | 49 |
| 'Petra' | 1 | 'London' | 44 |
When executing query:
"""
WITH 'Stefan' AS name, 1 AS id
CALL test.my.proc(name, id) YIELD -
RETURN name, id, count(*) AS count
"""
Then the result should be, in order:
| name | id | count |
| 'Stefan' | 1 | 1 |
And no side effects
Scenario: Standalone call to procedure with explicit arguments
And there exists a procedure test.my.proc(name :: STRING?, id :: INTEGER?) :: (city :: STRING?, country_code :: INTEGER?):
| name | id | city | country_code |
| 'Andres' | 1 | 'Malmö' | 46 |
| 'Tobias' | 1 | 'Malmö' | 46 |
| 'Mats' | 1 | 'Malmö' | 46 |
| 'Stefan' | 1 | 'Berlin' | 49 |
| 'Stefan' | 2 | 'München' | 49 |
| 'Petra' | 1 | 'London' | 44 |
When executing query:
"""
CALL test.my.proc('Stefan', 1)
"""
Then the result should be, in order:
| city | country_code |
| 'Berlin' | 49 |
And no side effects
Scenario: Standalone call to procedure with implicit arguments
And there exists a procedure test.my.proc(name :: STRING?, id :: INTEGER?) :: (city :: STRING?, country_code :: INTEGER?):
| name | id | city | country_code |
| 'Andres' | 1 | 'Malmö' | 46 |
| 'Tobias' | 1 | 'Malmö' | 46 |
| 'Mats' | 1 | 'Malmö' | 46 |
| 'Stefan' | 1 | 'Berlin' | 49 |
| 'Stefan' | 2 | 'München' | 49 |
| 'Petra' | 1 | 'London' | 44 |
And parameters are:
| name | 'Stefan' |
| id | 1 |
When executing query:
"""
CALL test.my.proc
"""
Then the result should be, in order:
| city | country_code |
| 'Berlin' | 49 |
And no side effects
Scenario: Standalone call to procedure with argument of type NUMBER accepts value of type INTEGER
And there exists a procedure test.my.proc(in :: NUMBER?) :: (out :: STRING?):
| in | out |
| 42 | 'wisdom' |
| 42.3 | 'about right' |
When executing query:
"""
CALL test.my.proc(42)
"""
Then the result should be, in order:
| out |
| 'wisdom' |
And no side effects
Scenario: In-query call to procedure with argument of type NUMBER accepts value of type INTEGER
And there exists a procedure test.my.proc(in :: NUMBER?) :: (out :: STRING?):
| in | out |
| 42 | 'wisdom' |
| 42.3 | 'about right' |
When executing query:
"""
CALL test.my.proc(42) YIELD out
RETURN out
"""
Then the result should be, in order:
| out |
| 'wisdom' |
And no side effects
Scenario: Standalone call to procedure with argument of type NUMBER accepts value of type FLOAT
And there exists a procedure test.my.proc(in :: NUMBER?) :: (out :: STRING?):
| in | out |
| 42 | 'wisdom' |
| 42.3 | 'about right' |
When executing query:
"""
CALL test.my.proc(42.3)
"""
Then the result should be, in order:
| out |
| 'about right' |
And no side effects
Scenario: In-query call to procedure with argument of type NUMBER accepts value of type FLOAT
And there exists a procedure test.my.proc(in :: NUMBER?) :: (out :: STRING?):
| in | out |
| 42 | 'wisdom' |
| 42.3 | 'about right' |
When executing query:
"""
CALL test.my.proc(42.3) YIELD out
RETURN out
"""
Then the result should be, in order:
| out |
| 'about right' |
And no side effects
Scenario: Standalone call to procedure with argument of type FLOAT accepts value of type INTEGER
And there exists a procedure test.my.proc(in :: FLOAT?) :: (out :: STRING?):
| in | out |
| 42.0 | 'close enough' |
When executing query:
"""
CALL test.my.proc(42)
"""
Then the result should be, in order:
| out |
| 'close enough' |
And no side effects
Scenario: In-query call to procedure with argument of type FLOAT accepts value of type INTEGER
And there exists a procedure test.my.proc(in :: FLOAT?) :: (out :: STRING?):
| in | out |
| 42.0 | 'close enough' |
When executing query:
"""
CALL test.my.proc(42) YIELD out
RETURN out
"""
Then the result should be, in order:
| out |
| 'close enough' |
And no side effects
Scenario: Standalone call to procedure with argument of type INTEGER accepts value of type FLOAT
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: STRING?):
| in | out |
| 42 | 'close enough' |
When executing query:
"""
CALL test.my.proc(42.0)
"""
Then the result should be, in order:
| out |
| 'close enough' |
And no side effects
Scenario: In-query call to procedure with argument of type INTEGER accepts value of type FLOAT
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: STRING?):
| in | out |
| 42 | 'close enough' |
When executing query:
"""
CALL test.my.proc(42.0) YIELD out
RETURN out
"""
Then the result should be, in order:
| out |
| 'close enough' |
And no side effects
Scenario: Standalone call to procedure with null argument
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: STRING?):
| in | out |
| null | 'nix' |
When executing query:
"""
CALL test.my.proc(null)
"""
Then the result should be, in order:
| out |
| 'nix' |
And no side effects
Scenario: In-query call to procedure with null argument
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: STRING?):
| in | out |
| null | 'nix' |
When executing query:
"""
CALL test.my.proc(null) YIELD out
RETURN out
"""
Then the result should be, in order:
| out |
| 'nix' |
And no side effects
Scenario: Standalone call to procedure should fail if input type is wrong
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc(true)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: In-query call to procedure should fail if input type is wrong
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc(true) YIELD out
RETURN out
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Standalone call to procedure should fail if explicit argument is missing
And there exists a procedure test.my.proc(name :: STRING?, in :: INTEGER?) :: (out :: INTEGER?):
| name | in | out |
When executing query:
"""
CALL test.my.proc('Dobby')
"""
Then a SyntaxError should be raised at compile time: InvalidNumberOfArguments
Scenario: In-query call to procedure should fail if explicit argument is missing
And there exists a procedure test.my.proc(name :: STRING?, in :: INTEGER?) :: (out :: INTEGER?):
| name | in | out |
When executing query:
"""
CALL test.my.proc('Dobby') YIELD out
RETURN out
"""
Then a SyntaxError should be raised at compile time: InvalidNumberOfArguments
Scenario: Standalone call to procedure should fail if too many explicit argument are given
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc(1, 2, 3, 4)
"""
Then a SyntaxError should be raised at compile time: InvalidNumberOfArguments
Scenario: In-query call to procedure should fail if too many explicit argument are given
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc(1, 2, 3, 4) YIELD out
RETURN out
"""
Then a SyntaxError should be raised at compile time: InvalidNumberOfArguments
Scenario: Standalone call to procedure should fail if implicit argument is missing
And there exists a procedure test.my.proc(name :: STRING?, in :: INTEGER?) :: (out :: INTEGER?):
| name | in | out |
And parameters are:
| name | 'Stefan' |
When executing query:
"""
CALL test.my.proc
"""
Then a ParameterMissing should be raised at compile time: MissingParameter
Scenario: In-query call to procedure that has outputs fails if no outputs are yielded
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc(1)
RETURN out
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: In-query call to procedure that both takes arguments and has outputs fails if the arguments are passed implicitly and no outputs are yielded
And there exists a procedure test.my.proc(in :: INTEGER?) :: (out :: INTEGER?):
| in | out |
When executing query:
"""
CALL test.my.proc
RETURN out
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Standalone call to unknown procedure should fail
When executing query:
"""
CALL test.my.proc
"""
Then a ProcedureError should be raised at compile time: ProcedureNotFound
Scenario: In-query call to unknown procedure should fail
When executing query:
"""
CALL test.my.proc() YIELD out
RETURN out
"""
Then a ProcedureError should be raised at compile time: ProcedureNotFound
Scenario: In-query procedure call should fail if shadowing an already bound variable
And there exists a procedure test.labels() :: (label :: STRING?):
| label |
| 'A' |
| 'B' |
| 'C' |
When executing query:
"""
WITH 'Hi' AS label
CALL test.labels() YIELD label
RETURN *
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: In-query procedure call should fail if one of the argument expressions uses an aggregation function
And there exists a procedure test.labels(in :: INTEGER?) :: (label :: STRING?):
| in | label |
When executing query:
"""
MATCH (n)
CALL test.labels(count(n)) YIELD label
RETURN label
"""
Then a SyntaxError should be raised at compile time: InvalidAggregation

View File

@ -0,0 +1,161 @@
#
# Copyright (c) 2015-2018 "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: RemoveAcceptance
Scenario: Should ignore nulls
Given an empty graph
And having executed:
"""
CREATE ({prop: 42})
"""
When executing query:
"""
MATCH (n)
OPTIONAL MATCH (n)-[r]->()
REMOVE r.prop
RETURN n
"""
Then the result should be:
| n |
| ({prop: 42}) |
And no side effects
Scenario: Remove a single label
Given an empty graph
And having executed:
"""
CREATE (:L {prop: 42})
"""
When executing query:
"""
MATCH (n)
REMOVE n:L
RETURN n.prop
"""
Then the result should be:
| n.prop |
| 42 |
And the side effects should be:
| -labels | 1 |
Scenario: Remove multiple labels
Given an empty graph
And having executed:
"""
CREATE (:L1:L2:L3 {prop: 42})
"""
When executing query:
"""
MATCH (n)
REMOVE n:L1:L3
RETURN labels(n)
"""
Then the result should be:
| labels(n) |
| ['L2'] |
And the side effects should be:
| -labels | 2 |
Scenario: Remove a single node property
Given an empty graph
And having executed:
"""
CREATE (:L {prop: 42})
"""
When executing query:
"""
MATCH (n)
REMOVE n.prop
RETURN exists(n.prop) AS still_there
"""
Then the result should be:
| still_there |
| false |
And the side effects should be:
| -properties | 1 |
Scenario: Remove multiple node properties
Given an empty graph
And having executed:
"""
CREATE (:L {prop: 42, a: 'a', b: 'B'})
"""
When executing query:
"""
MATCH (n)
REMOVE n.prop, n.a
RETURN size(keys(n)) AS props
"""
Then the result should be:
| props |
| 1 |
And the side effects should be:
| -properties | 2 |
Scenario: Remove a single relationship property
Given an empty graph
And having executed:
"""
CREATE (a), (b), (a)-[:X {prop: 42}]->(b)
"""
When executing query:
"""
MATCH ()-[r]->()
REMOVE r.prop
RETURN exists(r.prop) AS still_there
"""
Then the result should be:
| still_there |
| false |
And the side effects should be:
| -properties | 1 |
Scenario: Remove multiple relationship properties
Given an empty graph
And having executed:
"""
CREATE (a), (b), (a)-[:X {prop: 42, a: 'a', b: 'B'}]->(b)
"""
When executing query:
"""
MATCH ()-[r]->()
REMOVE r.prop, r.a
RETURN size(keys(r)) AS props
"""
Then the result should be:
| props |
| 1 |
And the side effects should be:
| -properties | 2 |
Scenario: Remove a missing property should be a valid operation
Given an empty graph
And having executed:
"""
CREATE (), (), ()
"""
When executing query:
"""
MATCH (n)
REMOVE n.prop
RETURN sum(size(keys(n))) AS totalNumberOfProps
"""
Then the result should be:
| totalNumberOfProps |
| 0 |
And no side effects

View File

@ -0,0 +1,312 @@
#
# Copyright (c) 2015-2018 "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: ReturnAcceptanceTest
Scenario: Allow addition
Given an empty graph
And having executed:
"""
CREATE ({id: 1337, version: 99})
"""
When executing query:
"""
MATCH (a)
WHERE a.id = 1337
RETURN a.version + 5
"""
Then the result should be:
| a.version + 5 |
| 104 |
And no side effects
Scenario: Limit to two hits
Given an empty graph
When executing query:
"""
UNWIND [1, 1, 1, 1, 1] AS i
RETURN i
LIMIT 2
"""
Then the result should be:
| i |
| 1 |
| 1 |
And no side effects
Scenario: Limit to two hits with explicit order
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
When executing query:
"""
MATCH (n)
RETURN n
ORDER BY n.name ASC
LIMIT 2
"""
Then the result should be:
| n |
| ({name: 'A'}) |
| ({name: 'B'}) |
And no side effects
Scenario: Start the result from the second row
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
When executing query:
"""
MATCH (n)
RETURN n
ORDER BY n.name ASC
SKIP 2
"""
Then the result should be, in order:
| n |
| ({name: 'C'}) |
| ({name: 'D'}) |
| ({name: 'E'}) |
And no side effects
Scenario: Start the result from the second row by param
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
And parameters are:
| skipAmount | 2 |
When executing query:
"""
MATCH (n)
RETURN n
ORDER BY n.name ASC
SKIP $skipAmount
"""
Then the result should be, in order:
| n |
| ({name: 'C'}) |
| ({name: 'D'}) |
| ({name: 'E'}) |
And no side effects
Scenario: Get rows in the middle
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
When executing query:
"""
MATCH (n)
RETURN n
ORDER BY n.name ASC
SKIP 2
LIMIT 2
"""
Then the result should be, in order:
| n |
| ({name: 'C'}) |
| ({name: 'D'}) |
And no side effects
Scenario: Get rows in the middle by param
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
And parameters are:
| s | 2 |
| l | 2 |
When executing query:
"""
MATCH (n)
RETURN n
ORDER BY n.name ASC
SKIP $s
LIMIT $l
"""
Then the result should be, in order:
| n |
| ({name: 'C'}) |
| ({name: 'D'}) |
And no side effects
Scenario: Sort on aggregated function
Given an empty graph
And having executed:
"""
CREATE ({division: 'A', age: 22}),
({division: 'B', age: 33}),
({division: 'B', age: 44}),
({division: 'C', age: 55})
"""
When executing query:
"""
MATCH (n)
RETURN n.division, max(n.age)
ORDER BY max(n.age)
"""
Then the result should be, in order:
| n.division | max(n.age) |
| 'A' | 22 |
| 'B' | 44 |
| 'C' | 55 |
And no side effects
Scenario: Support sort and distinct
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'})
"""
When executing query:
"""
MATCH (a)
RETURN DISTINCT a
ORDER BY a.name
"""
Then the result should be, in order:
| a |
| ({name: 'A'}) |
| ({name: 'B'}) |
| ({name: 'C'}) |
And no side effects
Scenario: Support column renaming
Given an empty graph
And having executed:
"""
CREATE (:Singleton)
"""
When executing query:
"""
MATCH (a)
RETURN a AS ColumnName
"""
Then the result should be:
| ColumnName |
| (:Singleton) |
And no side effects
Scenario: Support ordering by a property after being distinct-ified
Given an empty graph
And having executed:
"""
CREATE (:A)-[:T]->(:B)
"""
When executing query:
"""
MATCH (a)-->(b)
RETURN DISTINCT b
ORDER BY b.name
"""
Then the result should be, in order:
| b |
| (:B) |
And no side effects
Scenario: Arithmetic precedence test
Given any graph
When executing query:
"""
RETURN 12 / 4 * 3 - 2 * 4
"""
Then the result should be:
| 12 / 4 * 3 - 2 * 4 |
| 1 |
And no side effects
Scenario: Arithmetic precedence with parenthesis test
Given any graph
When executing query:
"""
RETURN 12 / 4 * (3 - 2 * 4)
"""
Then the result should be:
| 12 / 4 * (3 - 2 * 4) |
| -15 |
And no side effects
Scenario: Count star should count everything in scope
Given an empty graph
And having executed:
"""
CREATE (:L1), (:L2), (:L3)
"""
When executing query:
"""
MATCH (a)
RETURN a, count(*)
ORDER BY count(*)
"""
Then the result should be:
| a | count(*) |
| (:L1) | 1 |
| (:L2) | 1 |
| (:L3) | 1 |
And no side effects
Scenario: Absolute function
Given any graph
When executing query:
"""
RETURN abs(-1)
"""
Then the result should be:
| abs(-1) |
| 1 |
And no side effects
Scenario: Return collection size
Given any graph
When executing query:
"""
RETURN size([1, 2, 3]) AS n
"""
Then the result should be:
| n |
| 3 |
And no side effects

View File

@ -0,0 +1,623 @@
#
# Copyright (c) 2015-2018 "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: ReturnAcceptance2
Scenario: Fail when returning properties of deleted nodes
Given an empty graph
And having executed:
"""
CREATE ({p: 0})
"""
When executing query:
"""
MATCH (n)
DELETE n
RETURN n.p
"""
Then a EntityNotFound should be raised at runtime: DeletedEntityAccess
Scenario: Fail when returning labels of deleted nodes
Given an empty graph
And having executed:
"""
CREATE (:A)
"""
When executing query:
"""
MATCH (n)
DELETE n
RETURN labels(n)
"""
Then a EntityNotFound should be raised at runtime: DeletedEntityAccess
Scenario: Fail when returning properties of deleted relationships
Given an empty graph
And having executed:
"""
CREATE ()-[:T {p: 0}]->()
"""
When executing query:
"""
MATCH ()-[r]->()
DELETE r
RETURN r.p
"""
Then a EntityNotFound should be raised at runtime: DeletedEntityAccess
Scenario: Do not fail when returning type of deleted relationships
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH ()-[r]->()
DELETE r
RETURN type(r)
"""
Then the result should be:
| type(r) |
| 'T' |
And the side effects should be:
| -relationships | 1 |
Scenario: Accept valid Unicode literal
Given any graph
When executing query:
"""
RETURN '\u01FF' AS a
"""
Then the result should be:
| a |
| 'ǿ' |
And no side effects
Scenario: LIMIT 0 should return an empty result
Given an empty graph
And having executed:
"""
CREATE (), (), ()
"""
When executing query:
"""
MATCH (n)
RETURN n
LIMIT 0
"""
Then the result should be:
| n |
And no side effects
Scenario: Fail when sorting on variable removed by DISTINCT
Given an empty graph
And having executed:
"""
CREATE ({name: 'A', age: 13}), ({name: 'B', age: 12}), ({name: 'C', age: 11})
"""
When executing query:
"""
MATCH (a)
RETURN DISTINCT a.name
ORDER BY a.age
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Ordering with aggregation
Given an empty graph
And having executed:
"""
CREATE ({name: 'nisse'})
"""
When executing query:
"""
MATCH (n)
RETURN n.name, count(*) AS foo
ORDER BY n.name
"""
Then the result should be:
| n.name | foo |
| 'nisse' | 1 |
And no side effects
Scenario: DISTINCT on nullable values
Given an empty graph
And having executed:
"""
CREATE ({name: 'Florescu'}), (), ()
"""
When executing query:
"""
MATCH (n)
RETURN DISTINCT n.name
"""
Then the result should be:
| n.name |
| 'Florescu' |
| null |
And no side effects
Scenario: Return all variables
Given an empty graph
And having executed:
"""
CREATE (:Start)-[:T]->()
"""
When executing query:
"""
MATCH p = (a:Start)-->(b)
RETURN *
"""
Then the result should be:
| a | b | p |
| (:Start) | () | <(:Start)-[:T]->()> |
And no side effects
Scenario: Setting and returning the size of a list property
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n.x = [1, 2, 3]
RETURN size(n.x)
"""
Then the result should be:
| size(n.x) |
| 3 |
And the side effects should be:
| +properties | 1 |
Scenario: `sqrt()` returning float values
Given any graph
When executing query:
"""
RETURN sqrt(12.96)
"""
Then the result should be:
| sqrt(12.96) |
| 3.6 |
And no side effects
Scenario: Arithmetic expressions inside aggregation
Given an empty graph
And having executed:
"""
CREATE (andres {name: 'Andres'}),
(michael {name: 'Michael'}),
(peter {name: 'Peter'}),
(bread {type: 'Bread'}),
(veggies {type: 'Veggies'}),
(meat {type: 'Meat'})
CREATE (andres)-[:ATE {times: 10}]->(bread),
(andres)-[:ATE {times: 8}]->(veggies),
(michael)-[:ATE {times: 4}]->(veggies),
(michael)-[:ATE {times: 6}]->(bread),
(michael)-[:ATE {times: 9}]->(meat),
(peter)-[:ATE {times: 7}]->(veggies),
(peter)-[:ATE {times: 7}]->(bread),
(peter)-[:ATE {times: 4}]->(meat)
"""
When executing query:
"""
MATCH (me)-[r1:ATE]->()<-[r2:ATE]-(you)
WHERE me.name = 'Michael'
WITH me, count(DISTINCT r1) AS H1, count(DISTINCT r2) AS H2, you
MATCH (me)-[r1:ATE]->()<-[r2:ATE]-(you)
RETURN me, you, sum((1 - abs(r1.times / H1 - r2.times / H2)) * (r1.times + r2.times) / (H1 + H2)) AS sum
"""
Then the result should be:
| me | you | sum |
| ({name: 'Michael'}) | ({name: 'Andres'}) | -7 |
| ({name: 'Michael'}) | ({name: 'Peter'}) | 0 |
And no side effects
Scenario: Matching and disregarding output, then matching again
Given an empty graph
And having executed:
"""
CREATE (andres {name: 'Andres'}),
(michael {name: 'Michael'}),
(peter {name: 'Peter'}),
(bread {type: 'Bread'}),
(veggies {type: 'Veggies'}),
(meat {type: 'Meat'})
CREATE (andres)-[:ATE {times: 10}]->(bread),
(andres)-[:ATE {times: 8}]->(veggies),
(michael)-[:ATE {times: 4}]->(veggies),
(michael)-[:ATE {times: 6}]->(bread),
(michael)-[:ATE {times: 9}]->(meat),
(peter)-[:ATE {times: 7}]->(veggies),
(peter)-[:ATE {times: 7}]->(bread),
(peter)-[:ATE {times: 4}]->(meat)
"""
When executing query:
"""
MATCH ()-->()
WITH 1 AS x
MATCH ()-[r1]->()<--()
RETURN sum(r1.times)
"""
Then the result should be:
| sum(r1.times) |
| 776 |
And no side effects
Scenario: Returning a list property
Given an empty graph
And having executed:
"""
CREATE ({foo: [1, 2, 3]})
"""
When executing query:
"""
MATCH (n)
RETURN n
"""
Then the result should be:
| n |
| ({foo: [1, 2, 3]}) |
And no side effects
Scenario: Returning a projected map
Given an empty graph
And having executed:
"""
CREATE ({foo: [1, 2, 3]})
"""
When executing query:
"""
RETURN {a: 1, b: 'foo'}
"""
Then the result should be:
| {a: 1, b: 'foo'} |
| {a: 1, b: 'foo'} |
And no side effects
Scenario: Returning an expression
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (a)
RETURN exists(a.id), a IS NOT NULL
"""
Then the result should be:
| exists(a.id) | a IS NOT NULL |
| false | true |
And no side effects
Scenario: Concatenating and returning the size of literal lists
Given any graph
When executing query:
"""
RETURN size([[], []] + [[]]) AS l
"""
Then the result should be:
| l |
| 3 |
And no side effects
Scenario: Returning nested expressions based on list property
Given an empty graph
And having executed:
"""
CREATE ()
"""
When executing query:
"""
MATCH (n)
SET n.array = [1, 2, 3, 4, 5]
RETURN tail(tail(n.array))
"""
Then the result should be:
| tail(tail(n.array)) |
| [3, 4, 5] |
And the side effects should be:
| +properties | 1 |
Scenario: Limiting amount of rows when there are fewer left than the LIMIT argument
Given an empty graph
And having executed:
"""
UNWIND range(0, 15) AS i
CREATE ({count: i})
"""
When executing query:
"""
MATCH (a)
RETURN a.count
ORDER BY a.count
SKIP 10
LIMIT 10
"""
Then the result should be, in order:
| a.count |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
And no side effects
Scenario: `substring()` with default second argument
Given any graph
When executing query:
"""
RETURN substring('0123456789', 1) AS s
"""
Then the result should be:
| s |
| '123456789' |
And no side effects
Scenario: Returning all variables with ordering
Given an empty graph
And having executed:
"""
CREATE ({id: 1}), ({id: 10})
"""
When executing query:
"""
MATCH (n)
RETURN *
ORDER BY n.id
"""
Then the result should be, in order:
| n |
| ({id: 1}) |
| ({id: 10}) |
And no side effects
Scenario: Using aliased DISTINCT expression in ORDER BY
Given an empty graph
And having executed:
"""
CREATE ({id: 1}), ({id: 10})
"""
When executing query:
"""
MATCH (n)
RETURN DISTINCT n.id AS id
ORDER BY id DESC
"""
Then the result should be, in order:
| id |
| 10 |
| 1 |
And no side effects
Scenario: Returned columns do not change from using ORDER BY
Given an empty graph
And having executed:
"""
CREATE ({id: 1}), ({id: 10})
"""
When executing query:
"""
MATCH (n)
RETURN DISTINCT n
ORDER BY n.id
"""
Then the result should be, in order:
| n |
| ({id: 1}) |
| ({id: 10}) |
And no side effects
Scenario: Arithmetic expressions should propagate null values
Given any graph
When executing query:
"""
RETURN 1 + (2 - (3 * (4 / (5 ^ (6 % null))))) AS a
"""
Then the result should be:
| a |
| null |
And no side effects
Scenario: Indexing into nested literal lists
Given any graph
When executing query:
"""
RETURN [[1]][0][0]
"""
Then the result should be:
| [[1]][0][0] |
| 1 |
And no side effects
Scenario: Aliasing expressions
Given an empty graph
And having executed:
"""
CREATE ({id: 42})
"""
When executing query:
"""
MATCH (a)
RETURN a.id AS a, a.id
"""
Then the result should be:
| a | a.id |
| 42 | 42 |
And no side effects
Scenario: Projecting an arithmetic expression with aggregation
Given an empty graph
And having executed:
"""
CREATE ({id: 42})
"""
When executing query:
"""
MATCH (a)
RETURN a, count(a) + 3
"""
Then the result should be:
| a | count(a) + 3 |
| ({id: 42}) | 4 |
And no side effects
Scenario: Multiple aliasing and backreferencing
Given any graph
When executing query:
"""
CREATE (m {id: 0})
WITH {first: m.id} AS m
WITH {second: m.first} AS m
RETURN m.second
"""
Then the result should be:
| m.second |
| 0 |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Aggregating by a list property has a correct definition of equality
Given an empty graph
And having executed:
"""
CREATE ({a: [1, 2, 3]}), ({a: [1, 2, 3]})
"""
When executing query:
"""
MATCH (a)
WITH a.a AS a, count(*) AS count
RETURN count
"""
Then the result should be:
| count |
| 2 |
And no side effects
Scenario: Reusing variable names
Given an empty graph
And having executed:
"""
CREATE (a:Person), (b:Person), (m:Message {id: 10})
CREATE (a)-[:LIKE {creationDate: 20160614}]->(m)-[:POSTED_BY]->(b)
"""
When executing query:
"""
MATCH (person:Person)<--(message)<-[like]-(:Person)
WITH like.creationDate AS likeTime, person AS person
ORDER BY likeTime, message.id
WITH head(collect({likeTime: likeTime})) AS latestLike, person AS person
RETURN latestLike.likeTime AS likeTime
ORDER BY likeTime
"""
Then the result should be, in order:
| likeTime |
| 20160614 |
And no side effects
Scenario: Concatenating lists of same type
Given any graph
When executing query:
"""
RETURN [1, 10, 100] + [4, 5] AS foo
"""
Then the result should be:
| foo |
| [1, 10, 100, 4, 5] |
And no side effects
Scenario: Appending lists of same type
Given any graph
When executing query:
"""
RETURN [false, true] + false AS foo
"""
Then the result should be:
| foo |
| [false, true, false] |
And no side effects
Scenario: DISTINCT inside aggregation should work with lists in maps
Given an empty graph
And having executed:
"""
CREATE ({list: ['A', 'B']}), ({list: ['A', 'B']})
"""
When executing query:
"""
MATCH (n)
RETURN count(DISTINCT {foo: n.list}) AS count
"""
Then the result should be:
| count |
| 1 |
And no side effects
Scenario: Handling DISTINCT with lists in maps
Given an empty graph
And having executed:
"""
CREATE ({list: ['A', 'B']}), ({list: ['A', 'B']})
"""
When executing query:
"""
MATCH (n)
WITH DISTINCT {foo: n.list} AS map
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: DISTINCT inside aggregation should work with nested lists in maps
Given an empty graph
And having executed:
"""
CREATE ({list: ['A', 'B']}), ({list: ['A', 'B']})
"""
When executing query:
"""
MATCH (n)
RETURN count(DISTINCT {foo: [[n.list, n.list], [n.list, n.list]]}) AS count
"""
Then the result should be:
| count |
| 1 |
And no side effects
Scenario: DISTINCT inside aggregation should work with nested lists of maps in maps
Given an empty graph
And having executed:
"""
CREATE ({list: ['A', 'B']}), ({list: ['A', 'B']})
"""
When executing query:
"""
MATCH (n)
RETURN count(DISTINCT {foo: [{bar: n.list}, {baz: {apa: n.list}}]}) AS count
"""
Then the result should be:
| count |
| 1 |
And no side effects

View File

@ -0,0 +1,394 @@
#
# Copyright (c) 2015-2018 "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: SemanticErrorAcceptance
Background:
Given any graph
Scenario: Failing when returning an undefined variable
When executing query:
"""
MATCH ()
RETURN foo
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when comparing to an undefined variable
When executing query:
"""
MATCH (s)
WHERE s.name = undefinedVariable
AND s.age = 10
RETURN s
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using IN on a string literal
When executing query:
"""
MATCH (n)
WHERE n.id IN ''
RETURN 1
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using IN on an integer literal
When executing query:
"""
MATCH (n)
WHERE n.id IN 1
RETURN 1
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using IN on a float literal
When executing query:
"""
MATCH (n)
WHERE n.id IN 1.0
RETURN 1
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using IN on a boolean literal
When executing query:
"""
MATCH (n)
WHERE n.id IN true
RETURN 1
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when a node is used as a relationship
When executing query:
"""
MATCH (r)
MATCH ()-[r]-()
RETURN r
"""
Then a SyntaxError should be raised at compile time: VariableTypeConflict
Scenario: Failing when a relationship is used as a node
When executing query:
"""
MATCH ()-[r]-(r)
RETURN r
"""
Then a SyntaxError should be raised at compile time: VariableTypeConflict
Scenario: Failing when using `type()` on a node
When executing query:
"""
MATCH (r)
RETURN type(r)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using `length()` on a node
When executing query:
"""
MATCH (r)
RETURN length(r)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when re-using a relationship in the same pattern
When executing query:
"""
MATCH (a)-[r]->()-[r]->(a)
RETURN r
"""
Then a SyntaxError should be raised at compile time: RelationshipUniquenessViolation
Scenario: Failing when using NOT on string literal
When executing query:
"""
RETURN NOT 'foo'
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using variable length relationship in CREATE
When executing query:
"""
CREATE ()-[:FOO*2]->()
"""
Then a SyntaxError should be raised at compile time: CreatingVarLength
Scenario: Failing when using variable length relationship in MERGE
When executing query:
"""
MERGE (a)
MERGE (b)
MERGE (a)-[:FOO*2]->(b)
"""
Then a SyntaxError should be raised at compile time: CreatingVarLength
Scenario: Failing when using parameter as node predicate in MATCH
When executing query:
"""
MATCH (n $param)
RETURN n
"""
Then a SyntaxError should be raised at compile time: InvalidParameterUse
Scenario: Failing when using parameter as relationship predicate in MATCH
When executing query:
"""
MATCH ()-[r:FOO $param]->()
RETURN r
"""
Then a SyntaxError should be raised at compile time: InvalidParameterUse
Scenario: Failing when using parameter as node predicate in MERGE
When executing query:
"""
MERGE (n $param)
RETURN n
"""
Then a SyntaxError should be raised at compile time: InvalidParameterUse
Scenario: Failing when using parameter as relationship predicate in MERGE
When executing query:
"""
MERGE (a)
MERGE (b)
MERGE (a)-[r:FOO $param]->(b)
RETURN r
"""
Then a SyntaxError should be raised at compile time: InvalidParameterUse
Scenario: Failing when deleting an integer expression
When executing query:
"""
MATCH ()
DELETE 1 + 1
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using CREATE on a node that is already bound
When executing query:
"""
MATCH (a)
CREATE (a)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using MERGE on a node that is already bound
When executing query:
"""
MATCH (a)
CREATE (a)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using CREATE on a relationship that is already bound
When executing query:
"""
MATCH ()-[r]->()
CREATE ()-[r]->()
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using MERGE on a relationship that is already bound
When executing query:
"""
MATCH (a)-[r]->(b)
MERGE (a)-[r]->(b)
"""
Then a SyntaxError should be raised at compile time: VariableAlreadyBound
Scenario: Failing when using undefined variable in ON CREATE
When executing query:
"""
MERGE (n)
ON CREATE SET x.foo = 1
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using undefined variable in ON MATCH
When executing query:
"""
MERGE (n)
ON MATCH SET x.foo = 1
"""
Then a SyntaxError should be raised at compile time: UndefinedVariable
Scenario: Failing when using MATCH after OPTIONAL MATCH
When executing query:
"""
OPTIONAL MATCH ()-->()
MATCH ()-->(d)
RETURN d
"""
Then a SyntaxError should be raised at compile time: InvalidClauseComposition
Scenario: Failing when float value is too large
When executing query:
"""
RETURN 1.34E999
"""
Then a SyntaxError should be raised at compile time: FloatingPointOverflow
Scenario: Handling property access on the Any type
When executing query:
"""
WITH [{prop: 0}, 1] AS list
RETURN (list[0]).prop
"""
Then the result should be:
| (list[0]).prop |
| 0 |
And no side effects
Scenario: Failing when performing property access on a non-map 1
When executing query:
"""
WITH [{prop: 0}, 1] AS list
RETURN (list[1]).prop
"""
Then a TypeError should be raised at runtime: PropertyAccessOnNonMap
Scenario: Failing when performing property access on a non-map 2
When executing query:
"""
CREATE (n {prop: 'foo'})
WITH n.prop AS n2
RETURN n2.prop
"""
Then a TypeError should be raised at runtime: PropertyAccessOnNonMap
Scenario: Failing when checking existence of a non-property and non-pattern
When executing query:
"""
MATCH (n)
RETURN exists(n.prop + 1)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentExpression
Scenario: Bad arguments for `range()`
When executing query:
"""
RETURN range(2, 8, 0)
"""
Then a ArgumentError should be raised at runtime: NumberOutOfRange
Scenario: Fail for invalid Unicode hyphen in subtraction
When executing query:
"""
RETURN 42 41
"""
Then a SyntaxError should be raised at compile time: InvalidUnicodeCharacter
Scenario: Failing for `size()` on paths
When executing query:
"""
MATCH p = (a)-[*]->(b)
RETURN size(p)
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when using aggregation in list comprehension
When executing query:
"""
MATCH (n)
RETURN [x IN [1, 2, 3, 4, 5] | count(*)]
"""
Then a SyntaxError should be raised at compile time: InvalidAggregation
Scenario: Failing when using non-constants in SKIP
When executing query:
"""
MATCH (n)
RETURN n
SKIP n.count
"""
Then a SyntaxError should be raised at compile time: NonConstantExpression
Scenario: Failing when using negative value in SKIP
When executing query:
"""
MATCH (n)
RETURN n
SKIP -1
"""
Then a SyntaxError should be raised at compile time: NegativeIntegerArgument
Scenario: Failing when using non-constants in LIMIT
When executing query:
"""
MATCH (n)
RETURN n
LIMIT n.count
"""
Then a SyntaxError should be raised at compile time: NonConstantExpression
Scenario: Failing when using negative value in LIMIT
When executing query:
"""
MATCH (n)
RETURN n
LIMIT -1
"""
Then a SyntaxError should be raised at compile time: NegativeIntegerArgument
Scenario: Failing when using floating point in LIMIT
When executing query:
"""
MATCH (n)
RETURN n
LIMIT 1.7
"""
Then a SyntaxError should be raised at compile time: InvalidArgumentType
Scenario: Failing when creating relationship without type
When executing query:
"""
CREATE ()-->()
"""
Then a SyntaxError should be raised at compile time: NoSingleRelationshipType
Scenario: Failing when merging relationship without type
When executing query:
"""
CREATE (a), (b)
MERGE (a)-->(b)
"""
Then a SyntaxError should be raised at compile time: NoSingleRelationshipType
Scenario: Failing when merging relationship without type, no colon
When executing query:
"""
MATCH (a), (b)
MERGE (a)-[NO_COLON]->(b)
"""
Then a SyntaxError should be raised at compile time: NoSingleRelationshipType
Scenario: Failing when creating relationship with more than one type
When executing query:
"""
CREATE ()-[:A|:B]->()
"""
Then a SyntaxError should be raised at compile time: NoSingleRelationshipType
Scenario: Failing when merging relationship with more than one type
When executing query:
"""
CREATE (a), (b)
MERGE (a)-[:A|:B]->(b)
"""
Then a SyntaxError should be raised at compile time: NoSingleRelationshipType

View File

@ -0,0 +1,289 @@
#
# Copyright (c) 2015-2018 "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: SetAcceptance
Scenario: Setting a node property to null removes the existing property
Given an empty graph
And having executed:
"""
CREATE (:A {property1: 23, property2: 46})
"""
When executing query:
"""
MATCH (n:A)
SET n.property1 = null
RETURN n
"""
Then the result should be:
| n |
| (:A {property2: 46}) |
And the side effects should be:
| -properties | 1 |
Scenario: Setting a relationship property to null removes the existing property
Given an empty graph
And having executed:
"""
CREATE ()-[:REL {property1: 12, property2: 24}]->()
"""
When executing query:
"""
MATCH ()-[r]->()
SET r.property1 = null
RETURN r
"""
Then the result should be:
| r |
| [:REL {property2: 24}] |
And the side effects should be:
| -properties | 1 |
Scenario: Set a property
Given any graph
And having executed:
"""
CREATE (:A {name: 'Andres'})
"""
When executing query:
"""
MATCH (n:A)
WHERE n.name = 'Andres'
SET n.name = 'Michael'
RETURN n
"""
Then the result should be:
| n |
| (:A {name: 'Michael'}) |
And the side effects should be:
| +properties | 1 |
| -properties | 1 |
Scenario: Set a property to an expression
Given an empty graph
And having executed:
"""
CREATE (:A {name: 'Andres'})
"""
When executing query:
"""
MATCH (n:A)
WHERE n.name = 'Andres'
SET n.name = n.name + ' was here'
RETURN n
"""
Then the result should be:
| n |
| (:A {name: 'Andres was here'}) |
And the side effects should be:
| +properties | 1 |
| -properties | 1 |
Scenario: Set a property by selecting the node using a simple expression
Given an empty graph
And having executed:
"""
CREATE (:A)
"""
When executing query:
"""
MATCH (n:A)
SET (n).name = 'neo4j'
RETURN n
"""
Then the result should be:
| n |
| (:A {name: 'neo4j'}) |
And the side effects should be:
| +properties | 1 |
Scenario: Set a property by selecting the relationship using a simple expression
Given an empty graph
And having executed:
"""
CREATE ()-[:REL]->()
"""
When executing query:
"""
MATCH ()-[r:REL]->()
SET (r).name = 'neo4j'
RETURN r
"""
Then the result should be:
| r |
| [:REL {name: 'neo4j'}] |
And the side effects should be:
| +properties | 1 |
Scenario: Setting a property to null removes the property
Given an empty graph
And having executed:
"""
CREATE (:A {name: 'Michael', age: 35})
"""
When executing query:
"""
MATCH (n)
WHERE n.name = 'Michael'
SET n.name = null
RETURN n
"""
Then the result should be:
| n |
| (:A {age: 35}) |
And the side effects should be:
| -properties | 1 |
Scenario: Add a label to a node
Given an empty graph
And having executed:
"""
CREATE (:A)
"""
When executing query:
"""
MATCH (n:A)
SET n:Foo
RETURN n
"""
Then the result should be:
| n |
| (:A:Foo) |
And the side effects should be:
| +labels | 1 |
Scenario: Adding a list property
Given an empty graph
And having executed:
"""
CREATE (:A)
"""
When executing query:
"""
MATCH (n:A)
SET n.x = [1, 2, 3]
RETURN [i IN n.x | i / 2.0] AS x
"""
Then the result should be:
| x |
| [0.5, 1.0, 1.5] |
And the side effects should be:
| +properties | 1 |
Scenario: Concatenate elements onto a list property
Given any graph
When executing query:
"""
CREATE (a {foo: [1, 2, 3]})
SET a.foo = a.foo + [4, 5]
RETURN a.foo
"""
Then the result should be:
| a.foo |
| [1, 2, 3, 4, 5] |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Concatenate elements in reverse onto a list property
Given any graph
When executing query:
"""
CREATE (a {foo: [3, 4, 5]})
SET a.foo = [1, 2] + a.foo
RETURN a.foo
"""
Then the result should be:
| a.foo |
| [1, 2, 3, 4, 5] |
And the side effects should be:
| +nodes | 1 |
| +properties | 1 |
Scenario: Overwrite values when using +=
Given an empty graph
And having executed:
"""
CREATE (:X {foo: 'A', bar: 'B'})
"""
When executing query:
"""
MATCH (n:X {foo: 'A'})
SET n += {bar: 'C'}
RETURN n
"""
Then the result should be:
| n |
| (:X {foo: 'A', bar: 'C'}) |
And the side effects should be:
| +properties | 1 |
| -properties | 1 |
Scenario: Retain old values when using +=
Given an empty graph
And having executed:
"""
CREATE (:X {foo: 'A'})
"""
When executing query:
"""
MATCH (n:X {foo: 'A'})
SET n += {bar: 'B'}
RETURN n
"""
Then the result should be:
| n |
| (:X {foo: 'A', bar: 'B'}) |
And the side effects should be:
| +properties | 1 |
Scenario: Explicit null values in a map remove old values
Given an empty graph
And having executed:
"""
CREATE (:X {foo: 'A', bar: 'B'})
"""
When executing query:
"""
MATCH (n:X {foo: 'A'})
SET n += {foo: null}
RETURN n
"""
Then the result should be:
| n |
| (:X {bar: 'B'}) |
And the side effects should be:
| -properties | 1 |
Scenario: Non-existent values in a property map are removed with SET =
Given an empty graph
And having executed:
"""
CREATE (:X {foo: 'A', bar: 'B'})
"""
When executing query:
"""
MATCH (n:X {foo: 'A'})
SET n = {foo: 'B', baz: 'C'}
RETURN n
"""
Then the result should be:
| n |
| (:X {foo: 'B', baz: 'C'}) |
And the side effects should be:
| +properties | 2 |
| -properties | 2 |

View File

@ -0,0 +1,71 @@
#
# Copyright (c) 2015-2018 "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: SkipLimitAcceptanceTest
Background:
Given any graph
Scenario: SKIP with an expression that depends on variables should fail
When executing query:
"""
MATCH (n) RETURN n SKIP n.count
"""
Then a SyntaxError should be raised at compile time: NonConstantExpression
Scenario: LIMIT with an expression that depends on variables should fail
When executing query:
"""
MATCH (n) RETURN n LIMIT n.count
"""
Then a SyntaxError should be raised at compile time: NonConstantExpression
Scenario: SKIP with an expression that does not depend on variables
And having executed:
"""
UNWIND range(1, 10) AS i
CREATE ({nr: i})
"""
When executing query:
"""
MATCH (n)
WITH n SKIP toInteger(rand()*9)
WITH count(*) AS count
RETURN count > 0 AS nonEmpty
"""
Then the result should be:
| nonEmpty |
| true |
And no side effects
Scenario: LIMIT with an expression that does not depend on variables
And having executed:
"""
UNWIND range(1, 3) AS i
CREATE ({nr: i})
"""
When executing query:
"""
MATCH (n)
WITH n LIMIT toInteger(ceil(1.7))
RETURN count(*) AS count
"""
Then the result should be:
| count |
| 2 |
And no side effects

View File

@ -0,0 +1,76 @@
#
# Copyright (c) 2015-2018 "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: StartingPointAcceptance
Scenario: Find all nodes
Given an empty graph
And having executed:
"""
CREATE ({name: 'a'}),
({name: 'b'}),
({name: 'c'})
"""
When executing query:
"""
MATCH (n)
RETURN n
"""
Then the result should be:
| n |
| ({name: 'a'}) |
| ({name: 'b'}) |
| ({name: 'c'}) |
And no side effects
Scenario: Find labelled nodes
Given an empty graph
And having executed:
"""
CREATE ({name: 'a'}),
(:Person),
(:Animal),
(:Animal)
"""
When executing query:
"""
MATCH (n:Animal)
RETURN n
"""
Then the result should be:
| n |
| (:Animal) |
| (:Animal) |
And no side effects
Scenario: Find nodes by property
Given an empty graph
And having executed:
"""
CREATE ({prop: 1}),
({prop: 2})
"""
When executing query:
"""
MATCH (n)
WHERE n.prop = 2
RETURN n
"""
Then the result should be:
| n |
| ({prop: 2}) |
And no side effects

View File

@ -0,0 +1,360 @@
#
# Copyright (c) 2015-2018 "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: StartsWithAcceptance
Background:
Given an empty graph
And having executed:
"""
CREATE (:Label {name: 'ABCDEF'}), (:Label {name: 'AB'}),
(:Label {name: 'abcdef'}), (:Label {name: 'ab'}),
(:Label {name: ''}), (:Label)
"""
Scenario: Finding exact matches
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH 'ABCDEF'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
And no side effects
Scenario: Finding beginning of string
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH 'ABC'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
And no side effects
Scenario: Finding end of string 1
When executing query:
"""
MATCH (a)
WHERE a.name ENDS WITH 'DEF'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
And no side effects
Scenario: Finding end of string 2
When executing query:
"""
MATCH (a)
WHERE a.name ENDS WITH 'AB'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'AB'}) |
And no side effects
Scenario: Finding middle of string
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH 'a'
AND a.name ENDS WITH 'f'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'abcdef'}) |
And no side effects
Scenario: Finding the empty string
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH ''
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
| (:Label {name: 'AB'}) |
| (:Label {name: 'abcdef'}) |
| (:Label {name: 'ab'}) |
| (:Label {name: ''}) |
And no side effects
Scenario: Finding when the middle is known
When executing query:
"""
MATCH (a)
WHERE a.name CONTAINS 'CD'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
And no side effects
Scenario: Finding strings starting with whitespace
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH ' '
RETURN a.name AS name
"""
Then the result should be:
| name |
| ' Foo ' |
And no side effects
Scenario: Finding strings starting with newline
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH '\n'
RETURN a.name AS name
"""
Then the result should be:
| name |
| '\nFoo\n' |
And no side effects
Scenario: Finding strings ending with newline
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name ENDS WITH '\n'
RETURN a.name AS name
"""
Then the result should be:
| name |
| '\nFoo\n' |
And no side effects
Scenario: Finding strings ending with whitespace
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name ENDS WITH ' '
RETURN a.name AS name
"""
Then the result should be:
| name |
| ' Foo ' |
And no side effects
Scenario: Finding strings containing whitespace
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name CONTAINS ' '
RETURN a.name AS name
"""
Then the result should be:
| name |
| ' Foo ' |
And no side effects
Scenario: Finding strings containing newline
And having executed:
"""
CREATE (:Label {name: ' Foo '}),
(:Label {name: '\nFoo\n'}),
(:Label {name: '\tFoo\t'})
"""
When executing query:
"""
MATCH (a)
WHERE a.name CONTAINS '\n'
RETURN a.name AS name
"""
Then the result should be:
| name |
| '\nFoo\n' |
And no side effects
Scenario: No string starts with null
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: No string does not start with null
When executing query:
"""
MATCH (a)
WHERE NOT a.name STARTS WITH null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: No string ends with null
When executing query:
"""
MATCH (a)
WHERE a.name ENDS WITH null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: No string does not end with null
When executing query:
"""
MATCH (a)
WHERE NOT a.name ENDS WITH null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: No string contains null
When executing query:
"""
MATCH (a)
WHERE a.name CONTAINS null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: No string does not contain null
When executing query:
"""
MATCH (a)
WHERE NOT a.name CONTAINS null
RETURN a
"""
Then the result should be:
| a |
And no side effects
Scenario: Combining string operators
When executing query:
"""
MATCH (a)
WHERE a.name STARTS WITH 'A'
AND a.name CONTAINS 'C'
AND a.name ENDS WITH 'EF'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
And no side effects
Scenario: NOT with CONTAINS
When executing query:
"""
MATCH (a)
WHERE NOT a.name CONTAINS 'b'
RETURN a
"""
Then the result should be:
| a |
| (:Label {name: 'ABCDEF'}) |
| (:Label {name: 'AB'}) |
| (:Label {name: ''}) |
And no side effects
Scenario: Handling non-string operands for STARTS WITH
When executing query:
"""
WITH [1, 3.14, true, [], {}, null] AS operands
UNWIND operands AS op1
UNWIND operands AS op2
WITH op1 STARTS WITH op2 AS v
RETURN v, count(*)
"""
Then the result should be:
| v | count(*) |
| null | 36 |
And no side effects
Scenario: Handling non-string operands for CONTAINS
When executing query:
"""
WITH [1, 3.14, true, [], {}, null] AS operands
UNWIND operands AS op1
UNWIND operands AS op2
WITH op1 STARTS WITH op2 AS v
RETURN v, count(*)
"""
Then the result should be:
| v | count(*) |
| null | 36 |
And no side effects
Scenario: Handling non-string operands for ENDS WITH
When executing query:
"""
WITH [1, 3.14, true, [], {}, null] AS operands
UNWIND operands AS op1
UNWIND operands AS op2
WITH op1 STARTS WITH op2 AS v
RETURN v, count(*)
"""
Then the result should be:
| v | count(*) |
| null | 36 |
And no side effects

View File

@ -0,0 +1,50 @@
#
# Copyright (c) 2015-2018 "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: SyntaxErrorAcceptance
Background:
Given any graph
Scenario: Using a non-existent function
When executing query:
"""
MATCH (a)
RETURN foo(a)
"""
Then a SyntaxError should be raised at compile time: UnknownFunction
Scenario: Using `rand()` in aggregations
When executing query:
"""
RETURN count(rand())
"""
Then a SyntaxError should be raised at compile time: NonConstantExpression
Scenario: Supplying invalid hexadecimal literal 1
When executing query:
"""
RETURN 0x23G34
"""
Then a SyntaxError should be raised at compile time: InvalidNumberLiteral
Scenario: Supplying invalid hexadecimal literal 2
When executing query:
"""
RETURN 0x23j
"""
Then a SyntaxError should be raised at compile time: InvalidNumberLiteral

View File

@ -0,0 +1,161 @@
#
# Copyright (c) 2015-2018 "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: TernaryLogicAcceptanceTest
Background:
Given any graph
Scenario: The inverse of a null is a null
When executing query:
"""
RETURN NOT null AS value
"""
Then the result should be:
| value |
| null |
And no side effects
Scenario: A literal null IS null
When executing query:
"""
RETURN null IS NULL AS value
"""
Then the result should be:
| value |
| true |
And no side effects
Scenario: A literal null is not IS NOT null
When executing query:
"""
RETURN null IS NOT NULL AS value
"""
Then the result should be:
| value |
| false |
And no side effects
Scenario: It is unknown - i.e. null - if a null is equal to a null
When executing query:
"""
RETURN null = null AS value
"""
Then the result should be:
| value |
| null |
And no side effects
Scenario: It is unknown - i.e. null - if a null is not equal to a null
When executing query:
"""
RETURN null <> null AS value
"""
Then the result should be:
| value |
| null |
And no side effects
Scenario Outline: Using null in AND
And parameters are:
| par | val |
| lhs | <lhs> |
| rhs | <rhs> |
When executing query:
"""
RETURN $lhs AND $rhs AS result
"""
Then the result should be:
| result |
| <result> |
And no side effects
Examples:
| lhs | rhs | result |
| null | null | null |
| null | true | null |
| true | null | null |
| null | false | false |
| false | null | false |
Scenario Outline: Using null in OR
And parameters are:
| par | val |
| lhs | <lhs> |
| rhs | <rhs> |
When executing query:
"""
RETURN $lhs OR $rhs AS result
"""
Then the result should be:
| result |
| <result> |
And no side effects
Examples:
| lhs | rhs | result |
| null | null | null |
| null | true | true |
| true | null | true |
| null | false | null |
| false | null | null |
Scenario Outline: Using null in XOR
And parameters are:
| par | val |
| lhs | <lhs> |
| rhs | <rhs> |
When executing query:
"""
RETURN $lhs XOR $rhs AS result
"""
Then the result should be:
| result |
| <result> |
And no side effects
Examples:
| lhs | rhs | result |
| null | null | null |
| null | true | null |
| true | null | null |
| null | false | null |
| false | null | null |
Scenario Outline: Using null in IN
And parameters are:
| par | val |
| elt | <elt> |
| coll | <coll> |
When executing query:
"""
RETURN $elt IN $coll AS result
"""
Then the result should be:
| result |
| <result> |
And no side effects
Examples:
| elt | coll | result |
| null | null | null |
| null | [1, 2, 3] | null |
| null | [1, 2, 3, null] | null |
| null | [] | false |
| 1 | [1, 2, 3, null] | true |
| 1 | [null, 1] | true |
| 5 | [1, 2, 3, null] | null |

View File

@ -0,0 +1,327 @@
#
# Copyright (c) 2015-2018 "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: TriadicSelection
Scenario: Handling triadic friend of a friend
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
| 'b3' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with different relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:FOLLOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with superset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with implicit subset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'b4' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
| 'c31' |
| 'c32' |
| 'c41' |
| 'c42' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with explicit subset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS|FOLLOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'b4' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
| 'c31' |
| 'c32' |
| 'c41' |
| 'c42' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with same labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c:X)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'c11' |
| 'c21' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with different labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c:Y)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'c12' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with implicit subset of labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c:X)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'c11' |
| 'c21' |
And no side effects
Scenario: Handling triadic friend of a friend that is not a friend with implicit superset of labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
| 'c11' |
| 'c12' |
| 'c21' |
| 'c22' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with different relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:FOLLOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b3' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with superset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
| 'b3' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with implicit subset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b1' |
| 'b2' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with explicit subset of relationship type
Given the binary-tree-1 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS|FOLLOWS]->(b)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b1' |
| 'b2' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with same labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c:X)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with different labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c:Y)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with implicit subset of labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b)-->(c:X)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
And no side effects
Scenario: Handling triadic friend of a friend that is a friend with implicit superset of labels
Given the binary-tree-2 graph
When executing query:
"""
MATCH (a:A)-[:KNOWS]->(b:X)-->(c)
OPTIONAL MATCH (a)-[r:KNOWS]->(c)
WITH c WHERE r IS NOT NULL
RETURN c.name
"""
Then the result should be:
| c.name |
| 'b2' |
And no side effects

View File

@ -0,0 +1,416 @@
#
# Copyright (c) 2015-2018 "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: TypeConversionFunctions
Scenario: `toBoolean()` on valid literal string
Given any graph
When executing query:
"""
RETURN toBoolean('true') AS b
"""
Then the result should be:
| b |
| true |
And no side effects
Scenario: `toBoolean()` on booleans
Given any graph
When executing query:
"""
UNWIND [true, false] AS b
RETURN toBoolean(b) AS b
"""
Then the result should be:
| b |
| true |
| false |
And no side effects
Scenario: `toBoolean()` on variables with valid string values
Given any graph
When executing query:
"""
UNWIND ['true', 'false'] AS s
RETURN toBoolean(s) AS b
"""
Then the result should be:
| b |
| true |
| false |
And no side effects
Scenario: `toBoolean()` on invalid strings
Given any graph
When executing query:
"""
UNWIND [null, '', ' tru ', 'f alse'] AS things
RETURN toBoolean(things) AS b
"""
Then the result should be:
| b |
| null |
| null |
| null |
| null |
And no side effects
Scenario Outline: `toBoolean()` on invalid types
Given any graph
When executing query:
"""
WITH [true, <invalid>] AS list
RETURN toBoolean(list[1]) AS b
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Examples:
| invalid |
| [] |
| {} |
| 1 |
| 1.0 |
Scenario: `toInteger()`
Given an empty graph
And having executed:
"""
CREATE (:Person {age: '42'})
"""
When executing query:
"""
MATCH (p:Person { age: '42' })
WITH *
MATCH (n)
RETURN toInteger(n.age) AS age
"""
Then the result should be:
| age |
| 42 |
And no side effects
Scenario: `toInteger()` on float
Given any graph
When executing query:
"""
WITH 82.9 AS weight
RETURN toInteger(weight)
"""
Then the result should be:
| toInteger(weight) |
| 82 |
And no side effects
Scenario: `toInteger()` returning null on non-numerical string
Given any graph
When executing query:
"""
WITH 'foo' AS foo_string, '' AS empty_string
RETURN toInteger(foo_string) AS foo, toInteger(empty_string) AS empty
"""
Then the result should be:
| foo | empty |
| null | null |
And no side effects
Scenario: `toInteger()` handling mixed number types
Given any graph
When executing query:
"""
WITH [2, 2.9] AS numbers
RETURN [n IN numbers | toInteger(n)] AS int_numbers
"""
Then the result should be:
| int_numbers |
| [2, 2] |
And no side effects
Scenario: `toInteger()` handling Any type
Given any graph
When executing query:
"""
WITH [2, 2.9, '1.7'] AS things
RETURN [n IN things | toInteger(n)] AS int_numbers
"""
Then the result should be:
| int_numbers |
| [2, 2, 1] |
And no side effects
Scenario: `toInteger()` on a list of strings
Given any graph
When executing query:
"""
WITH ['2', '2.9', 'foo'] AS numbers
RETURN [n IN numbers | toInteger(n)] AS int_numbers
"""
Then the result should be:
| int_numbers |
| [2, 2, null] |
And no side effects
Scenario: `toInteger()` on a complex-typed expression
Given any graph
And parameters are:
| param | 1 |
When executing query:
"""
RETURN toInteger(1 - $param) AS result
"""
Then the result should be:
| result |
| 0 |
And no side effects
Scenario Outline: `toInteger()` failing on invalid arguments
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH p = (n)-[r:T]->()
RETURN [x IN [1, <invalid>] | toInteger(x) ] AS list
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Examples:
| invalid |
| true |
| [] |
| {} |
| n |
| r |
| p |
Scenario: `toFloat()`
Given an empty graph
And having executed:
"""
CREATE (:Movie {rating: 4})
"""
When executing query:
"""
MATCH (m:Movie { rating: 4 })
WITH *
MATCH (n)
RETURN toFloat(n.rating) AS float
"""
Then the result should be:
| float |
| 4.0 |
And no side effects
Scenario: `toFloat()` on mixed number types
Given any graph
When executing query:
"""
WITH [3.4, 3] AS numbers
RETURN [n IN numbers | toFloat(n)] AS float_numbers
"""
Then the result should be:
| float_numbers |
| [3.4, 3.0] |
And no side effects
Scenario: `toFloat()` returning null on non-numerical string
Given any graph
When executing query:
"""
WITH 'foo' AS foo_string, '' AS empty_string
RETURN toFloat(foo_string) AS foo, toFloat(empty_string) AS empty
"""
Then the result should be:
| foo | empty |
| null | null |
And no side effects
Scenario: `toFloat()` handling Any type
Given any graph
When executing query:
"""
WITH [3.4, 3, '5'] AS numbers
RETURN [n IN numbers | toFloat(n)] AS float_numbers
"""
Then the result should be:
| float_numbers |
| [3.4, 3.0, 5.0] |
And no side effects
Scenario: `toFloat()` on a list of strings
Given any graph
When executing query:
"""
WITH ['1', '2', 'foo'] AS numbers
RETURN [n IN numbers | toFloat(n)] AS float_numbers
"""
Then the result should be:
| float_numbers |
| [1.0, 2.0, null] |
And no side effects
Scenario Outline: `toFloat()` failing on invalid arguments
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH p = (n)-[r:T]->()
RETURN [x IN [1.0, <invalid>] | toFloat(x) ] AS list
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Examples:
| invalid |
| true |
| [] |
| {} |
| n |
| r |
| p |
Scenario: `toString()`
Given an empty graph
And having executed:
"""
CREATE (:Movie {rating: 4})
"""
When executing query:
"""
MATCH (m:Movie { rating: 4 })
WITH *
MATCH (n)
RETURN toString(n.rating)
"""
Then the result should be:
| toString(n.rating) |
| '4' |
And no side effects
Scenario: `toString()` handling boolean properties
Given an empty graph
And having executed:
"""
CREATE (:Movie {watched: true})
"""
When executing query:
"""
MATCH (m:Movie)
RETURN toString(m.watched)
"""
Then the result should be:
| toString(m.watched) |
| 'true' |
And no side effects
Scenario: `toString()` handling inlined boolean
Given any graph
When executing query:
"""
RETURN toString(1 < 0) AS bool
"""
Then the result should be:
| bool |
| 'false' |
And no side effects
Scenario: `toString()` handling boolean literal
Given any graph
When executing query:
"""
RETURN toString(true) AS bool
"""
Then the result should be:
| bool |
| 'true' |
And no side effects
Scenario: `toString()` should work on Any type
Given any graph
When executing query:
"""
RETURN [x IN [1, 2.3, true, 'apa'] | toString(x) ] AS list
"""
Then the result should be:
| list |
| ['1', '2.3', 'true', 'apa'] |
And no side effects
Scenario: `toString()` on a list of integers
Given any graph
When executing query:
"""
WITH [1, 2, 3] AS numbers
RETURN [n IN numbers | toString(n)] AS string_numbers
"""
Then the result should be:
| string_numbers |
| ['1', '2', '3'] |
And no side effects
Scenario Outline: `toString()` failing on invalid arguments
Given an empty graph
And having executed:
"""
CREATE ()-[:T]->()
"""
When executing query:
"""
MATCH p = (n)-[r:T]->()
RETURN [x IN [1, '', <invalid>] | toString(x) ] AS list
"""
Then a TypeError should be raised at runtime: InvalidArgumentValue
Examples:
| invalid |
| [] |
| {} |
| n |
| r |
| p |
Scenario: `toString()` should accept potentially correct types 1
Given any graph
When executing query:
"""
UNWIND ['male', 'female', null] AS gen
RETURN coalesce(toString(gen), 'x') AS result
"""
Then the result should be:
| result |
| 'male' |
| 'female' |
| 'x' |
And no side effects
Scenario: `toString()` should accept potentially correct types 2
Given any graph
When executing query:
"""
UNWIND ['male', 'female', null] AS gen
RETURN toString(coalesce(gen, 'x')) AS result
"""
Then the result should be:
| result |
| 'male' |
| 'female' |
| 'x' |
And no side effects

View File

@ -0,0 +1,99 @@
#
# Copyright (c) 2015-2018 "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: UnionAcceptance
Scenario: Should be able to create text output from union queries
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a:A)
RETURN a AS a
UNION
MATCH (b:B)
RETURN b AS a
"""
Then the result should be:
| a |
| (:A) |
| (:B) |
And no side effects
Scenario: Two elements, both unique, not distinct
Given an empty graph
When executing query:
"""
RETURN 1 AS x
UNION ALL
RETURN 2 AS x
"""
Then the result should be:
| x |
| 1 |
| 2 |
And no side effects
Scenario: Two elements, both unique, distinct
Given an empty graph
When executing query:
"""
RETURN 1 AS x
UNION
RETURN 2 AS x
"""
Then the result should be:
| x |
| 1 |
| 2 |
And no side effects
Scenario: Three elements, two unique, distinct
Given an empty graph
When executing query:
"""
RETURN 2 AS x
UNION
RETURN 1 AS x
UNION
RETURN 2 AS x
"""
Then the result should be:
| x |
| 2 |
| 1 |
And no side effects
Scenario: Three elements, two unique, not distinct
Given an empty graph
When executing query:
"""
RETURN 2 AS x
UNION ALL
RETURN 1 AS x
UNION ALL
RETURN 2 AS x
"""
Then the result should be:
| x |
| 2 |
| 1 |
| 2 |
And no side effects

View File

@ -0,0 +1,268 @@
#
# Copyright (c) 2015-2018 "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: UnwindAcceptance
Scenario: Unwinding a list
Given any graph
When executing query:
"""
UNWIND [1, 2, 3] AS x
RETURN x
"""
Then the result should be:
| x |
| 1 |
| 2 |
| 3 |
And no side effects
Scenario: Unwinding a range
Given any graph
When executing query:
"""
UNWIND range(1, 3) AS x
RETURN x
"""
Then the result should be:
| x |
| 1 |
| 2 |
| 3 |
And no side effects
Scenario: Unwinding a concatenation of lists
Given any graph
When executing query:
"""
WITH [1, 2, 3] AS first, [4, 5, 6] AS second
UNWIND (first + second) AS x
RETURN x
"""
Then the result should be:
| x |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
And no side effects
Scenario: Unwinding a collected unwound expression
Given any graph
When executing query:
"""
UNWIND RANGE(1, 2) AS row
WITH collect(row) AS rows
UNWIND rows AS x
RETURN x
"""
Then the result should be:
| x |
| 1 |
| 2 |
And no side effects
Scenario: Unwinding a collected expression
Given an empty graph
And having executed:
"""
CREATE ({id: 1}), ({id: 2})
"""
When executing query:
"""
MATCH (row)
WITH collect(row) AS rows
UNWIND rows AS node
RETURN node.id
"""
Then the result should be:
| node.id |
| 1 |
| 2 |
And no side effects
Scenario: Creating nodes from an unwound parameter list
Given an empty graph
And having executed:
"""
CREATE (:Year {year: 2016})
"""
And parameters are:
| events | [{year: 2016, id: 1}, {year: 2016, id: 2}] |
When executing query:
"""
UNWIND $events AS event
MATCH (y:Year {year: event.year})
MERGE (e:Event {id: event.id})
MERGE (y)<-[:IN]-(e)
RETURN e.id AS x
ORDER BY x
"""
Then the result should be, in order:
| x |
| 1 |
| 2 |
And the side effects should be:
| +nodes | 2 |
| +relationships | 2 |
| +labels | 1 |
| +properties | 2 |
Scenario: Double unwinding a list of lists
Given any graph
When executing query:
"""
WITH [[1, 2, 3], [4, 5, 6]] AS lol
UNWIND lol AS x
UNWIND x AS y
RETURN y
"""
Then the result should be:
| y |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
And no side effects
Scenario: Unwinding the empty list
Given any graph
When executing query:
"""
UNWIND [] AS empty
RETURN empty
"""
Then the result should be:
| empty |
And no side effects
Scenario: Unwinding null
Given any graph
When executing query:
"""
UNWIND null AS nil
RETURN nil
"""
Then the result should be:
| nil |
And no side effects
Scenario: Unwinding list with duplicates
Given any graph
When executing query:
"""
UNWIND [1, 1, 2, 2, 3, 3, 4, 4, 5, 5] AS duplicate
RETURN duplicate
"""
Then the result should be:
| duplicate |
| 1 |
| 1 |
| 2 |
| 2 |
| 3 |
| 3 |
| 4 |
| 4 |
| 5 |
| 5 |
And no side effects
Scenario: Unwind does not prune context
Given any graph
When executing query:
"""
WITH [1, 2, 3] AS list
UNWIND list AS x
RETURN *
"""
Then the result should be:
| list | x |
| [1, 2, 3] | 1 |
| [1, 2, 3] | 2 |
| [1, 2, 3] | 3 |
And no side effects
Scenario: Unwind does not remove variables from scope
Given an empty graph
And having executed:
"""
CREATE (s:S),
(n),
(e:E),
(s)-[:X]->(e),
(s)-[:Y]->(e),
(n)-[:Y]->(e)
"""
When executing query:
"""
MATCH (a:S)-[:X]->(b1)
WITH a, collect(b1) AS bees
UNWIND bees AS b2
MATCH (a)-[:Y]->(b2)
RETURN a, b2
"""
Then the result should be:
| a | b2 |
| (:S) | (:E) |
And no side effects
Scenario: Multiple unwinds after each other
Given any graph
When executing query:
"""
WITH [1, 2] AS xs, [3, 4] AS ys, [5, 6] AS zs
UNWIND xs AS x
UNWIND ys AS y
UNWIND zs AS z
RETURN *
"""
Then the result should be:
| x | xs | y | ys | z | zs |
| 1 | [1, 2] | 3 | [3, 4] | 5 | [5, 6] |
| 1 | [1, 2] | 3 | [3, 4] | 6 | [5, 6] |
| 1 | [1, 2] | 4 | [3, 4] | 5 | [5, 6] |
| 1 | [1, 2] | 4 | [3, 4] | 6 | [5, 6] |
| 2 | [1, 2] | 3 | [3, 4] | 5 | [5, 6] |
| 2 | [1, 2] | 3 | [3, 4] | 6 | [5, 6] |
| 2 | [1, 2] | 4 | [3, 4] | 5 | [5, 6] |
| 2 | [1, 2] | 4 | [3, 4] | 6 | [5, 6] |
And no side effects
Scenario: Unwind with merge
Given an empty graph
And parameters are:
| props | [{login: 'login1', name: 'name1'}, {login: 'login2', name: 'name2'}] |
When executing query:
"""
UNWIND $props AS prop
MERGE (p:Person {login: prop.login})
SET p.name = prop.name
RETURN p.name, p.login
"""
Then the result should be:
| p.name | p.login |
| 'name1' | 'login1' |
| 'name2' | 'login2' |
And the side effects should be:
| +nodes | 2 |
| +labels | 1 |
| +properties | 4 |

View File

@ -0,0 +1,657 @@
#
# Copyright (c) 2015-2018 "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: VarLengthAcceptance
# TODO: Replace this with a named graph (or two)
Background:
Given an empty graph
And having executed:
"""
CREATE (n0:A {name: 'n0'}),
(n00:B {name: 'n00'}),
(n01:B {name: 'n01'}),
(n000:C {name: 'n000'}),
(n001:C {name: 'n001'}),
(n010:C {name: 'n010'}),
(n011:C {name: 'n011'}),
(n0000:D {name: 'n0000'}),
(n0001:D {name: 'n0001'}),
(n0010:D {name: 'n0010'}),
(n0011:D {name: 'n0011'}),
(n0100:D {name: 'n0100'}),
(n0101:D {name: 'n0101'}),
(n0110:D {name: 'n0110'}),
(n0111:D {name: 'n0111'})
CREATE (n0)-[:LIKES]->(n00),
(n0)-[:LIKES]->(n01),
(n00)-[:LIKES]->(n000),
(n00)-[:LIKES]->(n001),
(n01)-[:LIKES]->(n010),
(n01)-[:LIKES]->(n011),
(n000)-[:LIKES]->(n0000),
(n000)-[:LIKES]->(n0001),
(n001)-[:LIKES]->(n0010),
(n001)-[:LIKES]->(n0011),
(n010)-[:LIKES]->(n0100),
(n010)-[:LIKES]->(n0101),
(n011)-[:LIKES]->(n0110),
(n011)-[:LIKES]->(n0111)
"""
Scenario: Handling unbounded variable length match
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling explicitly unbounded variable length match
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*..]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Fail when asterisk operator is missing
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES..]->(c)
RETURN c.name
"""
Then a SyntaxError should be raised at compile time: InvalidRelationshipPattern
Scenario: Handling single bounded variable length match 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*0]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0' |
And no side effects
Scenario: Handling single bounded variable length match 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
And no side effects
Scenario: Handling single bounded variable length match 3
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling upper and lower bounded variable length match 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*0..2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0' |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling upper and lower bounded variable length match 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1..2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling symmetrically bounded variable length match, bounds are zero
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*0..0]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0' |
And no side effects
Scenario: Handling symmetrically bounded variable length match, bounds are one
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1..1]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
And no side effects
Scenario: Handling symmetrically bounded variable length match, bounds are two
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*2..2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Fail on negative bound
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*-2]->(c)
RETURN c.name
"""
Then a SyntaxError should be raised at compile time: InvalidRelationshipPattern
Scenario: Handling upper and lower bounded variable length match, empty interval 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*2..1]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
And no side effects
Scenario: Handling upper and lower bounded variable length match, empty interval 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1..0]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
And no side effects
Scenario: Handling upper bounded variable length match, empty interval
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*..0]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
And no side effects
Scenario: Handling upper bounded variable length match 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*..1]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
And no side effects
Scenario: Handling upper bounded variable length match 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*..2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling lower bounded variable length match 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*0..]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0' |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling lower bounded variable length match 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1..]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling lower bounded variable length match 3
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*2..]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, zero length 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*0]->()-[:LIKES]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, zero length 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES]->()-[:LIKES*0]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00' |
| 'n01' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, single length 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*1]->()-[:LIKES]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, single length 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES]->()-[:LIKES*1]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n000' |
| 'n001' |
| 'n010' |
| 'n011' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, longer 1
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES*2]->()-[:LIKES]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, longer 2
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES]->()-[:LIKES*2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n0000' |
| 'n0001' |
| 'n0010' |
| 'n0011' |
| 'n0100' |
| 'n0101' |
| 'n0110' |
| 'n0111' |
And no side effects
Scenario: Handling a variable length relationship and a standard relationship in chain, longer 3
And having executed:
"""
MATCH (d:D)
CREATE (e1:E {name: d.name + '0'}),
(e2:E {name: d.name + '1'})
CREATE (d)-[:LIKES]->(e1),
(d)-[:LIKES]->(e2)
"""
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES]->()-[:LIKES*3]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00000' |
| 'n00001' |
| 'n00010' |
| 'n00011' |
| 'n00100' |
| 'n00101' |
| 'n00110' |
| 'n00111' |
| 'n01000' |
| 'n01001' |
| 'n01010' |
| 'n01011' |
| 'n01100' |
| 'n01101' |
| 'n01110' |
| 'n01111' |
And no side effects
Scenario: Handling mixed relationship patterns and directions 1
And having executed:
"""
MATCH (a:A)-[r]->(b)
DELETE r
CREATE (b)-[:LIKES]->(a)
"""
And having executed:
"""
MATCH (d:D)
CREATE (e1:E {name: d.name + '0'}),
(e2:E {name: d.name + '1'})
CREATE (d)-[:LIKES]->(e1),
(d)-[:LIKES]->(e2)
"""
When executing query:
"""
MATCH (a:A)
MATCH (a)<-[:LIKES]-()-[:LIKES*3]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00000' |
| 'n00001' |
| 'n00010' |
| 'n00011' |
| 'n00100' |
| 'n00101' |
| 'n00110' |
| 'n00111' |
| 'n01000' |
| 'n01001' |
| 'n01010' |
| 'n01011' |
| 'n01100' |
| 'n01101' |
| 'n01110' |
| 'n01111' |
And no side effects
Scenario: Handling mixed relationship patterns and directions 2
# This gets hard to follow for a human mind. The answer is named graphs, but it's not crucial to fix.
And having executed:
"""
MATCH (a)-[r]->(b)
WHERE NOT a:A
DELETE r
CREATE (b)-[:LIKES]->(a)
"""
And having executed:
"""
MATCH (d:D)
CREATE (e1:E {name: d.name + '0'}),
(e2:E {name: d.name + '1'})
CREATE (d)-[:LIKES]->(e1),
(d)-[:LIKES]->(e2)
"""
When executing query:
"""
MATCH (a:A)
MATCH (a)-[:LIKES]->()<-[:LIKES*3]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00000' |
| 'n00001' |
| 'n00010' |
| 'n00011' |
| 'n00100' |
| 'n00101' |
| 'n00110' |
| 'n00111' |
| 'n01000' |
| 'n01001' |
| 'n01010' |
| 'n01011' |
| 'n01100' |
| 'n01101' |
| 'n01110' |
| 'n01111' |
And no side effects
Scenario: Handling mixed relationship patterns 1
And having executed:
"""
MATCH (d:D)
CREATE (e1:E {name: d.name + '0'}),
(e2:E {name: d.name + '1'})
CREATE (d)-[:LIKES]->(e1),
(d)-[:LIKES]->(e2)
"""
When executing query:
"""
MATCH (a:A)
MATCH (p)-[:LIKES*1]->()-[:LIKES]->()-[r:LIKES*2]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00000' |
| 'n00001' |
| 'n00010' |
| 'n00011' |
| 'n00100' |
| 'n00101' |
| 'n00110' |
| 'n00111' |
| 'n01000' |
| 'n01001' |
| 'n01010' |
| 'n01011' |
| 'n01100' |
| 'n01101' |
| 'n01110' |
| 'n01111' |
And no side effects
Scenario: Handling mixed relationship patterns 2
And having executed:
"""
MATCH (d:D)
CREATE (e1:E {name: d.name + '0'}),
(e2:E {name: d.name + '1'})
CREATE (d)-[:LIKES]->(e1),
(d)-[:LIKES]->(e2)
"""
When executing query:
"""
MATCH (a:A)
MATCH (p)-[:LIKES]->()-[:LIKES*2]->()-[r:LIKES]->(c)
RETURN c.name
"""
Then the result should be:
| c.name |
| 'n00000' |
| 'n00001' |
| 'n00010' |
| 'n00011' |
| 'n00100' |
| 'n00101' |
| 'n00110' |
| 'n00111' |
| 'n01000' |
| 'n01001' |
| 'n01010' |
| 'n01011' |
| 'n01100' |
| 'n01101' |
| 'n01110' |
| 'n01111' |
And no side effects

View File

@ -0,0 +1,41 @@
#
# Copyright (c) 2015-2018 "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: VarLengthAcceptance2
Scenario: Handling relationships that are already bound in variable length paths
Given an empty graph
And having executed:
"""
CREATE (n0:Node),
(n1:Node),
(n2:Node),
(n3:Node),
(n0)-[:EDGE]->(n1),
(n1)-[:EDGE]->(n2),
(n2)-[:EDGE]->(n3)
"""
When executing query:
"""
MATCH ()-[r:EDGE]-()
MATCH p = (n)-[*0..1]-()-[r]-()-[*0..1]-(m)
RETURN count(p) AS c
"""
Then the result should be:
| c |
| 32 |
And no side effects

View File

@ -0,0 +1,35 @@
#
# Copyright (c) 2015-2018 "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: WhereAcceptance
Scenario: NOT and false
Given an empty graph
And having executed:
"""
CREATE ({name: 'a'})
"""
When executing query:
"""
MATCH (n)
WHERE NOT(n.name = 'apa' AND false)
RETURN n
"""
Then the result should be:
| n |
| ({name: 'a'}) |
And no side effects

View File

@ -0,0 +1,363 @@
#
# Copyright (c) 2015-2018 "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: WithAcceptance
Scenario: Passing on pattern nodes
Given an empty graph
And having executed:
"""
CREATE (:A)-[:REL]->(:B)
"""
When executing query:
"""
MATCH (a:A)
WITH a
MATCH (a)-->(b)
RETURN *
"""
Then the result should be:
| a | b |
| (:A) | (:B) |
And no side effects
Scenario: ORDER BY and LIMIT can be used
Given an empty graph
And having executed:
"""
CREATE (a:A), (), (), (),
(a)-[:REL]->()
"""
When executing query:
"""
MATCH (a:A)
WITH a
ORDER BY a.name
LIMIT 1
MATCH (a)-->(b)
RETURN a
"""
Then the result should be:
| a |
| (:A) |
And no side effects
Scenario: No dependencies between the query parts
Given an empty graph
And having executed:
"""
CREATE (:A), (:B)
"""
When executing query:
"""
MATCH (a)
WITH a
MATCH (b)
RETURN a, b
"""
Then the result should be:
| a | b |
| (:A) | (:A) |
| (:A) | (:B) |
| (:B) | (:A) |
| (:B) | (:B) |
And no side effects
Scenario: Aliasing
Given an empty graph
And having executed:
"""
CREATE (:Begin {prop: 42}),
(:End {prop: 42}),
(:End {prop: 3})
"""
When executing query:
"""
MATCH (a:Begin)
WITH a.prop AS property
MATCH (b:End)
WHERE property = b.prop
RETURN b
"""
Then the result should be:
| b |
| (:End {prop: 42}) |
And no side effects
Scenario: Handle dependencies across WITH
Given an empty graph
And having executed:
"""
CREATE (a:End {prop: 42, id: 0}),
(:End {prop: 3}),
(:Begin {prop: a.id})
"""
When executing query:
"""
MATCH (a:Begin)
WITH a.prop AS property
LIMIT 1
MATCH (b)
WHERE b.id = property
RETURN b
"""
Then the result should be:
| b |
| (:End {prop: 42, id: 0}) |
And no side effects
Scenario: Handle dependencies across WITH with SKIP
Given an empty graph
And having executed:
"""
CREATE (a {prop: 'A', key: 0, id: 0}),
({prop: 'B', key: a.id, id: 1}),
({prop: 'C', key: 0, id: 2})
"""
When executing query:
"""
MATCH (a)
WITH a.prop AS property, a.key AS idToUse
ORDER BY property
SKIP 1
MATCH (b)
WHERE b.id = idToUse
RETURN DISTINCT b
"""
Then the result should be:
| b |
| ({prop: 'A', key: 0, id: 0}) |
And no side effects
Scenario: WHERE after WITH should filter results
Given an empty graph
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'})
"""
When executing query:
"""
MATCH (a)
WITH a
WHERE a.name = 'B'
RETURN a
"""
Then the result should be:
| a |
| ({name: 'B'}) |
And no side effects
Scenario: WHERE after WITH can filter on top of an aggregation
Given an empty graph
And having executed:
"""
CREATE (a {name: 'A'}),
(b {name: 'B'})
CREATE (a)-[:REL]->(),
(a)-[:REL]->(),
(a)-[:REL]->(),
(b)-[:REL]->()
"""
When executing query:
"""
MATCH (a)-->()
WITH a, count(*) AS relCount
WHERE relCount > 1
RETURN a
"""
Then the result should be:
| a |
| ({name: 'A'}) |
And no side effects
Scenario: ORDER BY on an aggregating key
Given an empty graph
And having executed:
"""
CREATE ({bar: 'A'}),
({bar: 'A'}),
({bar: 'B'})
"""
When executing query:
"""
MATCH (a)
WITH a.bar AS bars, count(*) AS relCount
ORDER BY a.bar
RETURN *
"""
Then the result should be:
| bars | relCount |
| 'A' | 2 |
| 'B' | 1 |
And no side effects
Scenario: ORDER BY a DISTINCT column
Given an empty graph
And having executed:
"""
CREATE ({bar: 'A'}),
({bar: 'A'}),
({bar: 'B'})
"""
When executing query:
"""
MATCH (a)
WITH DISTINCT a.bar AS bars
ORDER BY a.bar
RETURN *
"""
Then the result should be:
| bars |
| 'A' |
| 'B' |
And no side effects
Scenario: WHERE on a DISTINCT column
Given an empty graph
And having executed:
"""
CREATE ({bar: 'A'}),
({bar: 'A'}),
({bar: 'B'})
"""
When executing query:
"""
MATCH (a)
WITH DISTINCT a.bar AS bars
WHERE a.bar = 'B'
RETURN *
"""
Then the result should be:
| bars |
| 'B' |
And no side effects
Scenario: A simple pattern with one bound endpoint
Given an empty graph
And having executed:
"""
CREATE (:A)-[:REL]->(:B)
"""
When executing query:
"""
MATCH (a:A)-[r:REL]->(b:B)
WITH a AS b, b AS tmp, r AS r
WITH b AS a, r
LIMIT 1
MATCH (a)-[r]->(b)
RETURN a, r, b
"""
Then the result should be:
| a | r | b |
| (:A) | [:REL] | (:B) |
And no side effects
Scenario: Null handling
Given an empty graph
When executing query:
"""
OPTIONAL MATCH (a:Start)
WITH a
MATCH (a)-->(b)
RETURN *
"""
Then the result should be:
| a | b |
And no side effects
Scenario: Nested maps
Given an empty graph
When executing query:
"""
WITH {foo: {bar: 'baz'}} AS nestedMap
RETURN nestedMap.foo.bar
"""
Then the result should be:
| nestedMap.foo.bar |
| 'baz' |
And no side effects
Scenario: Connected components succeeding WITH
Given an empty graph
And having executed:
"""
CREATE (:A)-[:REL]->(:X)
CREATE (:B)
"""
When executing query:
"""
MATCH (n:A)
WITH n
LIMIT 1
MATCH (m:B), (n)-->(x:X)
RETURN *
"""
Then the result should be:
| m | n | x |
| (:B) | (:A) | (:X) |
And no side effects
Scenario: Single WITH using a predicate and aggregation
Given an empty graph
And having executed:
"""
CREATE ({prop: 43}), ({prop: 42})
"""
When executing query:
"""
MATCH (n)
WITH n
WHERE n.prop = 42
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects
Scenario: Multiple WITHs using a predicate and aggregation
Given an empty graph
And having executed:
"""
CREATE (a {name: 'David'}),
(b {name: 'Other'}),
(c {name: 'NotOther'}),
(d {name: 'NotOther2'}),
(a)-[:REL]->(b),
(a)-[:REL]->(c),
(a)-[:REL]->(d),
(b)-[:REL]->(),
(b)-[:REL]->(),
(c)-[:REL]->(),
(c)-[:REL]->(),
(d)-[:REL]->()
"""
When executing query:
"""
MATCH (david {name: 'David'})--(otherPerson)-->()
WITH otherPerson, count(*) AS foaf
WHERE foaf > 1
WITH otherPerson
WHERE otherPerson.name <> 'NotOther'
RETURN count(*)
"""
Then the result should be:
| count(*) |
| 1 |
And no side effects

View File

@ -0,0 +1,29 @@
CREATE (a:A {name: 'a'}),
(b1:X {name: 'b1'}),
(b2:X {name: 'b2'}),
(b3:X {name: 'b3'}),
(b4:X {name: 'b4'}),
(c11:X {name: 'c11'}),
(c12:X {name: 'c12'}),
(c21:X {name: 'c21'}),
(c22:X {name: 'c22'}),
(c31:X {name: 'c31'}),
(c32:X {name: 'c32'}),
(c41:X {name: 'c41'}),
(c42:X {name: 'c42'})
CREATE (a)-[:KNOWS]->(b1),
(a)-[:KNOWS]->(b2),
(a)-[:FOLLOWS]->(b3),
(a)-[:FOLLOWS]->(b4)
CREATE (b1)-[:FRIEND]->(c11),
(b1)-[:FRIEND]->(c12),
(b2)-[:FRIEND]->(c21),
(b2)-[:FRIEND]->(c22),
(b3)-[:FRIEND]->(c31),
(b3)-[:FRIEND]->(c32),
(b4)-[:FRIEND]->(c41),
(b4)-[:FRIEND]->(c42)
CREATE (b1)-[:FRIEND]->(b2),
(b2)-[:FRIEND]->(b3),
(b3)-[:FRIEND]->(b4),
(b4)-[:FRIEND]->(b1);

View File

@ -0,0 +1,29 @@
CREATE (a:A {name: 'a'}),
(b1:X {name: 'b1'}),
(b2:X {name: 'b2'}),
(b3:X {name: 'b3'}),
(b4:X {name: 'b4'}),
(c11:X {name: 'c11'}),
(c12:Y {name: 'c12'}),
(c21:X {name: 'c21'}),
(c22:Y {name: 'c22'}),
(c31:X {name: 'c31'}),
(c32:Y {name: 'c32'}),
(c41:X {name: 'c41'}),
(c42:Y {name: 'c42'})
CREATE (a)-[:KNOWS]->(b1),
(a)-[:KNOWS]->(b2),
(a)-[:FOLLOWS]->(b3),
(a)-[:FOLLOWS]->(b4)
CREATE (b1)-[:FRIEND]->(c11),
(b1)-[:FRIEND]->(c12),
(b2)-[:FRIEND]->(c21),
(b2)-[:FRIEND]->(c22),
(b3)-[:FRIEND]->(c31),
(b3)-[:FRIEND]->(c32),
(b4)-[:FRIEND]->(c41),
(b4)-[:FRIEND]->(c42)
CREATE (b1)-[:FRIEND]->(b2),
(b2)-[:FRIEND]->(b3),
(b3)-[:FRIEND]->(b4),
(b4)-[:FRIEND]->(b1);

View File

@ -0,0 +1,64 @@
/*
This graph is based upon YAGO, which is derived from Wikipedia.
The idea is to enlarge it over time.
http://www.mpi-inf.mpg.de/departments/databases-and-information-systems/research/yago-naga/yago/
*/
CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910})
CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908})
CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937})
CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939})
CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952})
CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963})
CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930})
CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954})
CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986})
CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965})
CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873})
CREATE (john:Person {name: 'John Williams', birthyear: 1932})
CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970})
CREATE (newyork:City {name: 'New York'})
CREATE (london:City {name: 'London'})
CREATE (houston:City {name: 'Houston'})
CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'})
CREATE (batmanbegins:Film {title: 'Batman Begins'})
CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'})
CREATE (parent:Film {title: 'The Parent Trap'})
CREATE (camelot:Film {title: 'Camelot'})
CREATE (rachel)-[:HAS_CHILD]->(vanessa),
(rachel)-[:HAS_CHILD]->(corin),
(michael)-[:HAS_CHILD]->(vanessa),
(michael)-[:HAS_CHILD]->(corin),
(corin)-[:HAS_CHILD]->(jemma),
(vanessa)-[:HAS_CHILD]->(natasha),
(roy)-[:HAS_CHILD]->(michael),
(rachel)-[:MARRIED]->(michael),
(michael)-[:MARRIED]->(rachel),
(natasha)-[:MARRIED]->(liam),
(liam)-[:MARRIED]->(natasha),
(vanessa)-[:BORN_IN]->(london),
(natasha)-[:BORN_IN]->(london),
(christopher)-[:BORN_IN]->(london),
(dennis)-[:BORN_IN]->(houston),
(lindsay)-[:BORN_IN]->(newyork),
(john)-[:BORN_IN]->(newyork),
(christopher)-[:DIRECTED]->(batmanbegins),
(john)-[:WROTE_MUSIC_FOR]->(harrypotter),
(john)-[:WROTE_MUSIC_FOR]->(mrchips),
(michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips),
(vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot),
(richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot),
(richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter),
(natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent),
(dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),
(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent),
(liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins)