openCypher_M06 added

Reviewers: buda

Reviewed By: buda

Differential Revision: https://phabricator.memgraph.io/D481
This commit is contained in:
Matej Gradiček 2017-06-16 10:29:41 +00:00
parent d8e06d12c1
commit f1e894d107
55 changed files with 15585 additions and 0 deletions

View File

@ -31,6 +31,9 @@ The following tck tests have been changed:
To correct tests, tag "the result should be" was changed with a
tag "the result should be (ignoring element order for lists)".
3. Behave can't escape character '|' and it throws parse error. Query was then
changed and result was returned with different name.
Comparability.feature tests are failing because integers are compared to strings
what is not allowed in openCypher.

View File

@ -0,0 +1,472 @@
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,67 @@
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,86 @@
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 any 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,521 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: CreateAcceptance
Scenario: Create a single node with multiple labels
Given any 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 any 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 any 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 any 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 | r1 | b | r2 | c |
| (:A) | [:R1] | (:B) | [:R2] | (:C) |

View File

@ -0,0 +1,372 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 |
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 |
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 the side effects should be:
| +nodes | 1 |
| -nodes | 1 |
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 |
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 |
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 |

View File

@ -0,0 +1,111 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,487 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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:
| params | p |
| 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:
| params | p |
| 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,162 @@
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 - wrong 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
Given an empty graph
And having executed:
"""
CREATE (a:End {value: 1})-[:REL {value: 1}]->(b:B)-[:REL {value: 2}]->(c:End {value: 2})
"""
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
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 2016 "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,153 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 |
When executing control query:
"""
MATCH ()-[r:TYPE]->()
RETURN [key IN keys(r) | key + '->' + r[key]] AS keyValue
"""
Then the result should be:
| keyValue |
| ['foo->baz', 'bar->baz'] |

View File

@ -0,0 +1,484 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 (ignoring element order for lists):
| 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 |
| +labels | 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 | 2 |
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 | 3 |
| +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 | 2 |
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 | 15 |
| +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 | 2 |
| +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 |
| +labels | 1 |
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,598 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 | 5 |
| +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 |
| +labels | 2 |
| +properties | 1 |
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 |
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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 (ignoring element order for lists):
| 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 (ignoring element order for lists):
| 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,515 @@
#
# Copyright (c) 2002-2016 "Neo Technology,"
# Network Engine for Objects in Lund AB [http://neotechnology.com]
#
# This file is part of Neo4j.
#
# Neo4j is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
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:
| label | c |
| 'A' | 3 |
| 'B' | 3 |
| 'C' | 3 |
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 empty
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 empty
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 |
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 |
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 |
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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,297 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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
And having executed:
"""
CREATE ({name: 'A'}),
({name: 'B'}),
({name: 'C'}),
({name: 'D'}),
({name: 'E'})
"""
When executing query:
"""
MATCH (n)
RETURN n
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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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:
| p | a | b |
| <(:Start)-[:T]->()> | (:Start) | () |
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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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,286 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 |
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 |
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 | 2 |
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 | 2 |
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 |
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 any 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 | 1 |

View File

@ -0,0 +1,71 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 | 2 |
| +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 | y | z | zs | ys | xs |
| 1 | 3 | 5 | [5, 6] | [3, 4] | [1, 2] |
| 1 | 3 | 6 | [5, 6] | [3, 4] | [1, 2] |
| 1 | 4 | 5 | [5, 6] | [3, 4] | [1, 2] |
| 1 | 4 | 6 | [5, 6] | [3, 4] | [1, 2] |
| 2 | 3 | 5 | [5, 6] | [3, 4] | [1, 2] |
| 2 | 3 | 6 | [5, 6] | [3, 4] | [1, 2] |
| 2 | 4 | 5 | [5, 6] | [3, 4] | [1, 2] |
| 2 | 4 | 6 | [5, 6] | [3, 4] | [1, 2] |
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 | 2 |
| +properties | 4 |

View File

@ -0,0 +1,657 @@
#
# Copyright 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 2017 "Neo Technology",
# Network Engine for Objects in Lund AB (http://neotechnology.com)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
Feature: 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 | r |
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,71 @@
{
"name": "binary-tree-1",
"scripts": [
"binary-tree-1"
],
"nodes": [
{
"label": "",
"key": "",
"count": 13,
"distinct": 1
},
{
"label": "",
"key": "name",
"count": 13,
"distinct": 13
},
{
"label": "A",
"key": "",
"count": 1,
"distinct": 1
},
{
"label": "A",
"key": "name",
"count": 1,
"distinct": 1
},
{
"label": "X",
"key": "",
"count": 12,
"distinct": 1
},
{
"label": "X",
"key": "name",
"count": 12,
"distinct": 12
}
],
"relationships": [
{
"type": "",
"key": "",
"count": 16,
"distinct": 1
},
{
"type": "FOLLOWS",
"key": "",
"count": 2,
"distinct": 1
},
{
"type": "FRIEND",
"key": "",
"count": 12,
"distinct": 1
},
{
"type": "KNOWS",
"key": "",
"count": 2,
"distinct": 1
}
],
"labels": []
}

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,83 @@
{
"name": "binary-tree-2",
"scripts": [
"binary-tree-2"
],
"nodes": [
{
"label": "",
"key": "",
"count": 13,
"distinct": 1
},
{
"label": "",
"key": "name",
"count": 13,
"distinct": 13
},
{
"label": "A",
"key": "",
"count": 1,
"distinct": 1
},
{
"label": "A",
"key": "name",
"count": 1,
"distinct": 1
},
{
"label": "X",
"key": "",
"count": 8,
"distinct": 1
},
{
"label": "X",
"key": "name",
"count": 8,
"distinct": 8
},
{
"label": "Y",
"key": "",
"count": 4,
"distinct": 1
},
{
"label": "Y",
"key": "name",
"count": 4,
"distinct": 4
}
],
"relationships": [
{
"type": "",
"key": "",
"count": 16,
"distinct": 1
},
{
"type": "FOLLOWS",
"key": "",
"count": 2,
"distinct": 1
},
{
"type": "FRIEND",
"key": "",
"count": 12,
"distinct": 1
},
{
"type": "KNOWS",
"key": "",
"count": 2,
"distinct": 1
}
],
"labels": []
}

View File

@ -0,0 +1,74 @@
= Named Graphs
This document describes how to use the named graph descriptions and metadata to properly set up the graphs for use by the TCK.
== Metadata File
Each named graph is described using a JSON file (the metadata file), which references various Cypher script files for creating the graph.
The metadata file also includes statistical information describing the graph composition.
The metadata file follows the below structure:
[source]
----
{
"name": // the graph name
"scripts": [] // a list of file names containing queries that create the graph
"nodes": [ // a list of descriptions of the graph's label/property combinations
{
"label": // label name
"key": // property key
"count": // number of existing combinations
"distinct": // number of distinct combinations
"advice": [] // an optional list of characteristics for the combination
}
],
"relationships": [ // a list of descriptions of the graphs type/property combinations
{
"type": // type name
"key": // property key
"count": // number of existing combinations
"distinct": // number of distinct combinations
"advice": [] // an optional list of characteristics for the combination
}
],
"labels": [ // a list of all labels and their correlations with other labels
{
"label": // the label name
"count": // the number of nodes with the label
"sublabels": [ // a list of sublabels that exist on nodes with the label
{
"label": // the sublabel name
"count": // the number of nodes with the sublabel and the label
"advice": [] // an optional list of characteristics for the label/sublabel combination
}
]
}
]
}
----
The empty string is used as an 'any' wildcard for label names and property keys (i.e. for describing any property key, or any node with or without labels).
=== Advice
For some statistical combinations, the metadata file contains an optional piece of information (that may be disregarded), which can be useful for imposing constraints on the graph.
This is called 'advice'.
For label/property and type/property combinations, the supported advice are:
* `exists`
** This indicates that each entity in the entry's context has the property. Additionally, the advice indicates that no included scenario will ever violate this constraint by performing updates to the graph.
* `unique`
** This indicates that each possible property value is at most assigned to one entity in the entry's context. Additionally, the advice guarantees that no included scenario will ever violate this constraint by performing updates to the graph.
* `index`
** This indicates that some scenarios include queries that try to match on entities in the entry's context via a property comparison (e.g. `MATCH (n:Label {prop: {value}}) ...`).
For label/sublabel combinations, the supported advice is:
* `implies`
** This indicates that the existence of the label on a node implies the existence of the sublabel. In other words, if a node has the label, it will always have the sublabel.
=== Scripts
The metadata file will specify one or more script files that contain Cypher statements which are used to create the named graph.
The statements in the script files are separated by the semicolon character.