Neo4j Cypher Manual 4.2
Neo4j Cypher Manual 4.2
2
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4. Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2. Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3. Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6. Parameters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.7. Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.8. Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.12. Lists. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
2.13. Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
3. Clauses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.1. MATCH . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
4. Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
5. Administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
This is the Cypher manual for Neo4j version 4.2, authored by the Neo4j Team.
• Glossary of keywords — A glossary of Cypher keywords, with links to other parts of the Cypher
manual.
• Cypher styleguide — A guide to the recommended style for writing Cypher queries.
1
Chapter 1. Introduction
This section provides an introduction to the Cypher query language.
Cypher is inspired by a number of different approaches and builds on established practices for expressive
querying. Many of the keywords, such as WHERE and ORDER BY, are inspired by SQL. Pattern matching
borrows expression approaches from SPARQL. Some of the list semantics are borrowed from languages
such as Haskell and Python. Cypher’s constructs, based on English prose and neat iconography, make
queries easy, both to write and to read.
Structure
Cypher borrows its structure from SQL — queries are built up using various clauses.
Clauses are chained together, and they feed intermediate result sets between each other. For example, the
matching variables from one MATCH clause will be the context that the next clause exists in.
The query language is comprised of several distinct clauses. These are discussed in more detail in the
chapter on Clauses.
The following are a few examples of clauses used to read from the graph:
• MATCH: The graph pattern to match. This is the most common way to get data from the graph.
• WHERE: Not a clause in its own right, but rather part of MATCH, OPTIONAL MATCH and WITH. Adds
constraints to a pattern, or filters the intermediate result passing through WITH.
2
Example Graph
N0 [
label = "{Person|name = \'John\'\l}"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
N1 [
label = "{Person|name = \'Joe\'\l}"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
N2 [
label = "{Person|name = \'Steve\'\l}"
]
N3 [
label = "{Person|name = \'Sara\'\l}"
]
N3 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
N4 [
label = "{Person|name = \'Maria\'\l}"
]
For example, here is a query which finds a user called 'John' and 'John’s' friends (though not his direct
friends) before returning both 'John' and any friends-of-friends that are found.
Resulting in:
+----------------------+
| john.name | fof.name |
+----------------------+
| "John" | "Maria" |
| "John" | "Steve" |
+----------------------+
2 rows
We take a list of user names and find all nodes with names from this list, match their friends and return
only those followed users who have a 'name' property starting with 'S'.
MATCH (user)-[:FRIEND]->(follower)
WHERE user.name IN ['Joe', 'John', 'Sara', 'Maria', 'Steve'] AND follower.name =~ 'S.*'
RETURN user.name, follower.name
Resulting in:
3
+---------------------------+
| user.name | follower.name |
+---------------------------+
| "Joe" | "Steve" |
| "John" | "Sara" |
+---------------------------+
2 rows
And these are examples of clauses that are used to update the graph:
• SET (and REMOVE): Set values to properties and add labels on nodes using SET and use REMOVE to remove
them.
• MERGE: Match existing or create new nodes and patterns. This is especially useful together with unique
constraints.
Cypher queries are executed against a Neo4j database, but normally apply to specific graphs. It is
important to understand the meaning of these terms and exactly when a graph is not a database.
DBMS
A Neo4j Database Management System is capable of containing and managing multiple graphs
contained in databases. Client applications will connect to the DBMS and open sessions against it. A
client session provides access to any graph in the DBMS.
Graph
This is a data model within a database. Normally there is only one graph within each database, and
many administrative commands that refer to a specific graph do so using the database name.
Cypher queries executed in a session may declare which graph they apply to, or use a default, given by
the session.
In Neo4j Fabric it is possible to refer to multiple graphs within the same query.
Database
A database is a storage and retrieval mechanism for collecting data in a defined space on disk and in
memory.
Most of the time Cypher queries are reading or updating queries which are run against a graph. There are,
however, administrative commands that apply to a database, or to the entire DBMS. Such commands
cannot be run in a session connected to a normal user database, but instead need to be run within a
session connected to the special system database.
4
1.2.1. The system database and the default database
All Neo4j servers will contain a built-in database called system which behaves differently than all other
databases. This database stores system data and you can not perform graph queries against it.
• system - the system database described above, containing meta-data on the DBMS and security
configuration.
• neo4j - the default database, named using the config option dbms.default_database=neo4j.
However it is worth listing up-front the key areas that are not supported in the open-source edition:
Multi-database Any number of user databases Only system and one user database
Role-based security User, Role and Privilege management Multi-user management. All users have
for flexible access control and sub- full access rights.
graph access control.
enterprise-only This feature only works in the enterprise CREATE DATABASE foo
edition of Neo4j
5
administering graphs and databases.
In the introduction we described the common case of using Cypher to perform read-only queries of the
graph. However, it is also possible to use Cypher to perform updates to the graph, import data into the
graph, and perform administrative actions on graphs, databases and the entire DBMS.
All these various options are described in more detail in later sections, but it is worth summarizing a few
key points first.
A Cypher query part can either read and match on the graph, or make updates on it, not
both simultaneously.
If your query only performs reads, Cypher will not actually match the pattern until you ask for the results. In
an updating query, the semantics are that all the reading will be done before any writing is performed.
The only pattern where the query parts are implicit is when you first read and then write — any other order
and you have to be explicit about your query parts. The parts are separated using the WITH statement. WITH
is like an event horizon — it’s a barrier between a plan and the finished execution of that plan.
When you want to filter using aggregated data, you have to chain together two reading query parts — the
first one does the aggregating, and the second filters on the results coming from the first one.
Using WITH, you specify how you want the aggregation to happen, and that the aggregation has to be
finished before Cypher can start filtering.
Here’s an example of updating the graph, writing the aggregated data to the graph:
6
MATCH (n {name: 'John'})-[:FRIEND]-(friend)
WITH n, count(friend) AS friendsCount
SET n.friendsCount = friendsCount
RETURN n.friendsCount
You can chain together as many query parts as the available memory permits.
After all the parts of the query comes one final RETURN clause. RETURN is not part of any query part — it is a
period symbol at the end of a query. The RETURN clause has three sub-clauses that come with it: SKIP/LIMIT
and ORDER BY.
If you return nodes or relationships from a query that has just deleted them — beware, you are holding a
pointer that is no longer valid.
1.4. Transactions
This section describes how Cypher queries work with database transactions.
All Cypher statements are explicitly run within a transaction. For read-only queries, the transaction will
always succeed. For updating queries it is possible that a failure can occur for some reason, for example if
the query attempts to violate a constraint, in which case the entire transaction is rolled back, and no
changes are made to the graph. Every statement is executed within the context of the transaction, and
nothing will be persisted to disk until that transaction is successfully committed.
In short, an updating query will always either fully succeed, or not succeed at all.
While it is not possible to run a Cypher query outside a transaction, it is possible to run multiple queries
within a single transaction using the following sequence of operations:
1. Open a transaction,
Note that the transaction will hold the changes in memory until the whole query, or whole set of queries,
has finished executing. A query that makes a large number of updates will consequently use large
amounts of memory. For memory configuration in Neo4j, see the Neo4j Operations Manual → Memory
configuration.
For examples of the API’s used to start and commit transactions, refer to the API specific documentation:
• For information on using transactions with a Neo4j driver, see The session API in the Neo4j Driver
manuals.
7
• For information on using transactions over the HTTP API, see the HTTP API documentation → Using
the HTTP API.
• For information on using transactions within the embedded Core API, see the Java Reference →
Executing Cypher queries from Java.
When writing procedures or using Neo4j embedded, remember that all iterators returned from an
execution result should be either fully exhausted or closed. This ensures that the resources bound to them
are properly released.
A database transaction is started when the first query to a specific database is issued. Database
transactions opened inside a DBMS-level transaction are committed or rolled back when the DBMS-level
transaction is committed or rolled back.
For an example of how queries to multiple databases can be issued in one transaction, see Databases and
execution context in the Neo4j Driver manuals.
◦ Operations on graphs.
◦ Schema commands.
◦ Administration commands.
Neo4j Cypher makes use of relationship isomorphism for path matching and is a very effective way of
reducing the result set size and preventing infinite traversals.
In Neo4j, all relationships have a direction. However, you can have the notion of
undirected relationships at query time.
In the case of variable length pattern expressions, it is particularly important to have a constraint check, or
an infinite number of result records could be found.
8
To understand this better, let us consider a few alternative options:
Homomorphism
No constraints for path matching.
Node isomorphism
The same node cannot be returned more than once for each path matching record.
Relationship isomorphism
The same relationship cannot be returned more than once for each path matching record. Cypher
makes use of relationship isomorphism for path matching.
1.5.1. Homomorphism
Constraints: No constraints for path matching.
Example 1. Homomorphism
The graph is composed of only two nodes (a) and (b), connected by one relationship, (a:Node)-
[r:R]->(b:Node).
If the query is looking for paths of length n and do not care about the direction, a path of length n will
be returned repeating the two nodes over and over.
For example, find all paths with 5 relationships and do not care about the relationship direction:
MATCH p = ()-[*5]-()
RETURN nodes(p)
This will return the two resulting records if homomorphism was used, [a,b,a,b,a,b], as well as
[b,a,b,a,b,a].
In another two-node example, such as (a:Node)-[r:R]->(b:Node); only paths of length 1 can be found
with the node isomorphism constraint.
9
Example 2. Node isomorphism
The graph is composed of only two nodes (a) and (b), connected by one relationship, (a:Node)-
[r:R]->(b:Node).
MATCH p = ()-[*1]-()
RETURN nodes(p)
This will return the two resulting records if node isomorphism was used, [a, b], as well as [b, a].
In another two-node example, such as (a:Node)-[r:R]->(b:Node); only paths of length 1 can be found
with the relationship isomorphism constraint.
The graph is composed of only two nodes (a) and (b), connected by one relationship, (a:Node)-
[r:R]->(b:Node).
MATCH p = ()-[*1]-()
RETURN nodes(p)
This will return the two resulting records [a, b], as well as [b, a].
10
Example 4. Friend of friends
Looking for a user’s friends of friends should not return said user.
CREATE
(adam:User {name: 'Adam'}),
(pernilla:User {name: 'Pernilla'}),
(david:User {name: 'David'}),
(adam)-[:FRIEND]->(pernilla),
(pernilla)-[:FRIEND]->(david)
Nodes created: 3
Relationships created: 2
Properties set: 3
N0 [
label = "{User|name = \'Adam\'\l}"
]
N1 [
label = "{User|name = \'Pernilla\'\l}"
]
N2 [
label = "{User|name = \'David\'\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FRIEND\n"
]
+---------+
| fofName |
+---------+
| "David" |
+---------+
Rows: 1
In this query, Cypher makes sure to not return matches where the pattern relationships r1 and r2
point to the same graph relationship.
11
This is however not always desired. If the query should return the user, it is possible to spread the
matching over multiple MATCH clauses, like so:
+---------+
| fofName |
+---------+
| "David" |
| "Adam" |
+---------+
Rows: 2
Note that while the following Query 4 looks similar to Query 3, it is actually equivalent to Query 2.
MATCH
(user:User {name: 'Adam'})-[r1:FRIEND]-(friend),
(friend)-[r2:FRIEND]-(friend_of_a_friend)
RETURN friend_of_a_friend.name AS fofName
Here, the MATCH clause has a single pattern with two paths, while the previous query has two distinct
patterns.
+---------+
| fofName |
+---------+
| "David" |
+---------+
Rows: 1
12
Chapter 2. Syntax
This section describes the syntax of the Cypher query language.
• Expressions
◦ Expressions in general
◦ CASE Expressions
• Variables
• Reserved keywords
• Parameters
◦ String literal
◦ Regular expression
◦ Node id
◦ Calling procedures
• Operators
◦ Operators at a glance
◦ Aggregation operators
◦ Property operators
◦ Mathematical operators
◦ Comparison operators
◦ Boolean operators
◦ String operators
◦ Temporal operators
◦ Map operators
◦ List operators
• Comments
13
• Patterns
◦ Patterns for nodes
◦ Specifying properties
◦ Time zones
◦ Temporal instants
▪ Specifying dates
▪ Specifying times
▪ Examples
◦ Durations
▪ Specifying durations
▪ Examples
◦ Examples
◦ Temporal indexing
• Spatial values
◦ Introduction
◦ Spatial instants
▪ Creating points
◦ Spatial index
• Lists
◦ Lists in general
14
◦ List comprehension
◦ Pattern comprehension
• Maps
◦ Literal maps
◦ Map projection
These fall into several categories which will be described in detail in the following subsections:
Property types
Integer, Float, String, Boolean, Point, Date, Time, LocalTime, DateTime, LocalDateTime, and Duration.
Structural types
Node, Relationship, and Path.
Composite types
List and Map.
• Number, an abstract type, which has the subtypes Integer and Float
• String
• Boolean
15
• The spatial type Point
The adjective numeric, when used in the context of describing Cypher functions or expressions, indicates
that any type of Number applies (Integer or Float).
Homogeneous lists of simple types can also be stored as properties, although lists in general (see
Composite types) cannot be stored.
Cypher also provides pass-through support for byte arrays, which can be stored as property values. Byte
arrays are not considered a first class data type by Cypher, so do not have a literal representation.
The most significant characters not in BMP are those belonging to the Supplementary
Multilingual Plane or the Supplementary Ideographic Plane. Examples are:
• Historic scripts and symbols and notation used within certain fields such as:
Egyptian hieroglyphs, modern musical notation, mathematical alphanumerics.
• CJK Ideograph that were not included in earlier character encoding standards.
• Node
◦ Id
◦ Label(s)
• Relationship
16
◦ Id
◦ Type
Nodes, relationships, and paths are returned as a result of pattern matching. In Neo4j, all
relationships have a direction. However, you can have the notion of undirected
relationships at query time.
• List, a heterogeneous, ordered collection of values, each of which has any property, structural or
composite type.
Special care must be taken when using null (see Working with null).
17
◦ This includes "non-English" characters, such as å, ä, ö, ü etc.
• Numbers:
◦ Names should not begin with a number.
• Symbols:
◦ Names should not contain symbols, except for underscore, as in my_variable, or $ as the first
character to denote a parameter, as given by $myParam.
• Length:
◦ Can be very long, up to 65535 (2^16 - 1) or 65534 characters, depending on the version of Neo4j.
• Case-sensitive:
◦ Names are case-sensitive and thus, :PERSON, :Person and :person are three different labels, and n
and N are two different variables.
• Whitespace characters:
◦ Leading and trailing whitespace characters will be removed automatically. For example, MATCH ( a
) RETURN a is equivalent to MATCH (a) RETURN a.
`$$n`, and `my variable has spaces`. Database names are an exception and may
include dots without the need for escaping. For example: naming a database
foo.bar.baz is perfectly valid.
• Variables for nodes and relationships must not re-use names within the same query scope.
◦ The following query is not valid as the node and relationship both have the name a: CREATE (a)-
[a]->(b).
2.2.3. Recommendations
Here are the recommended naming conventions:
18
2.3. Expressions
This section contains an overview of expressions in Cypher with examples.
• Expressions in general
• CASE expressions
◦ Simple CASE form: comparing an expression against multiple values
◦ Distinguishing between when to use the simple and generic CASE forms
• A list of expressions: ['a', 'b'], [1, 2, 3], ['a', 2, n.property, $param], [].
• A predicate expression is an expression that returns true or false: a.prop = 'Hello', length(p) > 10,
exists(a.name).
• An existential subquery is an expression that returns true or false: EXISTS { MATCH (n)-[r]→(p) WHERE
p.name = 'Sven' }.
• A case-sensitive string matching expression: a.surname STARTS WITH 'Sven', a.surname ENDS WITH
19
'son' or a.surname CONTAINS 'son'.
• A CASE expression.
\t Tab
\b Backspace
\n Newline
\r Carriage return
\f Form feed
\\ Backslash
20
Graph
N0 [
label = "{A|name = \'Alice\'\leyes = \'brown\'\lage = 38\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "{B|name = \'Bob\'\leyes = \'blue\'\lage = 25\l}"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "MARRIED\n"
]
N2 [
label = "{C|name = \'Charlie\'\leyes = \'green\'\lage = 53\l}"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "{D|name = \'Daniel\'\leyes = \'brown\'\l}"
]
N4 [
label = "{E|array = \[\'one\', \'two\', \'three\'\]\lname = \'Eskil\'\leyes = \'blue\'\lage = 41\l}"
]
Syntax:
CASE test
WHEN value THEN result
[WHEN ...]
[ELSE default]
END
Arguments:
Name Description
21
Name Description
Query
MATCH (n)
RETURN
CASE n.eyes
WHEN 'blue' THEN 1
WHEN 'brown' THEN 2
ELSE 3
END AS result
Table 1. Result
result
Rows: 5
Syntax:
CASE
WHEN predicate THEN result
[WHEN ...]
[ELSE default]
END
Arguments:
Name Description
22
Query
MATCH (n)
RETURN
CASE
WHEN n.eyes = 'blue' THEN 1
WHEN n.age < 40 THEN 2
ELSE 3
END AS result
Table 2. Result
result
Rows: 5
Distinguishing between when to use the simple and generic CASE forms
Owing to the close similarity between the syntax of the two forms, sometimes it may not be clear at the
outset as to which form to use. We illustrate this scenario by means of the following query, in which there
is an expectation that age_10_years_ago is -1 if n.age is null:
Query
MATCH (n)
RETURN n.name,
CASE n.age
WHEN n.age IS NULL THEN -1
ELSE n.age - 10
END AS age_10_years_ago
However, as this query is written using the simple CASE form, instead of age_10_years_ago being -1 for the
node named Daniel, it is null. This is because a comparison is made between n.age and n.age IS NULL.
As n.age IS NULL is a boolean value, and n.age is an integer value, the WHEN n.age IS NULL THEN -1
branch is never taken. This results in the ELSE n.age - 10 branch being taken instead, returning null.
Table 3. Result
n.name age_10_years_ago
"Alice" 28
"Bob" 15
"Charlie" 43
"Daniel" <null>
"Eskil" 31
Rows: 5
23
The corrected query, behaving as expected, is given by the following generic CASE form:
Query
MATCH (n)
RETURN n.name,
CASE
WHEN n.age IS NULL THEN -1
ELSE n.age - 10
END AS age_10_years_ago
We now see that the age_10_years_ago correctly returns -1 for the node named Daniel.
Table 4. Result
n.name age_10_years_ago
"Alice" 28
"Bob" 15
"Charlie" 43
"Daniel" -1
"Eskil" 31
Rows: 5
2.4. Variables
This section provides an overview of variables in Cypher.
When you reference parts of a pattern or a query, you do so by naming them. The names you give the
different parts are called variables.
In this example:
MATCH (n)-->(b)
RETURN b
24
Reserved keywords are words that have a special meaning in Cypher. The listing of the reserved keywords
are grouped by the categories from which they are drawn. In addition to this, there are a number of
keywords that are reserved for future use.
The reserved keywords are not permitted to be used as identifiers in the following contexts:
• Variables
• Function names
• Parameters
If any reserved keyword is escaped — i.e. is encapsulated by backticks `, such as `AND` — it would become
a valid identifier in the above contexts.
2.5.1. Clauses
• CALL
• CREATE
• DELETE
• DETACH
• EXISTS
• FOREACH
• LOAD
• MATCH
• MERGE
• OPTIONAL
• REMOVE
• RETURN
• SET
• START
• UNION
• UNWIND
• WITH
2.5.2. Subclauses
• LIMIT
• ORDER
• SKIP
• WHERE
25
• YIELD
2.5.3. Modifiers
• ASC
• ASCENDING
• ASSERT
• BY
• CSV
• DESC
• DESCENDING
• ON
2.5.4. Expressions
• ALL
• CASE
• ELSE
• END
• THEN
• WHEN
2.5.5. Operators
• AND
• AS
• CONTAINS
• DISTINCT
• ENDS
• IN
• IS
• NOT
• OR
• STARTS
• XOR
26
2.5.6. Schema
• CONSTRAINT
• CREATE
• DROP
• EXISTS
• INDEX
• NODE
• KEY
• UNIQUE
2.5.7. Hints
• INDEX
• JOIN
• PERIODIC
• COMMIT
• SCAN
• USING
2.5.8. Literals
• false
• null
• true
• DO
• FOR
• MANDATORY
• OF
• REQUIRE
• SCALAR
2.6. Parameters
This section describes parameterized quering.
27
2.6.1. Introduction
Cypher supports querying with parameters. A parameterized query is a query in which placeholders are
used for parameters and the parameter values are supplied at execution time. This means developers do
not have to resort to string building to create a query. Additionally, parameters make caching of execution
plans much easier for Cypher, thus leading to faster query execution times.
Parameters cannot be used for the following constructs, as these form part of the query structure that is
compiled into a query plan:
• relationship types
• labels
Parameters may consist of letters and numbers, and any combination of these, but cannot start with a
number or a currency symbol.
Setting parameters when running a query is dependent on the client environment. For example:
• To set a parameter in Cypher Shell use :param name => 'Joe'. For more information refer to
Operations Manual → Cypher Shell - Query Parameters.
• For Neo4j Browser use the same syntax as Cypher Shell, :param name => 'Joe'.
• When using drivers, the syntax is dependent on the language choice. See the examples in
Transactions in the Neo4j Driver manuals.
• For usage via the Neo4j HTTP API, see the HTTP API documentation.
We provide below a comprehensive list of examples of parameter usage. In these examples, parameters
are given in JSON; the exact manner in which they are to be submitted depends upon the driver being
used.
The old parameter syntax {param} was deprecated in Neo4j 3.0 and removed entirely in
Neo4j 4.0. Using it will result in a syntax error. However, it is still possible to use it, with
warnings, if you prefix the query with CYPHER 3.5. See Cypher Compatibility for further
information.
28
Parameters
{
"name" : "Johan"
}
Query
MATCH (n:Person)
WHERE n.name = $name
RETURN n
Parameters
{
"name" : "Johan"
}
Query
{
"regex" : ".*h.*"
}
Query
MATCH (n:Person)
WHERE n.name =~ $regex
RETURN n.name
{
"name" : "Michael"
}
Query
MATCH (n:Person)
WHERE n.name STARTS WITH $name
RETURN n.name
29
2.6.5. Create node with properties
Parameters
{
"props" : {
"name" : "Andy",
"position" : "Developer"
}
}
Query
CREATE ($props)
{
"props" : [ {
"awesome" : true,
"name" : "Andy",
"position" : "Developer"
}, {
"children" : 3,
"name" : "Michael",
"position" : "Developer"
} ]
}
Query
Parameters
{
"props" : {
"name" : "Andy",
"position" : "Developer"
}
}
Query
MATCH (n:Person)
WHERE n.name = 'Michaela'
SET n = $props
30
2.6.8. SKIP and LIMIT
Parameters
{
"s" : 1,
"l" : 1
}
Query
MATCH (n:Person)
RETURN n.name
SKIP $s
LIMIT $l
2.6.9. Node id
Parameters
{
"id" : 0
}
Query
MATCH (n)
WHERE id(n) = $id
RETURN n.name
{
"ids" : [ 0, 1, 2 ]
}
Query
MATCH (n)
WHERE id(n) IN $ids
RETURN n.name
{
"indexname" : "My index"
}
31
Query
CALL db.resampleIndex($indexname)
2.7. Operators
• Operators at a glance
• Aggregation operators
◦ Using the DISTINCT operator
• Property operators
◦ Statically accessing a property of a node or relationship using the . operator
• Mathematical operators
◦ Using the exponentiation operator ^
• Comparison operators
◦ Comparing two numbers
• Boolean operators
◦ Using boolean operators to filter numbers
• String operators
◦ Using a regular expression with =~ to filter words
• Temporal operators
◦ Adding and subtracting a Duration to or from a temporal instant
• Map operators
◦ Statically accessing the value of a nested map by key using the . operator"
◦ Dynamically accessing the value of a map by key using the [] operator and a parameter
• List operators
◦ Concatenating two lists using +
32
◦ Using IN to check if a number is in a list
Property operators . for static property access, [] for dynamic property access, =
for replacing all properties, += for mutating specific properties
Mathematical operators +, -, *, /, %, ^
Comparison operators =, <>, <, >, <=, >=, IS NULL, IS NOT NULL
Map operators . for static value access by key, [] for dynamic value access
by key
Query
CREATE
(a:Person {name: 'Anne', eyeColor: 'blue'}),
(b:Person {name: 'Bill', eyeColor: 'brown'}),
(c:Person {name: 'Carol', eyeColor: 'blue'})
WITH [a, b, c] AS ps
UNWIND ps AS p
RETURN DISTINCT p.eyeColor
Even though both 'Anne' and 'Carol' have blue eyes, 'blue' is only returned once.
33
Table 5. Result
p.eyeColor
"blue"
"brown"
Rows: 2
Nodes created: 3
Properties set: 6
Labels added: 3
• statically access the property of a node or relationship using the dot operator: .
• dynamically access the property of a node or relationship using the subscript operator: []
CREATE
(a:Person {name: 'Jane', livesIn: 'London'}),
(b:Person {name: 'Tom', livesIn: 'Copenhagen'})
WITH a, b
MATCH (p:Person)
RETURN p.name
Table 6. Result
p.name
"Jane"
"Tom"
Rows: 2
Nodes created: 2
Properties set: 4
Labels added: 2
34
Query
CREATE
(a:Restaurant {name: 'Hungry Jo', rating_hygiene: 10, rating_food: 7}),
(b:Restaurant {name: 'Buttercup Tea Rooms', rating_hygiene: 5, rating_food: 6}),
(c1:Category {name: 'hygiene'}),
(c2:Category {name: 'food'})
WITH a, b, c1, c2
MATCH (restaurant:Restaurant), (category:Category)
WHERE restaurant["rating_" + category.name] > 6
RETURN DISTINCT restaurant.name
Table 7. Result
restaurant.name
"Hungry Jo"
Rows: 1
Nodes created: 4
Properties set: 8
Labels added: 4
All the existing properties on the node are replaced by those provided in the map; i.e. the name property is
updated from Jane to Ellen, the age property is deleted, and the livesIn property is added.
Table 8. Result
Rows: 1
Nodes created: 1
Properties set: 5
Labels added: 1
See Replace all properties using a map and = for more details on using the property replacement operator
=.
35
Mutating specific properties of a node or relationship using the += operator
Query
The properties on the node are updated as follows by those provided in the map: the name property is
updated from Jane to Ellen, the age property is left untouched, and the livesIn property is added.
Table 9. Result
"Ellen" 20 "London"
Rows: 1
Nodes created: 1
Properties set: 4
Labels added: 1
See Mutate specific properties using a map and += for more details on using the property mutation
operator +=.
• addition: +
• multiplication: *
• division: /
• modulo division: %
• exponentiation: ^
result
8.0
Rows: 1
36
Using the unary minus operator -
Query
WITH -3 AS a, 4 AS b
RETURN b - a AS result
result
Rows: 1
• equality: =
• inequality: <>
• IS NULL
• IS NOT NULL
result
true
Rows: 1
37
See Equality and comparison of values for more details on the behavior of comparison operators, and
Using ranges for more examples showing how these may be used.
candidate
"John"
"Jonathan"
Rows: 2
String matching contains more information regarding the string-specific comparison operators as well as
additional examples illustrating the usage thereof.
Equality
Cypher supports comparing values (see Values and types) by equality using the = and <> operators.
Values of the same type are only equal if they are the same identical value (e.g. 3 = 3 and "x" <> "xy").
Maps are only equal if they map exactly the same keys to equal values and lists are only equal if they
contain the same sequence of equal values (e.g. [3, 4] = [1+2, 8/2]).
Values of different types are considered as equal according to the following rules:
• Paths are treated as lists of alternating nodes and relationships and are equal to all lists that contain
that very same sequence of nodes and relationships.
• Testing any value against null with both the = and the <> operators always is null. This includes null
= null and null <> null. The only way to reliably test if a value v is null is by using the special v IS
NULL, or v IS NOT NULL equality operators.
All other combinations of types of values cannot be compared with each other. Especially, nodes,
relationships, and literal maps are incomparable with each other.
38
ordering. The following points give some details on how the comparison is performed.
• Numerical values are compared for ordering using numerical order (e.g. 3 < 4 is true).
• The special value java.lang.Double.NaN is regarded as being larger than all other numbers.
• String values are compared for ordering using lexicographic order (e.g. "x" < "xy").
• Boolean values are compared for ordering such that false < true.
◦ If none if the above is true, the points are considered incomparable and any comparison operator
between them will return null.
◦ Points of different CRS are ordered by the CRS code (the value of SRID field). For the currently
supported set of Coordinate Reference Systems this means the order: 4326, 4979, 7302, 9157
◦ Points of the same CRS are ordered by each coordinate value in turn, x first, then y and finally z.
◦ Note that this order is different to the order returned by the spatial index, which will be the order of
the space filling curve.
39
◦ ORDER BY requires all values to be orderable.
◦ Temporal instances are ordered after spatial instances and before strings.
◦ Comparable values should be ordered in the same order as implied by their comparison order.
◦ Temporal instant values are first ordered by type, and then by comparison order within the type.
◦ Since no complete comparison order can be defined for Duration values, we define an order for
ORDER BY specifically for Duration:
▪ Duration values are ordered by normalising all components as if all years were 365.2425 days
long (PT8765H49M12S), all months were 30.436875 (1/12 year) days long (PT730H29M06S), and all
[1]
days were 24 hours long .
• Comparing for ordering when one argument is null (e.g. null < 3 is null).
Formally, if a, b, c, ..., y, z are expressions and op1, op2, ..., opN are comparison operators, then a
op1 b op2 c ... y opN z is equivalent to a op1 b and b op2 c and ... y opN z.
Note that a op1 b op2 c does not imply any kind of comparison between a and c, so that, e.g., x < y > z
is perfectly legal (although perhaps not elegant).
The example:
is equivalent to
Thus it will match all nodes where the age is between 21 and 30.
This syntax extends to all equality and inequality comparisons, as well as extending to chains longer than
three.
For example:
Is equivalent to:
40
• conjunction: AND
• disjunction: OR,
• negation: NOT
Here is the truth table for AND, OR, XOR and NOT.
number
Rows: 3
• concatenating strings: +
41
Using a regular expression with =~ to filter words
Query
word
"mouse"
"house"
Rows: 2
Further information and examples regarding the use of regular expressions in filtering can be found in
Regular expressions. In addition, refer to String-specific comparison operators comprise: for details on
string-specific comparison operators.
The following table shows — for each combination of operation and operand type — the type of the value
returned from the application of each temporal operator:
42
Adding and subtracting a Duration to or from a temporal instant
Query
WITH
localdatetime({year:1984, month:10, day:11, hour:12, minute:31, second:14}) AS aDateTime,
duration({years: 12, nanoseconds: 2}) AS aDuration
RETURN aDateTime + aDuration, aDateTime - aDuration
1996-10-11T12:31:14.000000002 1972-10-11T12:31:13.999999998
Rows: 1
Components of a Duration that do not apply to the temporal instant are ignored. For example, when
adding a Duration to a Date, the hours, minutes, seconds and nanoseconds of the Duration are ignored
(Time behaves in an analogous manner):
Query
WITH
date({year:1984, month:10, day:11}) AS aDate,
duration({years: 12, nanoseconds: 2}) AS aDuration
RETURN aDate + aDuration, aDate - aDuration
1996-10-11 1972-10-11
Rows: 1
Adding two durations to a temporal instant is not an associative operation. This is because non-existing
dates are truncated to the nearest existing date:
Query
RETURN
(date("2011-01-31") + duration("P1M")) + duration("P12M") AS date1,
date("2011-01-31") + (duration("P1M") + duration("P12M")) AS date2
date1 date2
2012-02-28 2012-02-29
Rows: 1
43
Query
WITH
duration({years: 12, months: 5, days: 14, hours: 16, minutes: 12, seconds: 70, nanoseconds: 1}) as
duration1,
duration({months:1, days: -14, hours: 16, minutes: -12, seconds: 70}) AS duration2
RETURN duration1, duration2, duration1 + duration2, duration1 - duration2
Rows: 1
Query
WITH duration({days: 14, minutes: 12, seconds: 70, nanoseconds: 1}) AS aDuration
RETURN aDuration, aDuration * 2, aDuration / 3
Rows: 1
• statically access the value of a map by key using the dot operator: .
• dynamically access the value of a map by key using the subscript operator: []
The behavior of the [] operator with respect to null is detailed in The [] operator and
null.
Statically accessing the value of a nested map by key using the . operator
Query
44
p.person.name
"Anne"
Rows: 1
Dynamically accessing the value of a map by key using the [] operator and a
parameter
A parameter may be used to specify the key of the value to access:
Parameters
{
"myKey" : "name"
}
Query
result
"Anne"
Rows: 1
The behavior of the IN and [] operators with respect to null is detailed here.
45
myList
[1,2,3,4,5,6,7]
Rows: 1
number
Rows: 2
The following query checks whether or not the list [2, 1] is an element of the list [1, [2, 1], 3]:
Query
The query evaluates to true as the right-hand list contains, as an element, the list [1, 2] which is of the
same type (a list) and contains the same contents (the numbers 2 and 1 in the given order) as the left-hand
operand. If the left-hand operator had been [1, 2] instead of [2, 1], the query would have returned
false.
inList
true
Rows: 1
At first glance, the contents of the left-hand operand and the right-hand operand appear to be the same in
the following query:
46
Query
However, IN evaluates to false as the right-hand operand does not contain an element that is of the same
type — i.e. a list — as the left-hand-operand.
inList
false
Rows: 1
The following query can be used to ascertain whether or not a list — obtained from, say, the labels()
function — contains at least one element that is also present in another list:
MATCH (n)
WHERE size([label IN labels(n) WHERE label IN ['Person', 'Employee'] | 1]) > 0
RETURN count(n)
As long as labels(n) returns either Person or Employee (or both), the query will return a value greater than
zero.
The square brackets will extract the elements from the start index 1, and up to (but excluding) the end
index 3.
result
["John","Bill"]
Rows: 1
Parameters
{
"myIndex" : 1
}
47
Query
result
"John"
Rows: 1
Parameters
{
"myIndex" : 1
}
Query
result
true
Rows: 1
2.8. Comments
This section describes how how to use comments in Cypher.
A comment begin with double slash (//) and continue to the end of the line. Comments do not execute,
they are for humans to read.
Examples:
MATCH (n)
//This is a whole line comment
RETURN n
48
MATCH (n) WHERE n.property = '//This is NOT a comment' RETURN n
2.9. Patterns
• Introduction
• Specifying properties
2.9.1. Introduction
Patterns and pattern-matching are at the very heart of Cypher, so being effective with Cypher requires a
good understanding of patterns.
Using patterns, you describe the shape of the data you’re looking for. For example, in the MATCH clause you
describe the shape with a pattern, and Cypher will figure out how to get that data for you.
The pattern describes the data using a form that is very similar to how one typically draws the shape of
property graph data on a whiteboard: usually as circles (representing nodes) and arrows between them to
represent relationships.
Patterns appear in multiple places in Cypher: in MATCH, CREATE and MERGE clauses, and in pattern
expressions. Each of these is described in more detail in:
• MATCH
• OPTIONAL MATCH
• CREATE
• MERGE
(a)
This simple pattern describes a single node, and names that node using the variable a.
49
2.9.3. Patterns for related nodes
A more powerful construct is a pattern that describes multiple nodes and relationships between them.
Cypher patterns describe relationships by employing an arrow between two nodes. For example:
(a)-->(b)
This pattern describes a very simple data shape: two nodes, and a single relationship from one to the
other. In this example, the two nodes are both named as a and b respectively, and the relationship is
'directed': it goes from a to b.
This manner of describing nodes and relationships can be extended to cover an arbitrary number of nodes
and the relationships between them, for example:
(a)-->(b)<--(c)
Note that the naming of the nodes in these patterns is only necessary should one need to refer to the same
node again, either later in the pattern or elsewhere in the Cypher query. If this is not necessary, then the
name may be omitted, as follows:
(a)-->()<--(c)
(a:User)-->(b)
(a:User:Admin)-->(b)
Properties can be expressed in patterns using a map-construct: curly brackets surrounding a number of
key-expression pairs, separated by commas. E.g. a node with two properties on it would look like:
50
(a)-[{blocked: false}]->(b)
When properties appear in patterns, they add an additional constraint to the shape of the data. In the case
of a CREATE clause, the properties will be set in the newly-created nodes and relationships. In the case of a
MERGE clause, the properties will be used as additional constraints on the shape any existing data must
have (the specified properties must exactly match any existing data in the graph). If no matching data is
found, then MERGE behaves like CREATE and the properties will be set in the newly created nodes and
relationships.
Note that patterns supplied to CREATE may use a single parameter to specify properties, e.g: CREATE (node
$paramName). This is not possible with patterns used in other clauses, as Cypher needs to know the
property names at the time the query is compiled, so that matching can be done effectively.
(a)--(b)
As with nodes, relationships may also be given names. In this case, a pair of square brackets is used to
break up the arrow and the variable is placed between. For example:
(a)-[r]->(b)
Much like labels on nodes, relationships can have types. To describe a relationship with a specific type, you
can specify this as follows:
(a)-[r:REL_TYPE]->(b)
Unlike labels, relationships can only have one type. But if we’d like to describe some data such that the
relationship could have any one of a set of types, then they can all be listed in the pattern, separating them
with the pipe symbol | like this:
(a)-[r:TYPE1|TYPE2]->(b)
Note that this form of pattern can only be used to describe existing data (ie. when using a pattern with
MATCH or as an expression). It will not work with CREATE or MERGE, since it’s not possible to create a
relationship with multiple types.
As with nodes, the name of the relationship can always be omitted, as exemplified by:
(a)-[:REL_TYPE]->(b)
51
2.9.7. Variable-length pattern matching
Variable length pattern matching in versions 2.1.x and earlier does not enforce
relationship uniqueness for patterns described within a single MATCH clause. This means
that a query such as the following: MATCH (a)-[r]->(b), p = (a)-[]->(c) RETURN *,
relationships(p) AS rs may include r as part of the rs set. This behavior has
changed in versions 2.2.0 and later, in such a way that r will be excluded from
the result set, as this better adheres to the rules of relationship uniqueness
as documented here Cypher path matching. If you have a query pattern that needs
to retrace relationships rather than ignoring them as the relationship
uniqueness rules normally dictate, you can accomplish this using multiple match
clauses, as follows: MATCH (a)-[r]->(b) MATCH p = (a)-[]->(c) RETURN *,
relationships(p). This will work in all versions of Neo4j that support the MATCH clause,
namely 2.0.0 and later.
Rather than describing a long path using a sequence of many node and relationship descriptions in a
pattern, many relationships (and the intermediate nodes) can be described by specifying a length in the
relationship description of a pattern. For example:
(a)-[*2]->(b)
This describes a graph of three nodes and two relationship, all in one path (a path of length 2). This is
equivalent to:
(a)-->()-->(b)
A range of lengths can also be specified: such relationship patterns are called 'variable length
relationships'. For example:
(a)-[*3..5]->(b)
This is a minimum length of 3, and a maximum of 5. It describes a graph of either 4 nodes and 3
relationships, 5 nodes and 4 relationships or 6 nodes and 5 relationships, all connected together in a single
path.
Either bound can be omitted. For example, to describe paths of length 3 or more, use:
(a)-[*3..]->(b)
(a)-[*..5]->(b)
(a)-[*]->(b)
52
As a simple example, let’s take the graph and query below:
Graph
N0 [
label = "name = \'Anders\'\l"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'Becky\'\l"
]
N1 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N2 [
label = "name = \'Cesar\'\l"
]
N2 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "name = \'Dilshad\'\l"
]
N3 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N4 [
label = "name = \'George\'\l"
]
N5 [
label = "name = \'Filipa\'\l"
]
Query
MATCH (me)-[:KNOWS*1..2]-(remote_friend)
WHERE me.name = 'Filipa'
RETURN remote_friend.name
remote_friend.name
"Dilshad"
"Anders"
Rows: 2
This query finds data in the graph with a shape that fits the pattern: specifically a node (with the name
53
property 'Filipa') and then the KNOWS related nodes, one or two hops away. This is a typical example of
finding first and second degree friends.
Note that variable length relationships cannot be used with CREATE and MERGE.
p = (a)-[*3..5]->(b)
You can do this in MATCH, CREATE and MERGE, but not when using patterns as expressions.
• Introduction
• Time zones
• Temporal instants
◦ Specifying temporal instants
▪ Specifying dates
▪ Specifying times
▪ Examples
• Durations
◦ Specifying durations
▪ Examples
• Examples
• Temporal indexing
54
Refer to Temporal functions - instant types for information regarding temporal functions
allowing for the creation and manipulation of temporal values.
Refer to Ordering and comparison of values for information regarding the comparison
and ordering of temporal values.
2.10.1. Introduction
The following table depicts the temporal value types and supported components:
Date X
Time X X
LocalTime X
DateTime X X X
LocalDateTime X X
Duration - - -
Date, Time, LocalTime, DateTime and LocalDateTime are temporal instant types. A temporal instant value
expresses a point in time with varying degrees of precision.
By contrast, Duration is not a temporal instant type. A Duration represents a temporal amount, capturing
the difference in time between two instants, and can be negative. Duration only captures the amount of
time between two instants, and thus does not encapsulate a start time and end time.
When creating a time using a named time zone, the offset from UTC is computed from the rules in the time
zone database to create a time instant in UTC, and to ensure the named time zone is a valid one.
It is possible for time zone rules to change in the IANA time zone database. For example, there could be
alterations to the rules for daylight savings time in a certain area. If this occurs after the creation of a
temporal instant, the presented time could differ from the originally-entered time, insofar as the local
timezone is concerned. However, the absolute time in UTC would remain the same.
• Specifying the offset from UTC in hours and minutes (ISO 8601)
55
• Specifying a named time zone
• Specifying both the offset and the time zone name (with the requirement that these match)
The named time zone form uses the rules of the IANA time zone database to manage daylight savings
time (DST).
The default time zone of the database can be configured using the configuration option
db.temporal.timezone. This configuration option influences the creation of temporal types for the
following functions:
• Getting the current date and time without specifying a time zone.
• Creating a temporal type from its components without specifying a time zone.
• Creating a temporal type by combining or selecting values that do not have a time zone component,
and without specifying a time zone.
• Truncating a temporal value that does not have a time zone component, and without specifying a time
zone.
Date <date>
DateTime* <date>T<time><timezone>
LocalDateTime* <date>T<time>
*When date and time are combined, date must be complete; i.e. fully identify a particular day.
Specifying dates
56
Component Format Description
If the year is before 0000 or after 9999, the following additional rules apply:
• The year must be separated from the next component with the following characters:
◦ - if the next component is month or day of the year
If the year component is prefixed with either - or +, and is separated from the next component, Year is
allowed to contain up to nine digits. Thus, the allowed range of years is between -999,999,999 and
+999,999,999. For all other cases, i.e. the year is between 0000 and 9999 (inclusive), Year must have
exactly four digits (the year component is interpreted as a year of the Common Era (CE)).
57
Format Description Example Interpretation of
example
The least significant components can be omitted. Cypher will assume omitted components to have their
lowest possible value. For example, 2013-06 will be interpreted as being the same date as 2013-06-01.
Specifying times
Cypher does not support leap seconds; UTC-SLS (UTC with Smoothed Leap Seconds) is used to manage
the difference in time between UTC and TAI (International Atomic Time).
58
Format Description Example Interpretation of
example
HH Hour 21 21:00:00.000
The least significant components can be omitted. For example, a time may be specified with Hour and
Minute, leaving out Second and fraction. On the other hand, specifying a time with Hour and Second, while
leaving out Minute, is not possible.
When specifying a time zone as an offset from UTC, the rules below apply:
• The time zone always starts with either a plus (+) or minus (-) sign.
◦ Positive offsets, i.e. time zones beginning with +, denote time zones east of UTC.
◦ Negative offsets, i.e. time zones beginning with -, denote time zones west of UTC.
• An optional double-digit minute offset follows the hour offset, optionally separated by a colon (:).
• The time zone of the International Date Line is denoted either by +12:00 or -12:00, depending on
country.
When creating values of the DateTime temporal instant type, the time zone may also be specified using a
named time zone, using the names from the IANA time zone database. This may be provided either in
addition to, or in place of the offset. The named time zone is given last and is enclosed in square brackets
([]). Should both the offset and the named time zone be provided, the offset must match the named time
zone.
Z UTC Z X X
59
Format Description Example Supported for Supported for Time
DateTime
Examples
We show below examples of parsing temporal instant values using various formats. For more details, refer
to An overview of temporal instant type creation.
Query
theDateTime
2015-06-24T12:50:35.556+01:00
Rows: 1
Query
theLocalDateTime
2015-07-04T19:32:24
Rows: 1
Query
60
theDate
2015-03-26
Rows: 1
Parsing a Time:
Query
theTime
12:50:35.556+01:00
Rows: 1
Parsing a LocalTime:
Query
theLocalTime
12:50:35.556
Rows: 1
Table 36. Components of temporal instant values and where they are supported
61
Component Description Type Range/Format Date DateTim LocalDat Time LocalTim
e eTime e
62
Component Description Type Range/Format Date DateTim LocalDat Time LocalTim
e eTime e
The following query shows how to extract the components of a Date value:
Query
Rows: 1
The following query shows how to extract the date related components of a DateTime value:
Query
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.year, d.quarter, d.month, d.week, d.weekYear, d.day, d.ordinalDay, d.dayOfWeek, d.dayOfQuarter
63
Table 38. Result
Rows: 1
The following query shows how to extract the time related components of a DateTime value:
Query
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.hour, d.minute, d.second, d.millisecond, d.microsecond, d.nanosecond
Rows: 1
The following query shows how to extract the epoch time and timezone related components of a
DateTime value:
Query
WITH datetime({
year: 1984, month: 11, day: 11,
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'
}) AS d
RETURN d.timezone, d.offset, d.offsetMinutes, d.epochSeconds, d.epochMillis
Rows: 1
2.10.4. Durations
Specifying durations
A Duration represents a temporal amount, capturing the difference in time between two instants, and can
be negative.
The specification of a Duration is prefixed with a P, and can use either a unit-based form or a date-and-
time-based form:
64
• Unit-based form: P[nY][nM][nW][nD][T[nH][nM][nS]]
◦ The square brackets ([]) denote an optional component (components with a zero value may be
omitted).
◦ The n denotes a numeric value which can be arbitrarily large.
◦ The value of the last — and least significant — component may contain a decimal fraction.
◦ The unit-based form uses M as a suffix for both months and minutes. Therefore, time parts must
always be preceded with T, even when no components of the date part are given.
The following table lists the component identifiers for the unit-based form:
Y Years
W Weeks
D Days
H Hours
S Seconds
Examples
The following examples demonstrate various methods of parsing Duration values. For more details, refer
to Creating a Duration from a string.
Query
theDuration
P14DT16H12M
Rows: 1
65
Query
theDuration
P5M1DT12H
Rows: 1
Query
theDuration
PT45S
Rows: 1
Query
theDuration
P17DT12H
Rows: 1
Within each group, the components can be converted without any loss:
66
• There are always 12 months in 1 year.
• There are always 60 seconds in 1 minute (Cypher uses UTC-SLS when handling leap seconds).
• There are not always 24 hours in 1 day; when switching to/from daylight savings time, a day can have
23 or 25 hours.
• Due to leap years, there are not always the same number of days in a year.
Table 45. Components of Duration values and how they are truncated within their component group
duration.years Months The total number of Integer Each set of 4 quarters is counted
years as 1 year; each set of 12 months
is counted as 1 year.
duration.weeks Days The total number of Integer Each set of 7 days is counted as
weeks 1 week.
duration.days Days The total number of Integer Each week is counted as 7 days.
days
duration.seconds Seconds The total number of Integer Each hour is counted as 3600
seconds seconds; each minute is counted
as 60 seconds.
67
Component Component Group Description Type Details
It is also possible to access the smaller (less significant) components of a component group bounded by
the largest (most significant) component of the group:
The following query shows how to extract the month based components of a Duration value:
Query
1 5 1 17 5 2
Rows: 1
The following query shows how to extract the day based components of a Duration value:
68
Query
3 25 4
Rows: 1
The following query shows how to extract the most significant second based components of a Duration
value:
Query
WITH duration({
years: 1, months:1, days:1, hours: 1,
minutes: 1, seconds: 1, nanoseconds: 111111111
}) AS d
RETURN d.hours, d.minutes, d.seconds, d.milliseconds, d.microseconds, d.nanoseconds
Rows: 1
The following query shows how to extract the less significant second based components of a Duration
value:
Query
WITH duration({
years: 1, months:1, days:1,
hours: 1, minutes: 1, seconds: 1, nanoseconds: 111111111
}) AS d
RETURN d.minutesOfHour, d.secondsOfMinute, d.millisecondsOfSecond, d.microsecondsOfSecond,
d.nanosecondsOfSecond
Rows: 1
2.10.5. Examples
The following examples illustrate the use of some of the temporal functions and operators. Refer to
Temporal functions - instant types and Temporal operators for more details.
69
Create a Duration representing 1.5 days:
Query
theDuration
P1DT12H
Rows: 1
Query
theDuration
P30Y8M13D
Rows: 1
Query
theDuration
P299D
Rows: 1
Query
day
2021-01-01
Rows: 1
70
Query
thursday
2019-10-03
Rows: 1
Query
lastDay
2021-06-30
Rows: 1
Query
theTime
01:42:19Z
Rows: 1
Query
theDuration
P1M2DT25H
Rows: 1
71
Query
theDuration
PT74H54M
Rows: 1
Query
theDuration
PT1H38M
Rows: 1
Examine whether two instants are less than one day apart:
Query
WITH
datetime('2015-07-21T21:40:32.142+0100') AS date1,
datetime('2015-07-21T17:12:56.333+0100') AS date2
RETURN
CASE
WHEN date1 < date2 THEN date1 + duration("P1D") > date2
ELSE date2 + duration("P1D") > date1
END AS lessThanOneDayApart
lessThanOneDayApart
true
Rows: 1
Query
RETURN ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date().month-
1] AS month
month
"May"
72
month
Rows: 1
• Introduction
• Spatial instants
◦ Creating points
• Spatial index
Refer to Spatial functions for information regarding spatial functions allowing for the
creation and manipulation of spatial values.
Refer to Ordering and comparison of values for information regarding the comparison
and ordering of spatial values.
2.11.1. Introduction
Neo4j supports only one type of spatial geometry, the Point with the following characteristics:
• Each point can have either 2 or 3 dimensions. This means it contains either 2 or 3 64-bit floating point
values, which together are called the Coordinate.
• Each point will also be associated with a specific Coordinate Reference System (CRS) that determines
the meaning of the values in the Coordinate.
• Instances of Point and lists of Point can be assigned to node and relationship properties.
• Nodes with Point or List(Point) properties can be indexed using a spatial index. This is true for all CRS
(and for both 2D and 3D). There is no special syntax for creating spatial indexes, as it is supported
using the existing indexes.
73
• The distance function will work on points in all CRS and in both 2D and 3D but only if the two points
have the same CRS (and therefore also same dimension).
◦ Cartesian 3D: x, y, z
Data within different coordinate systems are entirely incomparable, and cannot be implicitly converted
from one to the other. This is true even if they are both cartesian or both geographic. For example, if you
search for 3D points using a 2D range, you will get no results. However, they can be ordered, as discussed
in more detail in Ordering and comparison of values.
• WGS 84 2D
◦ A 2D geographic point in the WGS 84 CRS is specified in one of two ways:
▪ longitude and latitude (if these are specified, and the crs is not, then the crs is assumed to
be WGS-84)
▪ x and y (in this case the crs must be specified, or will be assumed to be Cartesian)
◦ Specifying this CRS can be done using either the name 'wgs-84' or the SRID 4326 as described in
Point(WGS-84)
• WGS 84 3D
◦ A 3D geographic point in the WGS 84 CRS is specified one of in two ways:
▪ longitude, latitude and either height or z (if these are specified, and the crs is not, then the
crs is assumed to be WGS-84-3D)
▪ x, y and z (in this case the crs must be specified, or will be assumed to be Cartesian-3D)
◦ Specifying this CRS can be done using either the name 'wgs-84-3d' or the SRID 4979 as
described in Point(WGS-84-3D)
The units of the latitude and longitude fields are in decimal degrees, and need to be specified as floating
point numbers using Cypher literals. It is not possible to use any other format, like 'degrees, minutes,
seconds'. The units of the height field are in meters. When geographic points are passed to the distance
74
function, the result will always be in meters. If the coordinates are in any other format or unit than
supported, it is necessary to explicitly convert them. For example, if the incoming $height is a string field in
kilometers, you would need to type height: toFloat($height) * 1000. Likewise if the results of the
distance function are expected to be returned in kilometers, an explicit conversion is required. For
example: RETURN distance(a,b) / 1000 AS km. An example demonstrating conversion on incoming and
outgoing values is:
Query
WITH
point({latitude:toFloat('13.43'), longitude:toFloat('56.21')}) AS p1,
point({latitude:toFloat('13.10'), longitude:toFloat('56.41')}) AS p2
RETURN toInteger(distance(p1, p2)/1000) AS km
km
42
Rows: 1
• Cartesian 2D
◦ A 2D point in the Cartesian CRS is specified with a map containing x and y coordinate values
◦ Specifying this CRS can be done using either the name 'cartesian' or the SRID 7203 as described
in Point(Cartesian)
• Cartesian 3D
◦ A 3D point in the Cartesian CRS is specified with a map containing x, y and z coordinate values
◦ Specifying this CRS can be done using either the name 'cartesian-3d' or the SRID 9157 as
described in Point(Cartesian-3D)
The units of the x, y and z fields are unspecified and can mean anything the user intends them to mean.
This also means that when two cartesian points are passed to the distance function, the resulting value
will be in the same units as the original coordinates. This is true for both 2D and 3D points, as the
pythagoras equation used is generalized to any number of dimensions. However, just as you cannot
compare geographic points to cartesian points, you cannot calculate the distance between a 2D point and
a 3D point. If you need to do that, explicitly transform the one type into the other. For example:
Query
WITH
point({x: 3, y: 0}) AS p2d,
point({x: 0, y: 4, z: 1}) AS p3d
RETURN
distance(p2d, p3d) AS bad,
distance(p2d, point({x: p3d.x, y: p3d.y})) AS good
75
bad good
<null> 5.0
Rows: 1
Creating points
All point types are created from two components:
• The Coordinate Reference System (or CRS) defining the meaning (and possibly units) of the values in
the Coordinate
For most use cases it is not necessary to specify the CRS explicitly as it will be deduced from the keys used
to specify the coordinate. Two rules are applied to deduce the CRS from the coordinate:
• Choice of keys:
◦ If the coordinate is specified using the keys latitude and longitude the CRS will be assumed to be
Geographic and therefor either WGS-84 or WGS-84-3D.
◦ If instead x and y are used, then the default CRS would be Cartesian or Cartesian-3D
• Number of dimensions:
◦ If there are 2 dimensions in the coordinate, x & y or longitude & latitude the CRS will be a 2D
CRS
◦ If there is a third dimensions in the coordinate, z or height the CRS will be a 3D CRS
All fields are provided to the point function in the form of a map of explicitly named arguments. We
specifically do not support an ordered list of coordinate fields because of the contradictory conventions
between geographic and cartesian coordinates, where geographic coordinates normally list y before x
(latitude before longitude). See for example the following query which returns points created in each of
the four supported CRS. Take particular note of the order and keys of the coordinates in the original point
function calls, and how those values are displayed in the results:
Query
RETURN
point({x: 3, y: 0}) AS cartesian_2d,
point({x: 0, y: 4, z: 1}) AS cartesian_3d,
point({latitude: 12, longitude: 56}) AS geo_2d,
point({latitude: 12, longitude: 56, height: 1000}) AS geo_3d
point({x: 3.0, y: 0.0, point({x: 0.0, y: 4.0, z: point({x: 56.0, y: 12.0, point({x: 56.0, y: 12.0,
crs: 'cartesian'}) 1.0, crs: 'cartesian-3d'}) crs: 'wgs-84'}) z: 1000.0, crs: 'wgs-84-
3d'})
76
cartesian_2d cartesian_3d geo_2d geo_3d
Rows: 1
For the geographic coordinates, it is important to note that the latitude value should always lie in the
interval [-90, 90] and any other value outside this range will throw an exception. The longitude value
should always lie in the interval [-180, 180] and any other value outside this range will be wrapped
around to fit in this range. The height value and any cartesian coordinates are not explicitly restricted, and
any value within the allowed range of the signed 64-bit floating point type will be accepted.
Table 65. Components of point instances and where they are supported
77
Component Description Type Range/Forma WGS-84 WGS-84-3D Cartesian Cartesian-3D
t
The following query shows how to extract the components of a Cartesian 2D point value:
Query
x y crs srid
Rows: 1
The following query shows how to extract the components of a WGS-84 3D point value:
Query
78
latitude longitude height x y z crs srid
Rows: 1
Query
WITH
point({x: 3, y: 0}) AS p2d,
point({x: 0, y: 4, z: 1}) AS p3d
RETURN
distance(p2d, p3d),
p2d < p3d,
p2d = p3d,
p2d <> p3d,
distance(p2d, point({x: p3d.x, y: p3d.y}))
distance(p2d, p3d) p2d < p3d p2d = p3d p2d <> p3d distance(p2d, point({x:
p3d.x, y: p3d.y}))
Rows: 1
However, all types are orderable. The Point types will be ordered after Numbers and before Temporal
types. Points with different CRS with be ordered by their SRID numbers. For the current set of four CRS,
this means the order is WGS84, WGS84-3D, Cartesian, Cartesian-3D.
79
Query
UNWIND [
point({x: 3, y: 0}),
point({x: 0, y: 4, z: 1}),
point({srid: 4326, x: 12, y: 56}),
point({srid: 4979, x: 12, y: 56, z: 1000})
] AS point
RETURN point ORDER BY point
point
Rows: 4
2.12. Lists
Cypher has comprehensive support for lists.
• Lists in general
• List comprehension
• Pattern comprehension
checking (IN) and access ([]) can be found here. The behavior of the IN and [] operators
with respect to null is detailed here.
Query
list
[0,1,2,3,4,5,6,7,8,9]
Rows: 1
In our examples, we’ll use the range function. It gives you a list containing all numbers between given start
and end numbers. Range is inclusive in both ends.
80
To access individual elements in the list, we use the square brackets again. This will extract from the start
index and up to but not including the end index.
Query
range(0, 10)[3]
Rows: 1
You can also use negative numbers, to start from the end of the list instead.
Query
range(0, 10)[-3]
Rows: 1
Finally, you can use ranges inside the brackets to return ranges of the list.
Query
range(0, 10)[0..3]
[0,1,2]
Rows: 1
Query
range(0, 10)[0..-5]
[0,1,2,3,4,5]
Rows: 1
81
Query
range(0, 10)[-5..]
[6,7,8,9,10]
Rows: 1
Query
range(0, 10)[..4]
[0,1,2,3]
Rows: 1
Out-of-bound slices are simply truncated, but out-of-bound single elements return null.
Query
range(0, 10)[15]
<null>
Rows: 1
Query
range(0, 10)[5..15]
[5,6,7,8,9,10]
Rows: 1
Query
82
Table 79. Result
size(range(0, 10)[0..3])
Rows: 1
Query
result
[0.0,8.0,64.0,216.0,512.0,1000.0]
Rows: 1
Either the WHERE part, or the expression, can be omitted, if you only want to filter or map respectively.
Query
result
[0,2,4,6,8,10]
Rows: 1
Query
result
[0.0,1.0,8.0,27.0,64.0,125.0,216.0,343.0,512.0,729.0,1000.0]
Rows: 1
83
predicates just like a normal WHERE clause, but will yield a custom projection as specified.
Graph
N0 [
label = "{Person|name = \'Keanu Reeves\'\l}"
]
N0 -> N7 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N6 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N1 [
label = "{Movie|title = \'Johnny Mnemonic\'\lreleased = 1995\l}"
]
N2 [
label = "{Movie|title = \'Somethings Gotta Give\'\lreleased = 2003\l}"
]
N3 [
label = "{Movie|title = \'The Matrix Revolutions\'\lreleased = 2003\l}"
]
N4 [
label = "{Movie|title = \'The Matrix Reloaded\'\lreleased = 2003\l}"
]
N5 [
label = "{Movie|title = \'The Replacements\'\lreleased = 2000\l}"
]
N6 [
label = "{Movie|title = \'The Matrix\'\lreleased = 1999\l}"
]
N7 [
label = "{Movie|title = \'The Devils Advocate\'\lreleased = 1997\l}"
]
Query
84
Table 83. Result
years
[1997,2003,2003,2000,1999,2003,1995]
Rows: 1
The whole predicate, including the WHERE keyword, is optional and may be omitted.
2.13. Maps
This section describes how to use maps in Cyphers.
• Literal maps
• Map projection
◦ Examples of map projection
Graph
N0 [
label = "{Person|name = \'Charlie Sheen\'\lrealName = \'Carlos Irwin Estévez\'\l}"
]
N0 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N1 [
label = "{Person|name = \'Martin Sheen\'\l}"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N1 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\n"
]
N2 [
label = "{Movie|year = 1987\ltitle = \'Wall Street\'\l}"
]
N3 [
label = "{Movie|year = 1984\ltitle = \'Red Dawn\'\l}"
]
N4 [
label = "{Movie|year = 1979\ltitle = \'Apocalypse Now\'\l}"
]
85
Information regarding property access operators such as . and [] can be found here.
The behavior of the [] operator with respect to null is detailed here.
Query
{listKey -> [{inner -> "Map1"},{inner -> "Map2"}], key -> "Value"}
Rows: 1
A map projection begins with the variable bound to the graph entity to be projected from, and contains a
body of comma-separated map elements, enclosed by { and }.
A map element projects one or more key-value pairs to the map projection. There exist four different types
of map projection elements:
• Property selector - Projects the property name as the key, and the value from the map_variable as the
value for the projection.
• Literal entry - This is a key-value pair, with the value being arbitrary expression key: <expression>.
• Variable selector - Projects a variable, with the variable name as the key, and the value the variable is
pointing to as the value of the projection. Its syntax is just the variable.
• All-properties selector - projects all key-value pairs from the map_variable value.
• If the map_variable points to a null value, the whole map projection will evaluate to null.
86
example of map projection with a literal entry, which in turn also uses map projection inside the
aggregating collect().
Query
actor
{movies -> [{year -> 1979, title -> "Apocalypse Now"},{year -> 1984, title -> "Red Dawn"},{year -> 1987, title
-> "Wall Street"}], realName -> "Carlos Irwin Estévez", name -> "Charlie Sheen"}
Rows: 1
Find all persons that have acted in movies, and show number for each. This example introduces an variable
with the count, and uses a variable selector to project the value.
Query
MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie)
WITH actor, count(movie) AS nbrOfMovies
RETURN actor{.name, nbrOfMovies}
actor
Rows: 2
Again, focusing on 'Charlie Sheen', this time returning all properties from the node. Here we use an all-
properties selector to project all the node properties, and additionally, explicitly project the property age.
Since this property does not exist on the node, a null value is projected instead.
Query
actor
{realName -> "Carlos Irwin Estévez", name -> "Charlie Sheen", age -> <null>}
Rows: 1
87
• Introduction to null in Cypher
null is not equal to null. Not knowing two values does not imply that they are the same value. So the
expression null = null yields null and not true.
Here is the truth table for AND, OR, XOR and NOT.
Expression Result
2 IN [1, 2, 3] true
88
Expression Result
2 IN [1] false
2 IN [] false
null IN [] false
Using all, any, none, and single follows a similar rule. If the result can be calculated definitely, true or
false is returned. Otherwise null is produced.
Expression Result
Using parameters to pass in the bounds, such as a[$lower..$upper], may result in a null for the lower or
upper bound (or both). The following workaround will prevent this from happening by setting the absolute
minimum and maximum bound values:
a[coalesce($lower,0)..coalesce($upper,size(a))]
• Trying to access a property that does not exist on a node or relationship: n.missingProperty
[1] The 365.2425 days per year comes from the frequency of leap years. A leap year occurs on a year with an ordinal number
divisible by 4, that is not divisible by 100, unless it divisible by 400. This means that over 400 years there are ((365 * 4 + 1)
* 25 - 1) * 4 + 1 = 146097 days, which means an average of 365.2425 days per year.
[2] This is in accordance with the Gregorian calendar; i.e. years AD/CE start at year 1, and the year before that (year 1
BC/BCE) is 0, while year 2 BCE is -1 etc.
[3] The first week of any year is the week that contains the first Thursday of the year, and thus always contains January 4.
[4] For dates from December 29, this could be the next year, and for dates until January 3 this could be the previous year,
89
depending on how week 1 begins.
[5] The expression datetime().epochMillis returns the equivalent value of the timestamp() function.
[6] For the nanosecond part of the epoch offset, the regular nanosecond component (instant.nanosecond) can be used.
90
Chapter 3. Clauses
This section contains information on all the clauses in the Cypher query language.
• Reading clauses
• Projecting clauses
• Reading sub-clauses
• Reading hints
• Writing clauses
• Reading/Writing clauses
• Set operations
• Subquery clauses
• Multiple graphs
• Importing data
• Administration clauses
Reading clauses
The flow of data within a Cypher query is an unordered sequence of maps with key-value pairs — a set of
possible bindings between the variables in the query and values derived from the database. This set is
refined and augmented by subsequent parts of the query.
Clause Description
OPTIONAL MATCH Specify the patterns to search for in the database while using
nulls for missing parts of the pattern.
Projecting clauses
These comprise clauses that define which expressions to return in the result set. The returned expressions
may all be aliased using AS.
Clause Description
RETURN ... [AS] Defines what to include in the query result set.
WITH ... [AS] Allows query parts to be chained together, piping the results
from one to be used as starting points or criteria in the next.
Reading sub-clauses
91
These comprise sub-clauses that must operate as part of reading clauses.
Sub-clause Description
WHERE EXISTS { ... } An existential sub-query used to filter the results of a MATCH,
OPTIONAL MATCH or WITH clause.
ORDER BY [ASC[ENDING] | DESC[ENDING]] A sub-clause following RETURN or WITH, specifying that the
output should be sorted in either ascending (the default) or
descending order.
SKIP Defines from which row to start including the rows in the
output.
Reading hints
These comprise clauses used to specify planner hints when tuning a query. More details regarding the
usage of these — and query tuning in general — can be found in Planner hints and the USING keyword.
Hint Description
USING INDEX Index hints are used to specify which index, if any, the
planner should use as a starting point.
USING INDEX SEEK Index seek hint instructs the planner to use an index seek for
this clause.
USING SCAN Scan hints are used to force the planner to do a label scan
(followed by a filtering operation) instead of using an index.
USING JOIN Join hints are used to enforce a join operation at specified
points.
Writing clauses
Clause Description
92
Clause Description
Reading/Writing clauses
These comprise clauses that both read data from and write data to the database.
Clause Description
MERGE Ensures that a pattern exists in the graph. Either the pattern
already exists, or it needs to be created.
CALL ... [YIELD ... ] Invokes a procedure deployed in the database and return any
results.
Set operations
Clause Description
Subquery clauses
Clause Description
Multiple graphs
Clause Description
Importing data
93
Clause Description
--- USING PERIODIC COMMIT This query hint may be used to prevent an out-of-memory
error from occurring when importing large amounts of data
using LOAD CSV.
Administration clauses
These comprise clauses used to manage databases, schema and security; further details can found in
Administration.
Clause Description
CREATE | DROP | START | STOP DATABASE Create, drop, start or stop a database.
CREATE | DROP INDEX Create or drop an index on all nodes with a particular label
and property.
CREATE | DROP CONSTRAINT Create or drop a constraint pertaining to either a node label
or relationship type, and a property.
Users, roles, privileges Manage users, roles and privileges for database, graph and
sub-graph access control.
3.1. MATCH
The MATCH clause is used to search for the pattern described in it.
• Introduction
◦ Related nodes
• Relationship basics
◦ Outgoing relationships
• Relationships in depth
◦ Relationship types with uncommon characters
◦ Multiple relationships
94
◦ Variable length relationships
◦ Named paths
• Shortest path
◦ Single shortest path
◦ Relationship by id
◦ Multiple nodes by id
3.1.1. Introduction
The MATCH clause allows you to specify the patterns Neo4j will search for in the database. This is the
primary way of getting data into the current set of bindings. It is worth reading up more on the
specification of the patterns themselves in Patterns.
MATCH is often coupled to a WHERE part which adds restrictions, or predicates, to the MATCH patterns, making
them more specific. The predicates are part of the pattern description, and should not be considered a filter
applied only after the matching is done. This means that WHERE should always be put together with the
MATCH clause it belongs to.
MATCH can occur at the beginning of the query or later, possibly after a WITH. If it is the first clause, nothing
will have been bound yet, and Neo4j will design a search to find the results matching the clause and any
associated predicates specified in any WHERE part. This could involve a scan of the database, a search for
nodes having a certain label, or a search of an index to find starting points for the pattern matching. Nodes
and relationships found by this search are available as bound pattern elements, and can be used for
pattern matching of paths. They can also be used in any further MATCH clauses, where Neo4j will use the
known elements, and from there find further unknown elements.
Cypher is declarative, and so usually the query itself does not specify the algorithm to use to perform the
search. Neo4j will automatically work out the best approach to finding start nodes and matching patterns.
Predicates in WHERE parts can be evaluated before pattern matching, during pattern matching, or after
finding matches. However, there are cases where you can influence the decisions taken by the query
compiler. Read more about indexes in Indexes for search performance, and more about specifying hints to
force Neo4j to solve a query in a specific way in Planner hints and the USING keyword.
95
To understand more about the patterns used in the MATCH clause, read Patterns
Graph
N0 [
label = "{Person|name = \'Charlie Sheen\'\l}"
]
N0 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Bud Fox\'\l"
]
N1 [
label = "{Person|name = \'Martin Sheen\'\l}"
]
N1 -> N6 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'A.J. MacInerney\'\l"
]
N1 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Carl Fox\'\l"
]
N2 [
label = "{Person|name = \'Michael Douglas\'\l}"
]
N2 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Gordon Gekko\'\l"
]
N2 -> N6 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'President Andrew Shepherd\'\l"
]
N3 [
label = "{Person|name = \'Oliver Stone\'\l}"
]
N3 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "DIRECTED\n"
]
N4 [
label = "{Person|name = \'Rob Reiner\'\l}"
]
N4 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "DIRECTED\n"
]
N5 [
label = "{Movie|title = \'Wall Street\'\l}"
]
N6 [
label = "{Movie|title = \'The American President\'\l}"
]
96
Query
MATCH (n)
RETURN n
Node[0]{name:"Charlie Sheen"}
Node[1]{name:"Martin Sheen"}
Node[2]{name:"Michael Douglas"}
Node[3]{name:"Oliver Stone"}
Node[4]{name:"Rob Reiner"}
Node[5]{title:"Wall Street"}
Rows: 7
Query
MATCH (movie:Movie)
RETURN movie.title
movie.title
"Wall Street"
Rows: 2
Related nodes
The symbol -- means related to, without regard to type or direction of the relationship.
Query
97
Table 90. Result
movie.title
"Wall Street"
Rows: 1
Query
Returns any nodes connected with the Person 'Oliver' that are labeled Movie.
movie.title
"Wall Street"
Rows: 1
Outgoing relationships
When the direction of a relationship is of interest, it is shown by using --> or <--, like this:
Query
Returns any nodes connected with the Person 'Oliver' by an outgoing relationship.
movie.title
"Wall Street"
Rows: 1
98
Query
type(r)
"DIRECTED"
Rows: 1
Query
actor.name
"Michael Douglas"
"Martin Sheen"
"Charlie Sheen"
Rows: 3
Query
person.name
"Oliver Stone"
"Michael Douglas"
99
person.name
"Martin Sheen"
"Charlie Sheen"
Rows: 4
Query
r.role
"Gordon Gekko"
"Carl Fox"
"Bud Fox"
Rows: 3
Query
MATCH
(charlie:Person {name: 'Charlie Sheen'}),
(rob:Person {name: 'Rob Reiner'})
CREATE (rob)-[:`TYPE INCLUDING A SPACE`]->(charlie)
100
Graph
N0 [
label = "{Person|name = \'Charlie Sheen\'\l}"
]
N0 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Bud Fox\'\l"
]
N1 [
label = "{Person|name = \'Martin Sheen\'\l}"
]
N1 -> N6 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'A.J. MacInerney\'\l"
]
N1 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Carl Fox\'\l"
]
N2 [
label = "{Person|name = \'Michael Douglas\'\l}"
]
N2 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'Gordon Gekko\'\l"
]
N2 -> N6 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTED_IN\nrole = \'President Andrew Shepherd\'\l"
]
N3 [
label = "{Person|name = \'Oliver Stone\'\l}"
]
N3 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "DIRECTED\n"
]
N4 [
label = "{Person|name = \'Rob Reiner\'\l}"
]
N4 -> N0 [
color = "#a40000"
fontcolor = "#a40000"
label = "TYPE INCLUDING A SPACE\n"
]
N4 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "DIRECTED\n"
]
N5 [
label = "{Movie|title = \'Wall Street\'\l}"
]
N6 [
label = "{Movie|title = \'The American President\'\l}"
]
Query
101
Table 97. Result
type(r)
Rows: 1
Multiple relationships
Relationships can be expressed by using multiple statements in the form of ()--(), or they can be strung
together, like this:
Query
movie.title director.name
Rows: 1
Query
movie.title
"Wall Street"
Rows: 3
102
Variable length relationships with multiple relationship types
Variable length relationships can be combined with multiple relationship types. In this case the
*minHops..maxHops applies to all relationship types as well as any combination of them.
Query
Returns all people related to 'Charlie Sheen' by 2 hops with any combination of the relationship types
ACTED_IN and DIRECTED.
person.name
"Oliver Stone"
"Michael Douglas"
"Martin Sheen"
Rows: 3
Query
relationships(p)
Rows: 2
103
Query
MATCH
(charlie:Person {name: 'Charlie Sheen'}),
(martin:Person {name: 'Martin Sheen'})
CREATE (charlie)-[:X {blocked: false}]->(:UNBLOCKED)<-[:X {blocked: false}]-(martin)
CREATE (charlie)-[:X {blocked: true}]->(:BLOCKED)<-[:X {blocked: false}]-(martin)
This means that we are starting out with the following graph:
Graph
N0 [
label = "{Person|name = \'Charlie Sheen\'\l}"
]
N0 -> N7 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "X\nblocked = false\l"
]
N0 -> N8 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "X\nblocked = true\l"
]
N0 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\nrole = \'Bud Fox\'\l"
]
N1 [
label = "{Person|name = \'Martin Sheen\'\l}"
]
N1 -> N8 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "X\nblocked = false\l"
]
N1 -> N7 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "X\nblocked = false\l"
]
N1 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\nrole = \'A.J. MacInerney\'\l"
]
N1 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\nrole = \'Carl Fox\'\l"
]
N2 [
label = "{Person|name = \'Michael Douglas\'\l}"
]
N2 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\nrole = \'Gordon Gekko\'\l"
]
N2 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\nrole = \'President Andrew Shepherd\'\l"
]
N3 [
label = "{Person|name = \'Oliver Stone\'\l}"
]
N3 -> N5 [
color = "#a40000"
fontcolor = "#a40000"
label = "DIRECTED\n"
104
]
N4 [
label = "{Person|name = \'Rob Reiner\'\l}"
]
N4 -> N6 [
color = "#a40000"
fontcolor = "#a40000"
label = "DIRECTED\n"
]
N5 [
label = "{Movie|title = \'Wall Street\'\l}"
]
N6 [
label = "{Movie|title = \'The American President\'\l}"
]
N7 [
label = "{UNBLOCKED|}"
]
N8 [
label = "{BLOCKED|}"
]
Query
Returns the paths between 'Charlie Sheen' and 'Martin Sheen' where all relationships have the blocked
property set to false.
(0)-[X,7]->(7)<-[X,8]-(1)
Rows: 1
Query
Returns the movie itself as well as actors and directors one relationship away
Node[5]{title:"Wall Street"}
Node[3]{name:"Oliver Stone"}
Node[2]{name:"Michael Douglas"}
105
x
Node[1]{name:"Martin Sheen"}
Node[0]{name:"Charlie Sheen"}
Rows: 5
Named paths
If you want to return or filter on a path in your pattern graph, you can a introduce a named path.
Query
(2)-[ACTED_IN,2]->(5)
(2)-[ACTED_IN,5]->(6)
Rows: 2
Query
MATCH (a)-[r]-(b)
WHERE id(r) = 0
RETURN a, b
This returns the two connected nodes, once as the start node, and once as the end node
a b
Rows: 2
106
Single shortest path
Finding a single shortest path between two nodes is as easy as using the shortestPath function. It is done
like this:
Query
MATCH
(martin:Person {name: 'Martin Sheen'}),
(oliver:Person {name: 'Oliver Stone'}),
p = shortestPath((martin)-[*..15]-(oliver))
RETURN p
This means: find a single shortest path between two nodes, as long as the path is max 15 relationships
long. Within the parentheses you define a single link of a path — the starting node, the connecting
relationship and the end node. Characteristics describing the relationship like relationship type, max hops
and direction are all used when finding the shortest path. If there is a WHERE clause following the match of a
shortestPath, relevant predicates will be included in the shortestPath. If the predicate is a none() or all()
on the relationship elements of the path, it will be used during the search to improve performance (see
Shortest path planning).
(1)-[ACTED_IN,1]->(5)<-[DIRECTED,3]-(3)
Rows: 1
Query
MATCH
(charlie:Person {name: 'Charlie Sheen'}),
(martin:Person {name: 'Martin Sheen'}),
p = shortestPath((charlie)-[*]-(martin))
WHERE none(r IN relationships(p) WHERE type(r) = 'FATHER')
RETURN p
This query will find the shortest path between 'Charlie Sheen' and 'Martin Sheen', and the WHERE predicate
will ensure that we do not consider the father/son relationship between the two.
(0)-[ACTED_IN,0]->(5)<-[ACTED_IN,1]-(1)
Rows: 1
107
All shortest paths
Finds all the shortest paths between two nodes.
Query
MATCH
(martin:Person {name: 'Martin Sheen'} ),
(michael:Person {name: 'Michael Douglas'}),
p = allShortestPaths((martin)-[*]-(michael))
RETURN p
Finds the two shortest paths between 'Martin Sheen' and 'Michael Douglas'.
(1)-[ACTED_IN,1]->(5)<-[ACTED_IN,2]-(2)
(1)-[ACTED_IN,4]->(6)<-[ACTED_IN,5]-(2)
Rows: 2
Node by id
Searching for nodes by id can be done with the id() function in a predicate.
Neo4j reuses its internal ids when nodes and relationships are deleted. This means that
applications using, and relying on internal Neo4j ids, are brittle or at risk of making
mistakes. It is therefore recommended to rather use application-generated ids.
Query
MATCH (n)
WHERE id(n) = 0
RETURN n
Node[0]{name:"Charlie Sheen"}
Rows: 1
Relationship by id
Search for relationships by id can be done with the id() function in a predicate.
This is not the recommended practice. See Node by id for more information on the use of Neo4j ids.
108
Query
MATCH ()-[r]->()
WHERE id(r) = 0
RETURN r
:ACTED_IN[0]{role:"Bud Fox"}
Rows: 1
Multiple nodes by id
Multiple nodes are selected by specifying them in an IN clause.
Query
MATCH (n)
WHERE id(n) IN [0, 3, 5]
RETURN n
Node[0]{name:"Charlie Sheen"}
Node[3]{name:"Oliver Stone"}
Node[5]{title:"Wall Street"}
Rows: 3
• Introduction
• Optional relationships
109
3.2.1. Introduction
OPTIONAL MATCH matches patterns against your graph database, just like MATCH does. The difference is that
if no matches are found, OPTIONAL MATCH will use a null for missing parts of the pattern. OPTIONAL MATCH
could be considered the Cypher equivalent of the outer join in SQL.
Either the whole pattern is matched, or nothing is matched. Remember that WHERE is part of the pattern
description, and the predicates will be considered while looking for matches, not after. This matters
especially in the case of multiple (OPTIONAL) MATCH clauses, where it is crucial to put WHERE together with the
MATCH it belongs to.
To understand the patterns used in the OPTIONAL MATCH clause, read Patterns.
110
Graph
N0 [
label = "{Person|name = \'Charlie Sheen\'\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FATHER\n"
]
N0 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N1 [
label = "{Person|name = \'Martin Sheen\'\l}"
]
N1 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N1 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N2 [
label = "{Person|name = \'Michael Douglas\'\l}"
]
N2 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N2 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N3 [
label = "{Person|name = \'Oliver Stone\'\l}"
]
N3 -> N5 [
color = "#a40000"
fontcolor = "#a40000"
label = "DIRECTED\n"
]
N4 [
label = "{Person|name = \'Rob Reiner\'\l}"
]
N4 -> N6 [
color = "#a40000"
fontcolor = "#a40000"
label = "DIRECTED\n"
]
N5 [
label = "{Movie|title = \'Wall Street\'\l}"
]
N6 [
label = "{Movie|title = \'The American President\'\l}"
]
111
Query
<null>
Rows: 1
Query
Returns the element x (null in this query), and null as its name.
x x.name
<null> <null>
Rows: 1
Query
This returns the title of the node, 'Wall Street', and, since the node has no outgoing ACTS_IN relationships,
null is returned for the relationship denoted by r.
a.title r
112
a.title r
Rows: 1
3.3. RETURN
The RETURN clause defines what to include in the query result set.
• Introduction
• Return nodes
• Return relationships
• Return property
• Column alias
• Optional properties
• Other expressions
• Unique results
3.3.1. Introduction
In the RETURN part of your query, you define which parts of the pattern you are interested in. It can be
nodes, relationships, or properties on these.
If what you actually want is the value of a property, make sure to not return the full
node/relationship. This will improve performance.
Graph
N0 [
label = "happy = \'Yes!\'\lname = \'A\'\lage = 55\l"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "BLOCKS\n"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N1 [
label = "name = \'B\'\l"
]
113
3.3.2. Return nodes
To return a node, list it in the RETURN statement.
Query
Node[1]{name:"B"}
Rows: 1
Query
:KNOWS[0]{}
Rows: 1
Query
n.name
"A"
Rows: 1
114
3.3.5. Return all elements
When you want to return all nodes, relationships and paths found in a query, you can use the * symbol.
Query
This returns the two nodes, the relationship and the path used in the query.
a b p r
Rows: 2
Query
"Yes!"
Rows: 1
Query
115
Table 120. Result
SomethingTotallyDifferent
55
Rows: 1
Query
MATCH (n)
RETURN n.age
This example returns the age when the node has that property, or null if the property is not there.
n.age
55
<null>
Rows: 2
Query
Returns a predicate, a literal and function call with a pattern expression parameter.
Rows: 1
116
Query
The node named "B" is returned by the query, but only once.
Node[1]{name:"B"}
Rows: 1
3.4. WITH
The WITH clause allows query parts to be chained together, piping the results from one to
be used as starting points or criteria in the next.
It is important to note that WITH affects variables in scope. Any variables not included in
the WITH clause are not carried over to the rest of the query.
• Introduction
3.4.1. Introduction
Using WITH, you can manipulate the output before it is passed on to the following query parts. The
manipulations can be of the shape and/or number of entries in the result set.
One common usage of WITH is to limit the number of entries that are then passed on to other MATCH clauses.
By combining ORDER BY and LIMIT, it’s possible to get the top X entries by some criteria, and then bring in
additional data from the graph.
Another use is to filter on aggregated values. WITH is used to introduce aggregates which can then be used
in predicates in WHERE. These aggregate expressions create new bindings in the results. WITH can also, like
RETURN, alias expressions that are introduced into the results using the aliases as the binding name.
WITH is also used to separate reading from updating of the graph. Every part of a query must be either
read-only or write-only. When going from a writing part to a reading part, the switch must be done with a
WITH clause.
117
Graph
N0 [
label = "name = \'Anders\'\l"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "BLOCKS\n"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N1 [
label = "name = \'Bossman\'\l"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "BLOCKS\n"
]
N2 [
label = "name = \'Caesar\'\l"
]
N2 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N3 [
label = "name = \'David\'\l"
]
N3 -> N0 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N4 [
label = "name = \'George\'\l"
]
Query
The name of the person connected to 'David' with the at least more than one outgoing relationship will be
returned by the query.
otherPerson.name
"Anders"
118
otherPerson.name
Rows: 1
Query
MATCH (n)
WITH n
ORDER BY n.name DESC
LIMIT 3
RETURN collect(n.name)
collect(n.name)
["George","David","Caesar"]
Rows: 1
Query
Starting at 'Anders', find all matching nodes, order by name descending and get the top result, then find all
the nodes connected to that top result, and return their names.
o.name
"Anders"
"Bossman"
Rows: 2
3.5. UNWIND
UNWIND expands a list into a sequence of rows.
119
• Introduction
• Unwinding a list
3.5.1. Introduction
With UNWIND, you can transform any list back into individual rows. These lists can be parameters that were
passed in, previously collect -ed result or other list expressions.
One common usage of unwind is to create distinct lists. Another is to create data from parameter lists that
are provided to the query.
UNWIND requires you to specify a new name for the inner values.
Query
x y
1 "val"
2 "val"
3 "val"
<null> "val"
Rows: 4
120
Query
Each value of the original list is unwound and passed through DISTINCT to create a unique set.
setOfVals
[1,2]
Rows: 1
Query
WITH
[1, 2] AS a,
[3, 4] AS b
UNWIND (a + b) AS x
RETURN x
The two lists — a and b — are concatenated to form a new list, which is then operated upon by UNWIND.
Rows: 4
Query
The first UNWIND results in three rows for x, each of which contains an element of the original list (two of
which are also lists); namely, [1, 2], [3, 4] and 5. The second UNWIND then operates on each of these rows
121
in turn, resulting in five rows for y.
Rows: 5
Essentially, UNWIND [] reduces the number of rows to zero, and thus causes the query to cease its
execution, returning no results. This has value in cases such as UNWIND v, where v is a variable from an
earlier clause that may or may not be an empty list — when it is an empty list, this will behave just as a
MATCH that has no results.
Query
UNWIND [] AS empty
RETURN empty, 'literal_that_is_not_returned'
Rows: 0
To avoid inadvertently using UNWIND on an empty list, CASE may be used to replace an empty list with a
null:
WITH [] AS list
UNWIND
CASE
WHEN list = [] THEN [null]
ELSE list
END AS emptylist
RETURN emptylist
122
Query
UNWIND null AS x
RETURN x, 'some_literal'
Rows: 0
Parameters
{
"events" : [ {
"year" : 2014,
"id" : 1
}, {
"year" : 2014,
"id" : 2
} ]
}
Query
Each value of the original list is unwound and passed through MERGE to find or create the nodes and
relationships.
Rows: 2
Nodes created: 3
Relationships created: 2
Properties set: 3
Labels added: 3
3.6. WHERE
WHERE adds constraints to the patterns in a MATCH or OPTIONAL MATCH clause or filters the
results of a WITH clause.
123
• Introduction
• Basic usage
◦ Boolean operations
• String matching
◦ Prefix string search using STARTS WITH
• Regular expressions
◦ Matching using regular expressions
• Lists
◦ IN operator
◦ Filter on null
• Using ranges
◦ Simple range
◦ Composite range
124
3.6.1. Introduction
WHERE is not a clause in its own right — rather, it’s part of MATCH, OPTIONAL MATCH and WITH.
For MATCH and OPTIONAL MATCH on the other hand, WHERE adds constraints to the patterns described. It
should not be seen as a filter after the matching is finished.
In the case of multiple MATCH / OPTIONAL MATCH clauses, the predicate in WHERE is always a
part of the patterns in the directly preceding MATCH / OPTIONAL MATCH. Both results and
performance may be impacted if the WHERE is put inside the wrong MATCH clause.
125
Graph
N0 [
label = "{Swedish, Person|belt = \'white\'\lname = \'Andy\'\lage = 36\l}"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "HAS_DOG\nsince = 2016\l"
]
N0 -> N2 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\nsince = 1999\l"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\nsince = 2012\l"
]
N1 [
label = "{Person|address = \'Sweden/Malmo\'\lname = \'Timothy\'\lage = 25\l}"
]
N2 [
label = "{Person|email = \'[email protected]\'\lname = \'Peter\'\lage = 35\l}"
]
N2 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "HAS_DOG\nsince = 2010\l"
]
N2 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "HAS_DOG\nsince = 2018\l"
]
N3 [
label = "{Dog|name = \'Andy\'\l}"
]
N4 [
label = "{Dog|name = \'Fido\'\l}"
]
N4 -> N6 [
color = "#a40000"
fontcolor = "#a40000"
label = "HAS_TOY\n"
]
N5 [
label = "{Dog|name = \'Ozzy\'\l}"
]
N6 [
label = "{Toy|name = \'Banana\'\l}"
]
Boolean operations
You can use the boolean operators AND, OR, XOR and NOT. See Working with null for more information on
how this works with null.
Query
MATCH (n:Person)
WHERE n.name = 'Peter' XOR (n.age < 30 AND n.name = 'Timothy') OR NOT (n.name = 'Timothy' OR n.name =
'Peter')
RETURN n.name, n.age
126
Table 134. Result
n.name n.age
"Andy" 36
"Timothy" 25
"Peter" 35
Rows: 3
Query
MATCH (n)
WHERE n:Swedish
RETURN n.name, n.age
The name and age for the 'Andy' node will be returned.
n.name n.age
"Andy" 36
Rows: 1
Query
MATCH (n:Person)
WHERE n.age < 30
RETURN n.name, n.age
The name and age values for the 'Timothy' node are returned because he is less than 30 years of age.
n.name n.age
"Timothy" 25
Rows: 1
127
Query
MATCH (n:Person)-[k:KNOWS]->(f)
WHERE k.since < 2000
RETURN f.name, f.age, f.email
The name, age and email values for the 'Peter' node are returned because Andy has known him since
before 2000.
"Peter" 35 "[email protected]"
Rows: 1
Query
The name and age values for the 'Timothy' node are returned because he is less than 30 years of age.
n.name n.age
"Timothy" 25
Rows: 1
Query
MATCH (n:Person)
WHERE exists(n.belt)
RETURN n.name, n.belt
The name and belt for the 'Andy' node are returned because he is the only one with a belt property.
The has() function has been superseded by exists() and has been removed.
128
n.name n.belt
"Andy" "white"
Rows: 1
Query
MATCH (n:Person)
WHERE n.name STARTS WITH 'Pet'
RETURN n.name, n.age
The name and age for the 'Peter' node are returned because his name starts with 'Pet'.
n.name n.age
"Peter" 35
Rows: 1
Query
MATCH (n:Person)
WHERE n.name ENDS WITH 'ter'
RETURN n.name, n.age
The name and age for the 'Peter' node are returned because his name ends with 'ter'.
n.name n.age
"Peter" 35
Rows: 1
129
Substring search using CONTAINS
The CONTAINS operator is used to perform case-sensitive matching regardless of location within a string.
Query
MATCH (n:Person)
WHERE n.name CONTAINS 'ete'
RETURN n.name, n.age
The name and age for the 'Peter' node are returned because his name contains with 'ete'.
n.name n.age
"Peter" 35
Rows: 1
Query
MATCH (n:Person)
WHERE NOT n.name ENDS WITH 'y'
RETURN n.name, n.age
The name and age for the 'Peter' node are returned because his name does not end with 'y'.
n.name n.age
"Peter" 35
Rows: 1
130
Query
MATCH (n:Person)
WHERE n.name =~ 'Tim.*'
RETURN n.name, n.age
The name and age for the 'Timothy' node are returned because his name starts with 'Tim'.
n.name n.age
"Timothy" 25
Rows: 1
Query
MATCH (n:Person)
WHERE n.email =~ '.*\\.com'
RETURN n.name, n.age, n.email
The name, age and email for the 'Peter' node are returned because his email ends with '.com'.
"Peter" 35 "[email protected]"
Rows: 1
Query
MATCH (n:Person)
WHERE n.name =~ '(?i)AND.*'
RETURN n.name, n.age
The name and age for the 'Andy' node are returned because his name starts with 'AND' irrespective of
casing.
n.name n.age
"Andy" 36
Rows: 1
131
3.6.5. Using path patterns in WHERE
Filter on patterns
Patterns are expressions in Cypher, expressions that return a list of paths. List expressions are also
predicates — an empty list represents false, and a non-empty represents true.
So, patterns are not only expressions, they are also predicates. The only limitation to your pattern is that
you must be able to express it in a single path. You cannot use commas between multiple paths like you do
in MATCH. You can achieve the same effect by combining multiple patterns with AND.
Note that you cannot introduce new variables here. Although it might look very similar to the MATCH
patterns, the WHERE clause is all about eliminating matched paths. MATCH (a)-[*]->(b) is very different
from WHERE (a)-[*]->(b). The first will produce a path for every path it can find between a and b, whereas
the latter will eliminate any matched paths where a and b do not have a directed relationship chain
between them.
Query
MATCH
(timothy:Person {name: 'Timothy'}),
(other:Person)
WHERE other.name IN ['Andy', 'Peter'] AND (other)-->(timothy)
RETURN other.name, other.age
The name and age for nodes that have an outgoing relationship to the 'Timothy' node are returned.
other.name other.age
"Andy" 36
Rows: 1
Query
MATCH
(person:Person),
(peter:Person {name: 'Peter'})
WHERE NOT (person)-->(peter)
RETURN person.name, person.age
Name and age values for nodes that do not have an outgoing relationship to the 'Peter' node are returned.
person.name person.age
"Timothy" 25
"Peter" 35
132
person.name person.age
Rows: 2
Query
MATCH (n:Person)
WHERE (n)-[:KNOWS]-({name: 'Timothy'})
RETURN n.name, n.age
Finds all name and age values for nodes that have a KNOWS relationship to a node with the name 'Timothy'.
n.name n.age
"Andy" 36
Rows: 1
Query
MATCH (n:Person)-[r]->()
WHERE n.name='Andy' AND type(r) =~ 'K.*'
RETURN type(r), r.since
This returns all relationships having a type whose name starts with 'K'.
type(r) r.since
"KNOWS" 1999
"KNOWS" 2012
Rows: 2
An existential subquery can be used to find out if a specified pattern exists at least once in the data. It can
be used in the same way as a path pattern but it allows you to use MATCH and WHERE clauses internally. A
subquery has a scope, as indicated by the opening and closing braces, { and }. Any variable that is defined
in the outside scope can be referenced inside the subquery’s own scope. Variables introduced inside the
subquery are not part of the outside scope and therefore can’t be accessed on the outside. If the subquery
evaluates even once to anything that is not null, the whole expression will become true. This also means
133
that the system only needs to calculate the first occurrence where the subquery evaluates to something
that is not null and can skip the rest of the work.
Syntax:
EXISTS {
MATCH [Pattern]
WHERE [Expression]
}
It is worth noting that the MATCH keyword can be omitted in subqueries and that the WHERE clause is
optional.
Query
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(:Dog)
}
RETURN person.name AS name
name
"Andy"
"Peter"
Rows: 2
Query
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(dog:Dog)
WHERE person.name = dog.name
}
RETURN person.name AS name
134
name
"Andy"
Rows: 1
Query
MATCH (person:Person)
WHERE EXISTS {
MATCH (person)-[:HAS_DOG]->(dog:Dog)
WHERE EXISTS {
MATCH (dog)-[:HAS_TOY]->(toy:Toy)
WHERE toy.name = 'Banana'
}
}
RETURN person.name AS name
name
"Peter"
Rows: 1
3.6.7. Lists
IN operator
To check if an element exists in a list, you can use the IN operator.
Query
MATCH (a:Person)
WHERE a.name IN ['Peter', 'Timothy']
RETURN a.name, a.age
a.name a.age
"Timothy" 25
"Peter" 35
Rows: 2
135
3.6.8. Missing properties and values
Query
MATCH (n:Person)
WHERE n.belt = 'white'
RETURN n.name, n.age, n.belt
Only the name, age and belt values of nodes with white belts are returned.
"Andy" 36 "white"
Rows: 1
Query
MATCH (n:Person)
WHERE n.belt = 'white' OR n.belt IS NULL
RETURN n.name, n.age, n.belt
ORDER BY n.name
This returns all values for all nodes, even those without the belt property.
"Andy" 36 "white"
"Peter" 35 <null>
"Timothy" 25 <null>
Rows: 3
Filter on null
Sometimes you might want to test if a value or a variable is null. This is done just like SQL does it, using
IS NULL. Also like SQL, the negative is IS NOT NULL, although NOT(IS NULL x) also works.
136
Query
MATCH (person:Person)
WHERE person.name = 'Peter' AND person.belt IS NULL
RETURN person.name, person.age, person.belt
The name and age values for nodes that have name 'Peter' but no belt property are returned.
"Peter" 35 <null>
Rows: 1
Simple range
To check for an element being inside a specific range, use the inequality operators <, <=, >=, >.
Query
MATCH (a:Person)
WHERE a.name >= 'Peter'
RETURN a.name, a.age
The name and age values of nodes having a name property lexicographically greater than or equal to
'Peter' are returned.
a.name a.age
"Timothy" 25
"Peter" 35
Rows: 2
Composite range
Several inequalities can be used to construct a range.
Query
MATCH (a:Person)
WHERE a.name > 'Andy' AND a.name < 'Timothy'
RETURN a.name, a.age
The name and age values of nodes having a name property lexicographically between 'Andy' and
'Timothy' are returned.
137
a.name a.age
"Peter" 35
Rows: 1
3.7. ORDER BY
ORDER BY is a sub-clause following RETURN or WITH, and it specifies that the output should be
sorted and how.
• Introduction
• Ordering null
3.7.1. Introduction
Note that you cannot sort on nodes or relationships, just on properties on these. ORDER BY relies on
comparisons to sort the output, see Ordering and comparison of values.
In terms of scope of variables, ORDER BY follows special rules, depending on if the projecting RETURN or WITH
clause is either aggregating or DISTINCT. If it is an aggregating or DISTINCT projection, only the variables
available in the projection are available. If the projection does not alter the output cardinality (which
aggregation and DISTINCT do), variables available from before the projecting clause are also available.
When the projection clause shadows already existing variables, only the new variables are available.
Lastly, it is not allowed to use aggregating expressions in the ORDER BY sub-clause if they are not also
listed in the projecting clause. This last rule is to make sure that ORDER BY does not change the results, only
the order of them.
The performance of Cypher queries using ORDER BY on node properties can be influenced by the existence
and use of an index for finding the nodes. If the index can provide the nodes in the order requested in the
query, Cypher can avoid the use of an expensive Sort operation. Read more about this capability in The
use of indexes.
138
Graph
N0 [
label = "name = \'A\'\llength = 170\lage = 34\l"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'B\'\lage = 36\l"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N2 [
label = "name = \'C\'\llength = 185\lage = 32\l"
]
Query
MATCH (n)
RETURN n.name, n.age
ORDER BY n.name
n.name n.age
"A" 34
"B" 36
"C" 32
Rows: 3
139
Query
MATCH (n)
RETURN n.name, n.age
ORDER BY n.age, n.name
This returns the nodes, sorted first by their age, and then by their name.
n.name n.age
"C" 32
"A" 34
"B" 36
Rows: 3
Query
MATCH (n)
RETURN n.name, n.age
ORDER BY n.name DESC
The example returns the nodes, sorted by their name in reverse order.
n.name n.age
"C" 32
"B" 36
"A" 34
Rows: 3
Query
MATCH (n)
RETURN n.length, n.name, n.age
ORDER BY n.length
The nodes are returned sorted by the length property, with a node without that property last.
140
n.length n.name n.age
170 "A" 34
185 "C" 32
<null> "B" 36
Rows: 3
Query
MATCH (n)
WITH n ORDER BY n.age
RETURN collect(n.name) AS names
The list of names built from the collect aggregating function contains the names in order of the age
property.
names
["C","A","B"]
Rows: 1
3.8. SKIP
SKIP defines from which row to start including the rows in the output.
• Introduction
3.8.1. Introduction
By using SKIP, the result set will get trimmed from the top. Please note that no guarantees are made on
the order of the result unless the query specifies the ORDER BY clause. SKIP accepts any expression that
evaluates to a positive integer — however the expression cannot refer to nodes or relationships.
141
Graph
N0 [
label = "name = \'A\'\l"
]
N0 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'B\'\l"
]
N2 [
label = "name = \'C\'\l"
]
N3 [
label = "name = \'D\'\l"
]
N4 [
label = "name = \'E\'\l"
]
Query
MATCH (n)
RETURN n.name
ORDER BY n.name
SKIP 3
The first three nodes are skipped, and only the last two are returned in the result.
n.name
"D"
"E"
Rows: 2
142
Query
MATCH (n)
RETURN n.name
ORDER BY n.name
SKIP 1
LIMIT 2
n.name
"B"
"C"
Rows: 2
Query
MATCH (n)
RETURN n.name
ORDER BY n.name
SKIP 1 + toInteger(3*rand())
n.name
"C"
"D"
"E"
Rows: 3
3.9. LIMIT
LIMIT constrains the number of returned rows.
• Introduction
143
3.9.1. Introduction
LIMIT accepts any expression that evaluates to a positive integer — however the expression cannot refer
to nodes or relationships.
Graph
N0 [
label = "name = \'A\'\l"
]
N0 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'B\'\l"
]
N2 [
label = "name = \'C\'\l"
]
N3 [
label = "name = \'D\'\l"
]
N4 [
label = "name = \'E\'\l"
]
Query
MATCH (n)
RETURN n.name
ORDER BY n.name
LIMIT 3
n.name
"A"
"B"
"C"
144
n.name
Rows: 3
Query
MATCH (n)
RETURN n.name
ORDER BY n.name
LIMIT 1 + toInteger(3 * rand())
n.name
"A"
"B"
Rows: 2
3.10. CREATE
The CREATE clause is used to create nodes and relationships.
• Create nodes
◦ Create single node
• Create relationships
◦ Create a relationship between two nodes
145
In the CREATE clause, patterns are used extensively. Read Patterns for an introduction.
Query
CREATE (n)
Rows: 0
Nodes created: 1
Query
Rows: 0
Nodes created: 2
Query
CREATE (n:Person)
Rows: 0
Nodes created: 1
Labels added: 1
146
Create a node with multiple labels
To add labels when creating a node, use the syntax below. In this case, we add two labels.
Query
CREATE (n:Person:Swedish)
Rows: 0
Nodes created: 1
Labels added: 2
Query
Rows: 0
Nodes created: 1
Properties set: 2
Labels added: 1
Query
a.name
"Andy"
Rows: 1
Nodes created: 1
Properties set: 1
147
3.10.2. Create relationships
Query
MATCH
(a:Person),
(b:Person)
WHERE a.name = 'A' AND b.name = 'B'
CREATE (a)-[r:RELTYPE]->(b)
RETURN type(r)
type(r)
"RELTYPE"
Rows: 1
Relationships created: 1
Query
MATCH
(a:Person),
(b:Person)
WHERE a.name = 'A' AND b.name = 'B'
CREATE (a)-[r:RELTYPE {name: a.name + '<->' + b.name}]->(b)
RETURN type(r), r.name
The type and name of the newly-created relationship is returned by the example query.
type(r) r.name
"RELTYPE" "A<->B"
Rows: 1
Relationships created: 1
Properties set: 1
148
be created.
Query
This query creates three nodes and two relationships in one go, assigns it to a path variable, and returns it.
(2)-[WORKS_AT,0]->(3)<-[WORKS_AT,1]-(4)
Rows: 1
Nodes created: 3
Relationships created: 2
Properties set: 2
Parameters
{
"props" : {
"name" : "Andy",
"position" : "Developer"
}
}
Query
Node[2]{name:"Andy",position:"Developer"}
Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1
149
Parameters
{
"props" : [ {
"name" : "Andy",
"position" : "Developer"
}, {
"name" : "Michael",
"position" : "Developer"
} ]
}
Query
Rows: 0
Nodes created: 2
Properties set: 4
3.11. DELETE
The DELETE clause is used to delete nodes, relationships or paths.
• Introduction
3.11.1. Introduction
For removing properties and labels, see REMOVE. Remember that you cannot delete a node without also
deleting relationships that start or end on said node. Either explicitly delete the relationships, or use DETACH
DELETE.
150
Graph
N0 [
label = "{Person|name = \'Andy\'\lage = 36\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "{Person|name = \'Timothy\'\lage = 25\l}"
]
N2 [
label = "{Person|name = \'Peter\'\lage = 34\l}"
]
N3 [
label = "{Person|name = \'UNKNOWN\'\l}"
]
Query
Rows: 0
Nodes deleted: 1
Query
MATCH (n)
DETACH DELETE n
Rows: 0
Nodes deleted: 4
Relationships deleted: 2
151
3.11.4. Delete a node with all its relationships
When you want to delete a node and any relationship going to or from it, use DETACH DELETE.
Query
Rows: 0
Nodes deleted: 1
Relationships deleted: 2
For DETACH DELETE for users with restricted security privileges, see Operations Manual →
Fine-grained access control.
Query
This deletes all outgoing KNOWS relationships from the node with the name 'Andy'.
Rows: 0
Relationships deleted: 2
3.12. SET
The SET clause is used to update labels on nodes and properties on nodes and
relationships.
• Introduction
• Set a property
• Update a property
• Remove a property
152
• Replace all properties using a map and =
3.12.1. Introduction
SET can be used with a map — provided as a literal, a parameter, or a node or relationship — to set
properties.
made to set a label on a node that already has that label. The query statistics will state
whether any updates actually took place.
Graph
N0 [
label = "{Swedish|hungry = true\lname = \'Andy\'\lage = 36\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'Stefan\'\l"
]
N1 -> N0 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N2 [
label = "name = \'Peter\'\lage = 34\l"
]
N3 [
label = "name = \'George\'\l"
]
N3 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
153
Query
n.name n.surname
"Andy" "Taylor"
Rows: 1
Properties set: 1
It is possible to set a property on a node or relationship using more complex expressions. For instance, in
contrast to specifying the node directly, the following query shows how to set a property for a node
selected by an expression:
Query
n.name n.worksIn
"Andy" "Malmo"
Rows: 1
Properties set: 1
No action will be taken if the node expression evaluates to null, as shown in this example:
Query
As no node matches the CASE expression, the expression returns a null. As a consequence, no updates
occur, and therefore no worksIn property is set.
n.name n.worksIn
"Andy" <null>
Rows: 1
154
3.12.3. Update a property
SET can be used to update a property on a node or relationship. This query forces a change of type in the
age property:
Query
n.name n.age
"Andy" "36"
Rows: 1
Properties set: 1
Query
n.name n.age
<null> 36
Rows: 1
Properties set: 1
Query
MATCH
(at {name: 'Andy'}),
(pn {name: 'Peter'})
SET at = pn
RETURN at.name, at.age, at.hungry, pn.name, pn.age
155
The 'Andy' node has had all its properties replaced by the properties of the 'Peter' node.
Rows: 1
Properties set: 3
Query
This query updated the name property from Peter to Peter Smith, deleted the age property, and added the
position property to the 'Peter' node.
Rows: 1
Properties set: 3
Query
This query removed all the existing properties — namely, name and age — from the 'Peter' node.
p.name p.age
<null> <null>
Rows: 1
Properties set: 2
156
3.12.8. Mutate specific properties using a map and +=
The property mutation operator += can be used with SET to mutate properties from a map in a fine-grained
fashion:
• Any properties in the map that are not on the node or relationship will be added.
• Any properties not in the map that are on the node or relationship will be left as is.
• Any properties that are in both the map and the node or relationship will be replaced in the node or
relationship. However, if any property in the map is null, it will be removed from the node or
relationship.
Query
This query left the name property unchanged, updated the age property from 34 to 38, and added the hungry
and position properties to the 'Peter' node.
Rows: 1
Properties set: 3
In contrast to the property replacement operator =, providing an empty map as the right operand to += will
not remove any existing properties from a node or relationship. In line with the semantics detailed above,
passing in an empty map with += will have no effect:
Query
p.name p.age
"Peter" 34
Rows: 1
157
Query
Rows: 0
Properties set: 2
Parameters
{
"surname" : "Taylor"
}
Query
n.name n.surname
"Andy" "Taylor"
Rows: 1
Properties set: 1
Parameters
{
"props" : {
"name" : "Andy",
"position" : "Developer"
}
}
Query
158
The 'Andy' node has had all its properties replaced by the properties in the props parameter.
Rows: 1
Properties set: 4
Query
n.name labels
"Stefan" ["German"]
Rows: 1
Labels added: 1
Query
n.name labels
"George" ["Swedish","Bossman"]
Rows: 1
Labels added: 2
159
3.13. REMOVE
The REMOVE clause is used to remove properties from nodes and relationships, and to
remove labels from nodes.
• Introduction
• Remove a property
3.13.1. Introduction
For deleting nodes and relationships, see DELETE.
Removing labels from a node is an idempotent operation: if you try to remove a label
from a node that does not have that label on it, nothing happens. The query statistics will
tell you if something needed to be done or not.
Graph
N0 [
label = "{Swedish|name = \'Andy\'\lage = 36\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "{Swedish|name = \'Timothy\'\lage = 25\l}"
]
N2 [
label = "{Swedish, German|name = \'Peter\'\lage = 34\l}"
]
160
Query
a.name a.age
"Andy" <null>
Rows: 1
Properties set: 1
Query
n.name labels(n)
"Peter" ["Swedish"]
Rows: 1
Labels removed: 1
Query
n.name labels(n)
"Peter" []
161
n.name labels(n)
Rows: 1
Labels removed: 2
3.14. FOREACH
The FOREACH clause is used to update data within a collection whether components of a
path, or result of aggregation.
3.14.1. Introduction
Lists and paths are key concepts in Cypher. The FOREACH clause can be used to update data, such as
executing update commands on elements in a path, or on a list created by aggregation.
The variable context within the FOREACH parenthesis is separate from the one outside it. This means that if
you CREATE a node variable within a FOREACH, you will not be able to use it outside of the foreach statement,
unless you match to find it.
Within the FOREACH parentheses, you can do any of the updating commands — SET, REMOVE, CREATE, MERGE,
DELETE, and FOREACH.
If you want to execute an additional MATCH for each element in a list then the UNWIND clause would be a
more appropriate command.
Graph
N0 [
label = "{Person|name = \'A\'\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "{Person|name = \'B\'\l}"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N2 [
label = "{Person|name = \'C\'\l}"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "{Person|name = \'D\'\l}"
]
162
3.14.2. Mark all nodes along a path
This query will set the property marked to true on all nodes along a path.
Query
MATCH p=(start)-[*]->(finish)
WHERE start.name = 'A' AND finish.name = 'D'
FOREACH (n IN nodes(p) | SET n.marked = true)
Rows: 0
Properties set: 4
3.15. MERGE
The MERGE clause ensures that a pattern exists in the graph. Either the pattern already
exists, or it needs to be created.
• Introduction
• Merge nodes
◦ Merge single node with a label
• Merge relationships
◦ Merge on a relationship
◦ Merge on a relationship between an existing node and a merged node derived from a node
property
163
◦ Merge using unique constraints matches an existing node
3.15.1. Introduction
MERGE either matches existing nodes and binds them, or it creates new data and binds that. It’s like a
combination of MATCH and CREATE that additionally allows you to specify what happens if the data was
matched or created.
For example, you can specify that the graph must contain a node for a user with a certain name. If there
isn’t a node with the correct name, a new node will be created and its name property set.
For performance reasons, creating a schema index on the label or property is highly
recommended when using MERGE. See Indexes for search performance for more
information.
When using MERGE on full patterns, the behavior is that either the whole pattern matches, or the whole
pattern is created. MERGE will not partially use existing patterns — it is all or nothing. If partial matches are
needed, this can be accomplished by splitting a pattern up into multiple MERGE clauses.
As with MATCH, MERGE can match multiple occurrences of a pattern. If there are multiple matches, they will
all be passed on to later stages of the query.
The last part of MERGE is the ON CREATE and ON MATCH. These allow a query to express additional changes to
the properties of a node or relationship, depending on if the element was matched (MATCH) in the database
or if it was created (CREATE).
164
Graph
N0 [
label = "{Person|bornIn = \'New York\'\lname = \'Charlie Sheen\'\lchauffeurName = \'John Brown\'\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "FATHER\n"
]
N0 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N1 [
label = "{Person|bornIn = \'Ohio\'\lname = \'Martin Sheen\'\lchauffeurName = \'Bob Brown\'\l}"
]
N1 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N1 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N2 [
label = "{Person|chauffeurName = \'John Brown\'\lbornIn = \'New Jersey\'\lname = \'Michael
Douglas\'\l}"
]
N2 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N2 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N3 [
label = "{Person|bornIn = \'New York\'\lname = \'Oliver Stone\'\lchauffeurName = \'Bill White\'\l}"
]
N3 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N4 [
label = "{Person|bornIn = \'New York\'\lname = \'Rob Reiner\'\lchauffeurName = \'Ted Green\'\l}"
]
N4 -> N6 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "ACTED_IN\n"
]
N5 [
label = "{Movie|title = \'Wall Street\'\l}"
]
N6 [
label = "{Movie|title = \'The American President\'\l}"
]
165
Query
MERGE (robert:Critic)
RETURN robert, labels(robert)
A new node is created because there are no nodes labeled Critic in the database.
robert labels(robert)
Node[7]{} ["Critic"]
Rows: 1
Nodes created: 1
Labels added: 1
Query
A new node with the name 'Charlie Sheen' will be created since not all properties matched the existing
'Charlie Sheen' node.
charlie
Node[7]{name:"Charlie Sheen",age:10}
Rows: 1
Nodes created: 1
Properties set: 2
Query
'Michael Douglas' will be matched and the name and bornIn properties returned.
michael.name michael.bornIn
166
michael.name michael.bornIn
Rows: 1
As mentioned previously, MERGE queries can greatly benefit from schema indexes. In this example, the
following would significantly improve the performance of the MERGE clause:
Query
MATCH (person:Person)
MERGE (city:City {name: person.bornIn})
RETURN person.name, person.bornIn, city
Three nodes labeled City are created, each of which contains a name property with the value of 'New York',
'Ohio', and 'New Jersey', respectively. Note that even though the MATCH clause results in three bound nodes
having the value 'New York' for the bornIn property, only a single 'New York' node (i.e. a City node with a
name of 'New York') is created. As the 'New York' node is not matched for the first bound node, it is
created. However, the newly-created 'New York' node is matched and bound for the second and third
bound nodes.
Rows: 5
Nodes created: 3
Properties set: 3
Labels added: 3
167
Query
The query creates the 'keanu' node and sets a timestamp on creation time.
keanu.name keanu.created
Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1
Query
MERGE (person:Person)
ON MATCH
SET person.found = true
RETURN person.name, person.found
The query finds all the Person nodes, sets a property on them, and returns them.
person.name person.found
Rows: 5
Properties set: 5
168
The query creates the 'keanu' node, and sets a timestamp on creation time. If 'keanu' had already existed,
a different property would have been set.
Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1
Query
MERGE (person:Person)
ON MATCH
SET
person.found = true,
person.lastAccessed = timestamp()
RETURN person.name, person.found, person.lastAccessed
Rows: 5
Properties set: 10
Merge on a relationship
MERGE can be used to match or create a relationship.
Query
MATCH
(charlie:Person {name: 'Charlie Sheen'}),
(wallStreet:Movie {title: 'Wall Street'})
MERGE (charlie)-[r:ACTED_IN]->(wallStreet)
RETURN charlie.name, type(r), wallStreet.title
'Charlie Sheen' had already been marked as acting in 'Wall Street', so the existing relationship is found
169
and returned. Note that in order to match or create a relationship when using MERGE, at least one bound
node must be specified, which is done via the MATCH clause in the above example.
Rows: 1
MATCH
(oliver:Person {name: 'Oliver Stone'}),
(reiner:Person {name: 'Rob Reiner'})
MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:ACTED_IN]-(reiner)
RETURN movie
In our example graph, 'Oliver Stone' and 'Rob Reiner' have never worked together. When we try to MERGE
a "movie between them, Neo4j will not use any of the existing movies already connected to either person.
Instead, a new 'movie' node is created.
movie
Node[7]{}
Rows: 1
Nodes created: 1
Relationships created: 2
Labels added: 1
Query
MATCH
(charlie:Person {name: 'Charlie Sheen'}),
(oliver:Person {name: 'Oliver Stone'})
MERGE (charlie)-[r:KNOWS]-(oliver)
RETURN r
As 'Charlie Sheen' and 'Oliver Stone' do not know each other this MERGE query will create a KNOWS
relationship between them. The direction of the created relationship is arbitrary.
:KNOWS[8]{}
170
r
Rows: 1
Relationships created: 1
Query
MATCH (person:Person)
MERGE (city:City {name: person.bornIn})
MERGE (person)-[r:BORN_IN]->(city)
RETURN person.name, person.bornIn, city
This builds on the example from Merge single node derived from an existing node property. The second
MERGE creates a BORN_IN relationship between each person and a city corresponding to the value of the
person’s bornIn property. 'Charlie Sheen', 'Rob Reiner' and 'Oliver Stone' all have a BORN_IN relationship to
the 'same' City node ('New York').
Rows: 5
Nodes created: 3
Relationships created: 5
Properties set: 3
Labels added: 3
Query
MATCH (person:Person)
MERGE (person)-[r:HAS_CHAUFFEUR]->(chauffeur:Chauffeur {name: person.chauffeurName})
RETURN person.name, person.chauffeurName, chauffeur
As MERGE found no matches — in our example graph, there are no nodes labeled with Chauffeur and no
171
HAS_CHAUFFEUR relationships — MERGE creates five nodes labeled with Chauffeur, each of which contains a
name property whose value corresponds to each matched Person node’s chauffeurName property value.
MERGE also creates a HAS_CHAUFFEUR relationship between each Person node and the newly-created
corresponding Chauffeur node. As 'Charlie Sheen' and 'Michael Douglas' both have a chauffeur with the
same name — 'John Brown' — a new node is created in each case, resulting in 'two' Chauffeur nodes
having a name of 'John Brown', correctly denoting the fact that even though the name property may be
identical, these are two separate people. This is in contrast to the example shown above in Merge on a
relationship between two existing nodes, where we used the first MERGE to bind the City nodes to prevent
them from being recreated (and thus duplicated) in the second MERGE.
Rows: 5
Nodes created: 5
Relationships created: 5
Properties set: 5
Labels added: 5
For example, given two unique constraints on :Person(id) and :Person(ssn), a query such as MERGE
(n:Person {id: 12, ssn: 437}) will fail, if there are two different nodes (one with id 12 and one with ssn
437) or if there is only one node with only one of the properties. In other words, there must be exactly one
node that matches the pattern, or no matching nodes.
Note that the following examples assume the existence of unique constraints that have been created
using:
Query
172
The query creates the 'laurence' node. If 'laurence' had already existed, MERGE would just match the
existing node.
laurence.name
"Laurence Fishburne"
Rows: 1
Nodes created: 1
Properties set: 1
Labels added: 1
Query
oliver.name oliver.bornIn
Rows: 1
Query
While there is a matching unique 'michael' node with the name 'Michael Douglas', there is no unique node
with the role of 'Gordon Gekko' and MERGE fails to match.
Error message
Merge did not find a matching node michael and can not create a new node due to
conflicts with existing unique nodes
If we want to give Michael Douglas the role of Gordon Gekko, we can use the SET clause instead:
173
Query
Query
While there is a matching unique 'oliver' node with the name 'Oliver Stone', there is also another unique
node with the role of 'Gordon Gekko' and MERGE fails to match.
Error message
Merge did not find a matching node oliver and can not create a new node due to
conflicts with existing unique nodes
Parameters
{
"param" : {
"name" : "Keanu Reeves",
"role" : "Neo"
}
}
Query
person.name person.role
Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1
174
3.16. CALL {} (subquery)
The CALL {} clause evaluates a subquery that returns some values.
• Introduction
• Post-union processing
3.16.1. Introduction
CALL allows to execute subqueries, i.e. queries inside of other queries. Subqueries allow you to compose
queries, which is especially useful when working with UNION or aggregations.
The CALL clause is also used for calling procedures. For descriptions of the CALL clause in
this context, refer to CALL procedure.
A subquery is evaluated for each incoming input row and may produce an arbitrary number of output
rows. Every output row is then combined with the input row to build the result of the subquery. That
means that a subquery will influence the number of rows. If the subquery does not return any rows, there
will be no rows available after the subquery.
There are restrictions on what queries are allowed as subqueries and how they interact with the enclosing
query:
• A subquery can only refer to variables from the enclosing query if they are explicitly imported.
• A subquery cannot return variables with the same names as variables in the enclosing query.
• All variables that are returned from a subquery are afterwards available in the enclosing query.
175
Graph
N0 [
label = "{Person, Child|name = \'Alice\'\lage = 20\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "CHILD_OF\n"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "FRIEND_OF\n"
]
N1 [
label = "{Person|name = \'Bob\'\lage = 27\l}"
]
N2 [
label = "{Person, Parent|name = \'Charlie\'\lage = 65\l}"
]
N3 [
label = "{Person|name = \'Dora\'\lage = 30\l}"
]
Query
UNWIND [0, 1, 2] AS x
CALL {
WITH x
RETURN x * 10 AS y
}
RETURN x, y
x y
0 0
1 10
2 20
Rows: 3
• Consist only of simple references to outside variables - e.g. WITH x, y, z. Aliasing or expressions are
not supported in importing WITH clauses - e.g. WITH a AS b or WITH a+1 AS b.
• Be the first clause of a subquery (or the second clause, if directly following a USE clause).
176
youngest and the oldest person in the database and orders them by name.
Query
CALL {
MATCH (p:Person)
RETURN p
ORDER BY p.age ASC
LIMIT 1
UNION
MATCH (p:Person)
RETURN p
ORDER BY p.age DESC
LIMIT 1
}
RETURN p.name, p.age
ORDER BY p.name
p.name p.age
"Alice" 20
"Charlie" 65
Rows: 2
If different parts of a result should be matched differently, with some aggregation over the whole results,
subqueries need to be used. This example query finds friends and/or parents for each person.
Subsequently the number of friends and parents are counted together.
Query
MATCH (p:Person)
CALL {
WITH p
OPTIONAL MATCH (p)-[:FRIEND_OF]->(other:Person)
RETURN other
UNION
WITH p
OPTIONAL MATCH (p)-[:CHILD_OF]->(other:Parent)
RETURN other
}
RETURN DISTINCT p.name, count(other)
p.name count(other)
"Alice" 2
"Bob" 0
"Charlie" 0
"Dora" 0
Rows: 4
177
creates five Clone nodes for each existing person. The aggregation ensures that cardinality is not changed
by the subquery. Without this, the result would be five times as many rows.
Query
MATCH (p:Person)
CALL {
UNWIND range(1, 5) AS i
CREATE (c:Clone)
RETURN count(c) AS numberOfClones
}
RETURN p.name, numberOfClones
p.name numberOfClones
"Alice" 5
"Bob" 5
"Charlie" 5
"Dora" 5
Rows: 4
Nodes created: 20
Labels added: 20
Query
MATCH (p:Person)
CALL {
WITH p
MATCH (other:Person)
WHERE other.age < p.age
RETURN count(other) AS youngerPersonsCount
}
RETURN p.name, youngerPersonsCount
p.name youngerPersonsCount
"Alice" 0
"Bob" 1
"Charlie" 3
"Dora" 2
Rows: 4
178
3.17. CALL procedure
The CALL clause is used to call a procedure deployed in the database.
3.17.1. Introduction
Procedures are called using the CALL clause.
The CALL clause is also used to evaluate a subquery. For descriptions of the CALL clause
in this context, refer to CALL {} (subquery).
Each procedure call needs to specify all required procedure arguments. This may be done either explicitly,
by using a comma-separated list wrapped in parentheses after the procedure name, or implicitly by using
available query parameters as procedure call arguments. The latter form is available only in a so-called
standalone procedure call, when the whole query consists of a single CALL clause.
Most procedures return a stream of records with a fixed set of result fields, similar to how running a
Cypher query returns a stream of records. The YIELD sub-clause is used to explicitly select which of the
available result fields are returned as newly-bound variables from the procedure call to the user or for
further processing by the remaining query. Thus, in order to be able to use YIELD, the names (and types) of
the output parameters need be known in advance. Each yielded result field may optionally be renamed
using aliasing (i.e., resultFieldName AS newName). All new variables bound by a procedure call are added to
the set of variables already bound in the current scope. It is an error if a procedure call tries to rebind a
previously bound variable (i.e., a procedure call cannot shadow a variable that was previously bound in the
current scope).
For more information on how to determine the input parameters for the CALL procedure and the output
parameters for the YIELD procedure, see View the signature for a procedure.
Inside a larger query, the records returned from a procedure call with an explicit YIELD may be further
filtered using a WHERE sub-clause followed by a predicate (similar to WITH ... WHERE ...).
If the called procedure declares at least one result field, YIELD may generally not be omitted. However
YIELD may always be omitted in a standalone procedure call. In this case, all result fields are yielded as
newly-bound variables from the procedure call to the user.
Neo4j supports the notion of VOID procedures. A VOID procedure is a procedure that does not declare any
result fields and returns no result records and that has explicitly been declared as VOID. Calling a VOID
procedure may only have a side effect and thus does neither allow nor require the use of YIELD. Calling a
VOID procedure in the middle of a larger query will simply pass on each input record (i.e., it acts like WITH *
in terms of the record stream).
Neo4j comes with a number of built-in procedures. For a list of these, see Operations
Manual → Procedures.
Users can also develop custom procedures and deploy to the database. See Java
Reference → Procedures and functions for details.
179
3.17.2. Call a procedure using CALL
This calls the built-in procedure db.labels, which lists all labels used in the database.
Query
CALL db.labels()
label
"User"
"Administrator"
Rows: 2
Query
CALL db.labels
label
"User"
"Administrator"
Rows: 2
Query
We can see that the dbms.listConfig has one input parameter, searchString, and three output
parameters, name, description and value.
180
signature
Rows: 1
Query
CALL `db`.`labels()`
Query
CALL `db`.`labels`
Query
Since our example procedure does not return any result, the result is empty.
Examples that use parameter arguments shows the given parameters in JSON format;
the exact manner in which they are to be submitted depends upon the driver being used.
See Parameters, for more about querying with parameters
Parameters
{
"username" : "example_username",
"password" : "example_password",
"requirePasswordChange" : false
}
Query
181
Since our example procedure does not return any result, the result is empty.
Cypher allows the omission of parentheses for procedures with arity-n (n arguments), Cypher implicitly
passes the parameter arguments.
only in a so-called standalone procedure call, when the whole query consists of a single
CALL clause.
Parameters
{
"username" : "example_username",
"password" : "example_password",
"requirePasswordChange" : false
}
Query
CALL dbms.security.createUser
Since our example procedure does not return any result, the result is empty.
Parameters
{
"password" : "example_password"
}
Query
Since our example procedure does not return any result, the result is empty.
Query
Since our example procedure does not return any result, the result is empty.
182
3.17.9. Call a procedure within a complex query using CALL YIELD
This calls the built-in procedure db.labels to count all labels used in the database.
Query
Since the procedure call is part of a larger query, all outputs must be named explicitly.
Query
Since the procedure call is part of a larger query, all outputs must be named explicitly.
Query
Since the procedure call is part of a larger query, all outputs must be named explicitly.
3.18. UNION
The UNION clause is used to combine the result of multiple queries.
• Introduction
183
3.18.1. Introduction
UNION combines the results of two or more queries into a single result set that includes all the rows that
belong to all queries in the union.
The number and the names of the columns must be identical in all queries combined by using UNION.
To keep all the result rows, use UNION ALL. Using just UNION will combine and remove duplicates from the
result set.
Graph
N0 [
label = "{Actor|name = \'Anthony Hopkins\'\l}"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTS_IN\n"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N1 [
label = "{Actor|name = \'Helen Mirren\'\l}"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "ACTS_IN\n"
]
N2 [
label = "{Actor|name = \'Hitchcock\'\l}"
]
N3 [
label = "{Movie|title = \'Hitchcock\'\l}"
]
Query
MATCH (n:Actor)
RETURN n.name AS name
UNION ALL
MATCH (n:Movie)
RETURN n.title AS name
name
"Anthony Hopkins"
"Helen Mirren"
"Hitchcock"
184
name
"Hitchcock"
Rows: 4
Query
MATCH (n:Actor)
RETURN n.name AS name
UNION
MATCH (n:Movie)
RETURN n.title AS name
name
"Anthony Hopkins"
"Helen Mirren"
"Hitchcock"
Rows: 3
3.19. USE
The USE clause determines which graph a query, or query part, is executed against.
• Introduction
• Syntax
• Examples
◦ Query remote graph by name
3.19.1. Introduction
The USE clause determines which graph a query, or query part, is executed against. It is supported for
queries and schema commands.
The USE clause can not be used together with the PERIODIC COMMIT clause.
185
3.19.2. Syntax
The USE clause can only appear as the prefix of schema commands, or as the first clause of queries:
USE <graph>
<other clauses>
Fabric syntax
When running queries against a Fabric database, the USE clause can also appear as the first clause of:
• Union parts:
USE <graph>
<other clauses>
UNION
USE <graph>
<other clauses>
• Subqueries:
CALL {
USE <graph>
<other clauses>
}
In subqueries, a USE clause may appear as the second clause, if directly following an importing WITH
clause
When executing queries against a Fabric database, in addition to referring to databases in the DBMS, the
<graph> may also refer to a graph mounted through the Fabric configuration. For more information, see
Operations Manual → Fabric.
3.19.3. Examples
Query.
USE myDatabase
MATCH (n) RETURN n
186
Query.
USE exampleFabricSetup.exampleDatabaseName
MATCH (n) RETURN n
The graph we wish to query is configured with the graph id 0, which is why we can refer to it using the
built-in function graph() with the argument 0:
Query.
USE exampleFabricSetup.graph(0)
MATCH (n) RETURN n
• Introduction
3.20.1. Introduction
• The URL of the CSV file is specified by using FROM followed by an arbitrary expression evaluating to the
URL in question.
• CSV files can be stored on the database server and are then accessible using a file:/// URL.
Alternatively, LOAD CSV also supports accessing CSV files via HTTPS, HTTP, and FTP.
• LOAD CSV supports resources compressed with gzip and Deflate. Additionally LOAD CSV supports locally
stored CSV files compressed with ZIP.
• LOAD CSV will follow HTTP redirects but for security reasons it will not follow redirects that changes the
protocol, for example if the redirect is going from HTTPS to HTTP.
187
• LOAD CSV is often used in conjunction with the query hint PERIODIC COMMIT; more information on this
may be found in PERIODIC COMMIT query hint.
dbms.directories.import
Sets the root directory for file:/// URLs used with the Cypher LOAD CSV clause. This should be set to a
single directory relative to the Neo4j installation path on the database server. All requests to load from
file:/// URLs will then be relative to the specified directory. The default value set in the config
settings is import. This is a security measure which prevents the database from accessing files outside
the standard import directory, similar to how a Unix chroot operates. Setting this to an empty field will
allow access to all files within the Neo4j installation folder. Commenting out this setting will disable the
security feature, allowing all files in the local system to be imported. This is definitely not recommended.
File URLs will be resolved relative to the dbms.directories.import directory. For example, a file URL will
typically look like file:///myfile.csv or file:///myproject/myfile.csv.
• If dbms.directories.import is set to the default value import, using the above URLs in LOAD CSV would
read from <NEO4J_HOME>/import/myfile.csv and <NEO4J_HOME>/import/myproject/myfile.csv
respectively.
• If it is set to /data/csv, using the above URLs in LOAD CSV would read from
<NEO4J_HOME>/data/csv/myfile.csv and <NEO4J_HOME>/data/csv/myproject/myfile.csv respectively.
• the end line termination is system dependent, e.g., it is \n on unix or \r\n on windows;
• the field terminator character can be change by using the option FIELDTERMINATOR available in the LOAD
CSV command;
• quoted strings are allowed in the CSV file and the quotes are dropped when reading the data;
• a double quote must be in a quoted string and escaped, either with the escape character or a second
double quote.
188
3.20.3. Import data from a CSV file
To import data from a CSV file into Neo4j, you can use LOAD CSV to get the data into your query. Then you
write it to your database using the normal updating clauses of Cypher.
artists.csv
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
Query
A new node with the Artist label is created for each row in the CSV file. In addition, two columns from the
CSV file are set as properties on the nodes.
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
none
artists-with-headers.csv
Id,Name,Year
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
Query
This time, the file starts with a single row containing column names. Indicate this using WITH HEADERS and
you can access specific fields by their corresponding column name.
189
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
none
3.20.5. Import data from a CSV file with a custom field delimiter
Sometimes, your CSV file has other field delimiters than commas. You can specify which delimiter your file
uses, using FIELDTERMINATOR. Hexadecimal representation of the unicode character encoding can be used
if prepended by \u. The encoding must be written with four digits. For example, \u003B is equivalent to ;
(SEMICOLON).
artists-fieldterminator.csv
1;ABBA;1992
2;Roxette;1986
3;Europe;1979
4;The Cardigans;1992
Query
As values in this file are separated by a semicolon, a custom FIELDTERMINATOR is specified in the LOAD CSV
clause.
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
none
190
3.20.6. Importing large amounts of data
If the CSV file contains a significant number of rows (approaching hundreds of thousands or millions),
USING PERIODIC COMMIT can be used to instruct Neo4j to perform a commit after a number of rows. This
reduces the memory overhead of the transaction state. By default, the commit will happen every 1000
rows. For more information, see PERIODIC COMMIT query hint.
Note: The USE clause can not be used together with the PERIODIC COMMIT clause.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
none
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
none
191
3.20.8. Import data containing escaped characters
In this example, we both have additional quotes around the values, as well as escaped quotes inside one
value.
artists-with-escaped-char.csv
"1","The ""Symbol""","1992"
Query
Note that strings are wrapped in quotes in the output here. You can see that when comparing to the
length of the string in this case!
Result
+------------------------------+
| name | year | size |
+------------------------------+
| "The "Symbol"" | 1992 | 12 |
+------------------------------+
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
none
artists.csv
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
192
Query
Result
+---------------------------------------+
| number | line |
+---------------------------------------+
| 1 | ["1","ABBA","1992"] |
| 2 | ["2","Roxette","1986"] |
| 3 | ["3","Europe","1979"] |
| 4 | ["4","The Cardigans","1992"] |
+---------------------------------------+
4 rows
none
artists.csv
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
Query
Since LOAD CSV can temporary download a file to process it, it is important to note that file() will always
return the path on disk. If LOAD CSV is invoked with a file:/// URL that points to your disk file() will
return that same path.
Result
+------------------------------------------+
| path |
+------------------------------------------+
| "/home/example/neo4j/import/artists.csv" |
+------------------------------------------+
1 row
193
Chapter 4. Functions
This section contains information on all functions in the Cypher query language.
Please note
• Functions in Cypher return null if an input parameter is null.
• Functions taking a string as input all operate on Unicode characters rather than on a standard char[].
For example, the size() function applied to any Unicode character will return 1, even if the character
does not fit in the 16 bits of one char.
Predicate functions
These functions return either true or false for the given arguments.
Function Description
all() Tests whether the predicate holds for all elements in a list.
any() Tests whether the predicate holds for at least one element in
a list.
exists() Returns true if a match for the pattern exists in the graph, or
if the specified property exists in the node, relationship or
map.
single() Returns true if the predicate holds for exactly one of the
elements in a list.
194
Scalar functions
Function Description
size() applied to pattern expression Returns the number of paths matching the pattern
expression.
Aggregating functions
These functions take multiple values as arguments, and calculate and return an aggregated value from
them.
Function Description
195
Function Description
stDev() Returns the standard deviation for the given value over a
group for a sample of a population.
stDevP() Returns the standard deviation for the given value over a
group for an entire population.
List functions
These functions return lists of other values. Further details and examples of lists may be found in Lists.
Function Description
keys() Returns a list containing the string representations for all the
property names of a node, relationship, or map.
labels() Returns a list containing the string representations for all the
labels of a node.
reverse() Returns a list in which the order of all elements in the original
list have been reversed.
These functions all operate on numerical expressions only, and will return an error if used on any other
values.
Function Description
196
Function Description
floor() Returns the largest floating point number that is less than or
equal to a number and equal to a mathematical integer.
round(), with precision Returns the value of the given number rounded with the
specified precision, with half-values always being rounded
up.
round(), with precision and rounding mode Returns the value of the given number rounded with the
specified precision and the specified rounding mode.
These functions all operate on numerical expressions only, and will return an error if used on any other
values.
Function Description
exp() Returns e^n, where e is the base of the natural logarithm, and
n is the value of the argument expression.
These functions all operate on numerical expressions only, and will return an error if used on any other
values.
Function Description
197
Function Description
String functions
These functions are used to manipulate strings or to create a string representation of another value.
Function Description
Values of the temporal types — Date, Time, LocalTime, DateTime, and LocalDateTime — can be created
198
manipulated using the following functions:
Function Description
199
Function Description
localtime({hour [, minute, second, …]}) Returns a LocalTime with the specified component values.
localtime({time [, hour, …]}) Returns a LocalTime from a map of another temporal value’s
components.
time({hour [, minute, …]}) Returns a Time with the specified component values.
time({time [, hour, …, timezone]}) Returns a Time from a map of another temporal value’s
components.
Duration values of the temporal types can be created manipulated using the following functions:
Function Description
200
Function Description
Spatial functions
These functions are used to specify 2D or 3D points in a geographic or cartesian Coordinate Reference
System and to calculate the geodesic distance between two points.
Function Description
point() - Cartesian 2D Returns a 2D point object, given two coordinate values in the
Cartesian coordinate system.
point() - WGS 84 2D Returns a 2D point object, given two coordinate values in the
WGS 84 geographic coordinate system.
User-defined functions
User-defined functions are written in Java, deployed into the database and are called in the same way as
any other Cypher function. There are two main types of functions that can be developed and used:
Scalar For each row the function Using UDF Extending Neo4j (UDF)
takes parameters and returns
a result
Aggregating Consumes many rows and Using aggregating UDF Extending Neo4j
produces an aggregated (Aggregating UDF)
result
LOAD CSV functions can be used to get information about the file that is processed by LOAD CSV.
201
Function Description
linenumber() Returns the line number that LOAD CSV is currently using.
file() Returns the absolute path of the file that LOAD CSV is using.
Functions:
• all()
• any()
• exists()
• none()
• single()
202
Graph
N0 [
label = "name = \'Alice\'\leyes = \'brown\'\lage = 38\l"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'Bob\'\leyes = \'blue\'\lage = 25\l"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "MARRIED\n"
]
N2 [
label = "name = \'Charlie\'\leyes = \'green\'\lage = 53\l"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "name = \'Daniel\'\leyes = \'brown\'\lage = 54\l"
]
N4 [
label = "liked_colors = \[\'pink\', \'yellow\', \'black\'\]\lname = \'Eskil\'\leyes = \'blue\'\lage =
41\l"
]
N5 [
label = "alias = \'Frank\'\leyes = \'brown\'\lage = 61\lliked_colors = \[\'blue\', \'green\'\]\l"
]
4.1.1. all()
The function all() returns true if the predicate holds for all elements in the given list. null is returned if
the list is null or all of its elements are null.
Returns:
A Boolean.
Arguments:
203
Name Description
Query
MATCH p = (a)-[*1..3]->(b)
WHERE
a.name = 'Alice'
AND b.name = 'Daniel'
AND all(x IN nodes(p) WHERE x.age > 30)
RETURN p
All nodes in the returned paths will have a property age with a value larger than 30.
(0)-[KNOWS,1]->(2)-[KNOWS,3]->(3)
Rows: 1
4.1.2. any()
The function any() returns true if the predicate holds for at least one element in the given list. null is
returned if the list is null or all of its elements are null.
Returns:
A Boolean.
Arguments:
Name Description
204
Query
MATCH (n)
WHERE any(color IN n.liked_colors WHERE color = 'yellow')
RETURN n
The query returns nodes with the property liked_colors (as a list), where at least one element has the
value 'yellow'.
Node[4]{liked_colors:["pink","yellow","black"],name:"Eskil",eyes:"blue",age:41}
Rows: 1
4.1.3. exists()
The function exists() returns true if a match for the given pattern exists in the graph, or if the specified
property exists in the node, relationship or map. null is returned if the input argument is null.
Syntax: exists(pattern-or-property)
Returns:
A Boolean.
Arguments:
Name Description
Query
MATCH (n)
WHERE exists(n.name)
RETURN
n.name AS name,
exists((n)-[:MARRIED]->()) AS is_married
The names of all nodes with the name property are returned, along with a boolean (true or false) indicating
if they are married.
name is_married
"Alice" false
"Bob" true
"Charlie" false
"Daniel" false
"Eskil" false
205
name is_married
Rows: 5
Query
MATCH
(a),
(b)
WHERE
exists(a.name)
AND NOT exists(b.name)
OPTIONAL MATCH (c:DoesNotExist)
RETURN
a.name AS a_name,
b.name AS b_name,
exists(b.name) AS b_has_name,
c.name AS c_name,
exists(c.name) AS c_has_name
ORDER BY a_name, b_name, c_name
LIMIT 1
Three nodes are returned: one with a property name, one without a property name, and one that does not
exist (e.g., is null). This query exemplifies the behavior of exists() when operating on null nodes.
Rows: 1
4.1.4. none()
The function none() returns true if the predicate does not hold for any element in the given list. null is
returned if the list is null or all of its elements are null.
Returns:
A Boolean.
Arguments:
Name Description
206
Query
MATCH p = (n)-[*1..3]->(b)
WHERE
n.name = 'Alice'
AND none(x IN nodes(p) WHERE x.age = 25)
RETURN p
No node in the returned paths has a property age with the value 25.
(0)-[KNOWS,1]->(2)
(0)-[KNOWS,1]->(2)-[KNOWS,3]->(3)
Rows: 2
4.1.5. single()
The function single() returns true if the predicate holds for exactly one of the elements in the given list.
null is returned if the list is null or all of its elements are null.
Returns:
A Boolean.
Arguments:
Name Description
Query
MATCH p = (n)-->(b)
WHERE
n.name = 'Alice'
AND single(var IN nodes(p) WHERE var.eyes = 'blue')
RETURN p
In every returned path there is exactly one node that has a property eyes with the value 'blue'.
(0)-[KNOWS,0]->(1)
Rows: 1
207
4.2. Scalar functions
Scalar functions return a single value.
Functions:
• coalesce()
• endNode()
• head()
• id()
• last()
• length()
• properties()
• randomUUID()
• size()
• Size of string
• startNode()
• timestamp()
• toBoolean()
• toFloat()
• toInteger()
• type()
The length() and size() functions are quite similar, and so it is important to take note of
the difference.
Function length()
Only works for paths.
Function size()
Only works for the three types: strings, lists, and pattern expressions.
208
Graph
N0 [
label = "{Developer|name = \'Alice\'\leyes = \'brown\'\lage = 38\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'Bob\'\leyes = \'blue\'\lage = 25\l"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "MARRIED\n"
]
N2 [
label = "name = \'Charlie\'\leyes = \'green\'\lage = 53\l"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "name = \'Daniel\'\leyes = \'brown\'\lage = 54\l"
]
N4 [
label = "liked_colors = \[\'pink\', \'yellow\', \'black\'\]\lname = \'Eskil\'\leyes = \'blue\'\lage =
41\l"
]
4.2.1. coalesce()
The function coalesce() returns the first non-null value in the given list of expressions.
Returns:
The type of the value returned will be that of the first non-null expression.
Arguments:
Name Description
Considerations:
209
Query
MATCH (a)
WHERE a.name = 'Alice'
RETURN coalesce(a.hairColor, a.eyes)
coalesce(a.hairColor, a.eyes)
"brown"
Rows: 1
4.2.2. endNode()
The function endNode() returns the end node of a relationship.
Syntax: endNode(relationship)
Returns:
A Node.
Arguments:
Name Description
Considerations:
Query
MATCH (x:Developer)-[r]-()
RETURN endNode(r)
endNode(r)
Node[2]{name:"Charlie",eyes:"green",age:53}
Node[1]{name:"Bob",eyes:"blue",age:25}
Rows: 2
4.2.3. head()
The function head() returns the first element in a list.
Syntax: head(expression)
210
Returns:
The type of the value returned will be that of the first element of the list.
Arguments:
Name Description
Considerations:
Query
MATCH (a)
WHERE a.name = 'Eskil'
RETURN a.liked_colors, head(a.liked_colors)
a.liked_colors head(a.liked_colors)
["pink","yellow","black"] "pink"
Rows: 1
4.2.4. id()
The function id() returns an identifier; the function can be utilized for a relationship or a node.
Node
Every node in a database has an identifier. The identifier for a node is guaranteed to
be unique among other nodes' identifiers in the same database, within the scope of a
single transaction.
Relationship
Every relationship in a database has an identifier. The identifier for a relationship is
guaranteed to be unique among other relationships' identifiers in the same database,
within the scope of a single transaction.
Syntax: id(expression)
Returns:
211
An Integer.
Arguments:
Name Description
Considerations:
Query
MATCH (a)
RETURN id(a)
id(a)
Rows: 5
4.2.5. last()
The function last() returns the last element in a list.
Syntax: last(expression)
Returns:
The type of the value returned will be that of the last element of the list.
Arguments:
Name Description
Considerations:
212
last([]) returns null.
Query
MATCH (a)
WHERE a.name = 'Eskil'
RETURN a.liked_colors, last(a.liked_colors)
a.liked_colors last(a.liked_colors)
["pink","yellow","black"] "black"
Rows: 1
4.2.6. length()
The function length() returns the length of a path.
Syntax: length(path)
Returns:
An Integer.
Arguments:
Name Description
Considerations:
Query
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'Alice'
RETURN length(p)
length(p)
213
length(p)
Rows: 3
4.2.7. properties()
The function properties() returns a map containing all the properties; the function can be utilized for a
relationship or a node. If the argument is already a map, it is returned unchanged.
Syntax: properties(expression)
Returns:
A Map.
Arguments:
Name Description
Considerations:
Query
properties(p)
Rows: 1
Nodes created: 1
Properties set: 2
Labels added: 1
4.2.8. randomUUID()
The function randomUUID() returns a randomly-generated Universally Unique Identifier (UUID), also known
as a Globally Unique Identifier (GUID). This is a 128-bit value with strong guarantees of uniqueness.
Syntax: randomUUID()
Returns:
214
A String.
Query
uuid
"4b8c2c56-03e9-4ac4-86ba-3a327cbf91b6"
Rows: 1
4.2.9. size()
The function size() returns the number of elements in a list.
Syntax: size(list)
Returns:
An Integer.
Arguments:
Name Description
Considerations:
Query
size(['Alice', 'Bob'])
Rows: 1
215
4.2.10. size() applied to pattern expression
This is the same function size() as described above, but you pass in a pattern expression, instead of a list.
The function size will then calculate on a list of paths.
Arguments:
Name Description
Query
MATCH (a)
WHERE a.name = 'Alice'
RETURN size((a)-->()-->()) AS fof
fof
Rows: 1
The number of paths matching the pattern expression is returned. (The size of the list of paths).
Syntax: size(string)
Returns:
An Integer.
Arguments:
Name Description
Considerations:
216
Query
MATCH (a)
WHERE size(a.name) > 6
RETURN size(a.name)
size(a.name)
Rows: 1
4.2.12. startNode()
The function startNode() returns the start node of a relationship.
Syntax: startNode(relationship)
Returns:
A Node.
Arguments:
Name Description
Considerations:
Query
MATCH (x:Developer)-[r]-()
RETURN startNode(r)
startNode(r)
Node[0]{name:"Alice",eyes:"brown",age:38}
Node[0]{name:"Alice",eyes:"brown",age:38}
Rows: 2
4.2.13. timestamp()
The function timestamp() returns the difference, measured in milliseconds, between the current time and
217
midnight, January 1, 1970 UTC.
Syntax: timestamp()
Returns:
An Integer.
Considerations:
timestamp() will return the same value during one entire query, even for long-running queries.
Query
RETURN timestamp()
timestamp()
1620202101120
Rows: 1
4.2.14. toBoolean()
The function toBoolean() converts a string value to a boolean value.
Syntax: toBoolean(expression)
Returns:
A Boolean.
Arguments:
Name Description
Considerations:
218
Query
true <null>
Rows: 1
4.2.15. toFloat()
The function toFloat() converts an integer or a string value to a floating point number.
Syntax: toFloat(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
11.5 <null>
Rows: 1
4.2.16. toInteger()
The function toInteger() converts a floating point or a string value to an integer value.
Syntax: toInteger(expression)
219
Returns:
An Integer.
Arguments:
Name Description
Considerations:
Query
42 <null>
Rows: 1
4.2.17. type()
The function type() returns the string representation of the relationship type.
Syntax: type(relationship)
Returns:
A String.
Arguments:
Name Description
Considerations:
220
Query
MATCH (n)-[r]->()
WHERE n.name = 'Alice'
RETURN type(r)
type(r)
"KNOWS"
"KNOWS"
Rows: 2
Functions:
• avg() - Durations
• collect()
• count()
• max()
• min()
• percentileCont()
• percentileDisc()
• stDev()
• stDevP()
• sum() - Durations
Aggregation can be computed over all the matching paths, or it can be further divided by introducing
grouping keys. Grouping keys are non-aggregate expressions, that are used to group the values going into
the aggregate functions.
RETURN n, count(*)
We have two return expressions: n, and count(*). The first, n, is not an aggregate function, so it will be the
221
grouping key. The latter, count(*) is an aggregate expression. The matching paths will be divided into
different buckets, depending on the grouping key. The aggregate function will then be run on these
buckets, calculating an aggregate value per bucket.
To use aggregations to sort the result set, the aggregation must be included in the RETURN to be used in the
ORDER BY.
The DISTINCT operator works in conjunction with aggregation. It is used to make all values unique before
running them through an aggregate function. More information about DISTINCT may be found in Syntax →
Aggregation operators.
Graph
N0 [
label = "{Person|name = \'A\'\lage = 13\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N5 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "READS\n"
]
N1 [
label = "{Person|name = \'B\'\lage = 33\leyes = \'blue\'\l}"
]
N1 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N2 [
label = "{Person|name = \'C\'\lage = 44\leyes = \'blue\'\l}"
]
N2 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "{Person|name = \'D\'\leyes = \'brown\'\l}"
]
N4 [
label = "{Person|name = \'D\'\l}"
]
N5 [
label = "{Book|name = \'Cypher\'\l}"
]
222
4.3.1. avg() - Numeric values
The function avg() returns the average of a set of numeric values.
Syntax: avg(expression)
Returns:
Either an Integer or a Float, depending on the values returned by expression and whether or not the calculation overflows.
Arguments:
Name Description
Considerations:
Query
MATCH (n:Person)
RETURN avg(n.age)
avg(n.age)
30.0
Rows: 1
Syntax: avg(expression)
Returns:
A Duration.
Arguments:
Name Description
223
Considerations:
Query
avg(dur)
P1DT2H22.5S
Rows: 1
4.3.3. collect()
The function collect() returns a single aggregated list containing the values returned by an expression.
Syntax: collect(expression)
Returns:
A list containing heterogeneous elements; the types of the elements are determined by the values returned by expression.
Arguments:
Name Description
Considerations:
Any null values are ignored and will not be added to the list.
Query
MATCH (n:Person)
RETURN collect(n.age)
224
collect(n.age)
[13,33,44]
Rows: 1
4.3.4. count()
The function count() returns the number of values or rows, and appears in two variants:
count(*)
returns the number of matching rows.
count(expr)
returns the number of non-null values returned by an expression.
Syntax: count(expression)
Returns:
An Integer.
Arguments:
Name Description
expression An expression.
Considerations:
count(null) returns 0.
Query
The labels and age property of the start node n and the number of nodes related to n are returned.
["Person"] 13 4
225
labels(n) n.age count(*)
Rows: 1
Query
The type of matched relationships are grouped and the group count are returned.
type(r) count(*)
"KNOWS" 3
"READS" 1
Rows: 2
Query
The number of nodes that are connected directly (one relationship) to the node, with the name 'A', is
returned.
count(x)
Rows: 1
Query
MATCH (n:Person)
RETURN count(n.age)
226
The number of nodes with the label Person and a property age is returned. (If you want the sum, use
sum(n.age))
count(n.age)
Rows: 1
count(DISTINCT friend_of_friend)
Will only count a friend_of_friend once, as DISTINCT removes the duplicates.
count(friend_of_friend)
Will consider the same friend_of_friend multiple times.
Query
MATCH (me:Person)-->(friend:Person)-->(friend_of_friend:Person)
WHERE me.name = 'A'
RETURN count(DISTINCT friend_of_friend), count(friend_of_friend)
Both B and C know D and thus D will get counted twice when not using DISTINCT.
1 2
Rows: 1
4.3.5. max()
The function max() returns the maximum value in a set of values.
Syntax: max(expression)
Returns:
Arguments:
Name Description
227
Considerations:
In a mixed set, any numeric value is always considered to be higher than any string value, and any string value is always
considered to be higher than any list.
Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to
the end.
Query
The highest of all the values in the mixed set — in this case, the numeric value 1 — is returned.
The value '99' (a string), is considered to be a lower value than 1 (an integer), because
'99' is a string.
max(val)
Rows: 1
Query
The highest of all the lists in the set — in this case, the list [1, 2] — is returned, as the number 2 is
considered to be a higher value than the string 'a', even though the list [1, 'a', 89] contains more
elements.
max(val)
[1,2]
Rows: 1
Query
MATCH (n:Person)
RETURN max(n.age)
228
max(n.age)
44
Rows: 1
4.3.6. min()
The function min() returns the minimum value in a set of values.
Syntax: min(expression)
Returns:
Arguments:
Name Description
Considerations:
In a mixed set, any string value is always considered to be lower than any numeric value, and any list is always considered to
be lower than any string.
Lists are compared in dictionary order, i.e. list elements are compared pairwise in ascending order from the start of the list to
the end.
Query
The lowest of all the values in the mixed set — in this case, the string value "1" — is returned. Note that the
(numeric) value 0.2, which may appear at first glance to be the lowest value in the list, is considered to be
a higher value than "1" as the latter is a string.
min(val)
"1"
Rows: 1
229
Query
The lowest of all the values in the set — in this case, the list ['a', 'c', 23] — is returned, as (i) the two
lists are considered to be lower values than the string "d", and (ii) the string "a" is considered to be a lower
value than the numerical value 1.
min(val)
["a","c",23]
Rows: 1
Query
MATCH (n:Person)
RETURN min(n.age)
min(n.age)
13
Rows: 1
4.3.7. percentileCont()
The function percentileCont() returns the percentile of the given value over a group, with a percentile
from 0.0 to 1.0. It uses a linear interpolation method, calculating a weighted average between two values if
the desired percentile lies between them. For nearest values using a rounding method, see
percentileDisc.
Returns:
A Float.
Arguments:
Name Description
Considerations:
230
Any null values are excluded from the calculation.
Query
MATCH (n:Person)
RETURN percentileCont(n.age, 0.4)
The 40th percentile of the values in the property age is returned, calculated with a weighted average.
percentileCont(n.age, 0.4)
29.0
Rows: 1
4.3.8. percentileDisc()
The function percentileDisc() returns the percentile of the given value over a group, with a percentile
from 0.0 to 1.0. It uses a rounding method and calculates the nearest value to the percentile. For
interpolated values, see percentileCont.
Returns:
Either an Integer or a Float, depending on the values returned by expression and whether or not the calculation overflows.
Arguments:
Name Description
Considerations:
Query
MATCH (n:Person)
RETURN percentileDisc(n.age, 0.5)
231
percentileDisc(n.age, 0.5)
33
Rows: 1
4.3.9. stDev()
The function stDev() returns the standard deviation for the given value over a group. It uses a standard
two-pass method, with N - 1 as the denominator, and should be used when taking a sample of the
population for an unbiased estimate. When the standard variation of the entire population is being
calculated, stdDevP should be used.
Syntax: stDev(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
stDev(null) returns 0.
Query
MATCH (n)
WHERE n.name IN ['A', 'B', 'C']
RETURN stDev(n.age)
stDev(n.age)
15.716233645501712
Rows: 1
4.3.10. stDevP()
The function stDevP() returns the standard deviation for the given value over a group. It uses a standard
two-pass method, with N as the denominator, and should be used when calculating the standard deviation
for an entire population. When the standard variation of only a sample of the population is being
calculated, stDev should be used.
232
Syntax: stDevP(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
stDevP(null) returns 0.
Query
MATCH (n)
WHERE n.name IN ['A', 'B', 'C']
RETURN stDevP(n.age)
The population standard deviation of the values in the property age is returned.
stDevP(n.age)
12.832251036613439
Rows: 1
Syntax: sum(expression)
Returns:
Arguments:
Name Description
Considerations:
233
sum(null) returns 0.
Query
MATCH (n:Person)
RETURN sum(n.age)
sum(n.age)
90
Rows: 1
Syntax: sum(expression)
Returns:
A Duration.
Arguments:
Name Description
Considerations:
Query
sum(dur)
P2DT4H45S
Rows: 1
234
4.4. List functions
List functions return lists of things — nodes in a path, and so on.
Further details and examples of lists may be found in Lists and List operators.
Functions:
• keys()
• labels()
• nodes()
• range()
• reduce()
• relationships()
• reverse()
• tail()
Graph
N0 [
label = "{Person, Developer|name = \'Alice\'\leyes = \'brown\'\lage = 38\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "name = \'Bob\'\leyes = \'blue\'\lage = 25\l"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "MARRIED\n"
]
N2 [
label = "name = \'Charlie\'\leyes = \'green\'\lage = 53\l"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "name = \'Daniel\'\leyes = \'brown\'\lage = 54\l"
]
N4 [
label = "array = \[\'one\', \'two\', \'three\'\]\lname = \'Eskil\'\leyes = \'blue\'\lage = 41\l"
]
235
4.4.1. keys()
keys returns a list containing the string representations for all the property names of a node, relationship,
or map.
Syntax: keys(expression)
Returns:
Arguments:
Name Description
Considerations:
Query
A list containing the names of all the properties on the node bound to a is returned.
keys(a)
["name","eyes","age"]
Rows: 1
4.4.2. labels()
labels returns a list containing the string representations for all the labels of a node.
Syntax: labels(node)
Returns:
Arguments:
Name Description
236
Considerations:
Query
labels(a)
["Person","Developer"]
Rows: 1
4.4.3. nodes()
nodes() returns a list containing all the nodes in a path.
Syntax: nodes(path)
Returns:
Arguments:
Name Description
Considerations:
Query
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'Alice' AND c.name = 'Eskil'
RETURN nodes(p)
nodes(p)
[Node[0]{name:"Alice",eyes:"brown",age:38},Node[1]{name:"Bob",eyes:"blue",age:25},Node[4]{array:["one","two","
three"],name:"Eskil",eyes:"blue",age:41}]
237
nodes(p)
Rows: 1
4.4.4. range()
range() returns a list comprising all integer values within a range bounded by a start value start and end
value end, where the difference step between any two consecutive values is constant; i.e. an arithmetic
progression. To create ranges with decreasing integer values, use a negative value step. The range is
inclusive for non-empty ranges, and the arithmetic progression will therefore always contain start
and — depending on the values of start, step and end — end. The only exception where the range does
not contain start are empty ranges. An empty range will be returned if the value step is negative and
start - end is positive, or vice versa, e.g. range(0, 5, -1).
Returns:
Arguments:
Name Description
Query
[0,1,2,3,4,5,6,7,8,9,10] [2,5,8,11,14,17] []
Rows: 1
4.4.5. reduce()
reduce() returns the value resulting from the application of an expression on each successive element in a
list in conjunction with the result of the computation thus far. This function will iterate through each
element e in the given list, run the expression on e — taking into account the current partial result — and
store the new partial result in the accumulator. This function is analogous to the fold or reduce method in
functional languages such as Lisp and Scala.
238
Syntax: reduce(accumulator = initial, variable IN list | expression)
Returns:
The type of the value returned depends on the arguments provided, along with the semantics of expression.
Arguments:
Name Description
accumulator A variable that will hold the result and the partial results as
the list is iterated.
expression This expression will run once per value in the list, and
produce the result value.
Query
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'Alice' AND b.name = 'Bob' AND c.name = 'Daniel'
RETURN reduce(totalAge = 0, n IN nodes(p) | totalAge + n.age) AS reduction
The age property of all nodes in the path are summed and returned as a single value.
reduction
117
Rows: 1
4.4.6. relationships()
relationships() returns a list containing all the relationships in a path.
Syntax: relationships(path)
Returns:
Arguments:
Name Description
239
Considerations:
Query
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'Alice' AND c.name = 'Eskil'
RETURN relationships(p)
relationships(p)
[:KNOWS[0]{},:MARRIED[4]{}]
Rows: 1
4.4.7. reverse()
reverse() returns a list in which the order of all elements in the original list have been reversed.
Syntax: reverse(original)
Returns:
A list containing homogeneous or heterogeneous elements; the types of the elements are determined by the elements
within original.
Arguments:
Name Description
Considerations:
Query
reverse(ids)
[487,<null>,521,"abc",4923]
Rows: 1
240
4.4.8. tail()
tail() returns a list lresult containing all the elements, excluding the first one, from a list list.
Syntax: tail(list)
Returns:
A list containing heterogeneous elements; the types of the elements are determined by the elements in list.
Arguments:
Name Description
Query
The property named array and a list comprising all but the first element of the array property are returned.
a.array tail(a.array)
["one","two","three"] ["two","three"]
Rows: 1
Functions:
• abs()
• ceil()
• floor()
• rand()
• round()
• sign()
241
Graph
N0 [
label = "{A|name = \'Alice\'\leyes = \'brown\'\lage = 38\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 [
label = "{B|name = \'Bob\'\leyes = \'blue\'\lage = 25\l}"
]
N1 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N1 -> N4 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "MARRIED\n"
]
N2 [
label = "{C|name = \'Charlie\'\leyes = \'green\'\lage = 53\l}"
]
N2 -> N3 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "KNOWS\n"
]
N3 [
label = "{D|name = \'Daniel\'\leyes = \'brown\'\lage = 54\l}"
]
N4 [
label = "{E|array = \[\'one\', \'two\', \'three\'\]\lname = \'Eskil\'\leyes = \'blue\'\lage = 41\l}"
]
4.5.1. abs()
abs() returns the absolute value of the given number.
Syntax: abs(expression)
Returns:
Arguments:
Name Description
Considerations:
242
Query
MATCH (a), (e) WHERE a.name = 'Alice' AND e.name = 'Eskil' RETURN a.age, e.age, abs(a.age - e.age)
38 41 3
Rows: 1
4.5.2. ceil()
ceil() returns the smallest floating point number that is greater than or equal to the given number and
equal to a mathematical integer.
Syntax: ceil(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN ceil(0.1)
ceil(0.1)
1.0
Rows: 1
4.5.3. floor()
floor() returns the largest floating point number that is less than or equal to the given number and equal
to a mathematical integer.
243
Syntax: floor(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN floor(0.9)
floor(0.9)
0.0
Rows: 1
4.5.4. rand()
rand() returns a random floating point number in the range from 0 (inclusive) to 1 (exclusive); i.e. [0,1).
The numbers returned follow an approximate uniform distribution.
Syntax: rand()
Returns:
A Float.
Query
RETURN rand()
rand()
0.3598472845106012
244
rand()
Rows: 1
4.5.5. round()
round() returns the value of the given number rounded to the nearest integer, with half-way values always
rounded up.
Syntax: round(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN round(3.141592)
3.0 is returned.
round(3.141592)
3.0
Rows: 1
Returns:
A Float.
Arguments:
245
Name Description
Considerations:
Query
RETURN round(3.141592, 3)
3.142 is returned.
round(3.141592, 3)
3.142
Rows: 1
Returns:
A Float.
Arguments:
Name Description
Modes:
Mode Description
246
Mode Description
Considerations:
Query
3.15 is returned.
round(3.141592, 2, 'CEILING')
3.15
Rows: 1
4.5.8. sign()
sign() returns the signum of the given number: 0 if the number is 0, -1 for any negative number, and 1 for
any positive number.
Syntax: sign(expression)
Returns:
An Integer.
Arguments:
Name Description
Considerations:
247
Query
sign(-17) sign(0.1)
-1 1
Rows: 1
Functions:
• e()
• exp()
• log()
• log10()
• sqrt()
4.6.1. e()
e() returns the base of the natural logarithm, e.
Syntax: e()
Returns:
A Float.
Query
RETURN e()
e()
2.718281828459045
248
e()
Rows: 1
4.6.2. exp()
exp() returns e^n, where e is the base of the natural logarithm, and n is the value of the argument
expression.
Syntax: e(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN exp(2)
exp(2)
7.38905609893065
Rows: 1
4.6.3. log()
log() returns the natural logarithm of a number.
Syntax: log(expression)
Returns:
A Float.
Arguments:
249
Name Description
Considerations:
Query
RETURN log(27)
log(27)
3.295836866004329
Rows: 1
4.6.4. log10()
log10() returns the common logarithm (base 10) of a number.
Syntax: log10(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN log10(27)
250
log10(27)
1.4313637641589874
Rows: 1
4.6.5. sqrt()
sqrt() returns the square root of a number.
Syntax: sqrt(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN sqrt(256)
sqrt(256)
16.0
Rows: 1
Functions:
• acos()
• asin()
251
• atan()
• atan2()
• cos()
• cot()
• degrees()
• haversin()
• pi()
• radians()
• sin()
• tan()
4.7.1. acos()
acos() returns the arccosine of a number in radians.
Syntax: acos(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
If (expression < -1) or (expression > 1), then (acos(expression)) returns null.
Query
RETURN acos(0.5)
acos(0.5)
1.0471975511965979
Rows: 1
252
4.7.2. asin()
asin() returns the arcsine of a number in radians.
Syntax: asin(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
If (expression < -1) or (expression > 1), then (asin(expression)) returns null.
Query
RETURN asin(0.5)
asin(0.5)
0.5235987755982989
Rows: 1
4.7.3. atan()
atan() returns the arctangent of a number in radians.
Syntax: atan(expression)
Returns:
A Float.
Arguments:
Name Description
253
Considerations:
Query
RETURN atan(0.5)
atan(0.5)
0.4636476090008061
Rows: 1
4.7.4. atan2()
atan2() returns the arctangent2 of a set of coordinates in radians.
Returns:
A Float.
Arguments:
Name Description
Considerations:
atan2(null, null), atan2(null, expression2) and atan(expression1, null) all return null.
Query
atan2(0.5, 0.6)
0.6947382761967033
254
atan2(0.5, 0.6)
Rows: 1
4.7.5. cos()
cos() returns the cosine of a number.
Syntax: cos(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN cos(0.5)
cos(0.5)
0.8775825618903728
Rows: 1
4.7.6. cot()
cot() returns the cotangent of a number.
Syntax: cot(expression)
Returns:
A Float.
Arguments:
255
Name Description
Considerations:
Query
RETURN cot(0.5)
cot(0.5)
1.830487721712452
Rows: 1
4.7.7. degrees()
degrees() converts radians to degrees.
Syntax: degrees(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN degrees(3.14159)
256
degrees(3.14159)
179.9998479605043
Rows: 1
4.7.8. haversin()
haversin() returns half the versine of a number.
Syntax: haversin(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN haversin(0.5)
haversin(0.5)
0.06120871905481362
Rows: 1
Query
CREATE (ber:City {lat: 52.5, lon: 13.4}), (sm:City {lat: 37.5, lon: -122.3})
RETURN 2 * 6371 * asin(sqrt(haversin(radians( sm.lat - ber.lat ))
+ cos(radians( sm.lat )) * cos(radians( ber.lat )) *
haversin(radians( sm.lon - ber.lon )))) AS dist
257
The estimated distance between 'Berlin' and 'San Mateo' is returned.
dist
9129.969740051658
Rows: 1
Nodes created: 2
Properties set: 4
Labels added: 2
4.7.10. pi()
pi() returns the mathematical constant pi.
Syntax: pi()
Returns:
A Float.
Query
RETURN pi()
pi()
3.141592653589793
Rows: 1
4.7.11. radians()
radians() converts degrees to radians.
Syntax: radians(expression)
Returns:
A Float.
Arguments:
Name Description
258
Considerations:
Query
RETURN radians(180)
radians(180)
3.141592653589793
Rows: 1
4.7.12. sin()
sin() returns the sine of a number.
Syntax: sin(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN sin(0.5)
sin(0.5)
0.479425538604203
Rows: 1
259
4.7.13. tan()
tan() returns the tangent of a number.
Syntax: tan(expression)
Returns:
A Float.
Arguments:
Name Description
Considerations:
Query
RETURN tan(0.5)
tan(0.5)
0.5463024898437905
Rows: 1
Functions taking a string as input all operate on Unicode characters rather than on a standard char[]. For
example, the size() function applied to any Unicode character will return 1, even if the character does not
fit in the 16 bits of one char.
suitable for parsing by the corresponding temporal functions. This string will therefore
be formatted according to the ISO 8601 format.
260
See also String operators.
Functions:
• left()
• lTrim()
• replace()
• reverse()
• right()
• rTrim()
• split()
• substring()
• toLower()
• toString()
• toUpper()
• trim()
4.8.1. left()
left() returns a string containing the specified number of leftmost characters of the original string.
Returns:
A String.
Arguments:
Name Description
Considerations:
261
Query
RETURN left('hello', 3)
left('hello', 3)
"hel"
Rows: 1
4.8.2. ltrim()
lTrim() returns the original string with leading whitespace removed.
Syntax: lTrim(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
lTrim(' hello')
"hello"
Rows: 1
4.8.3. replace()
replace() returns a string in which all occurrences of a specified string in the original string have been
replaced by another (specified) string.
Returns:
262
A String.
Arguments:
Name Description
Considerations:
Query
"hewwo"
Rows: 1
4.8.4. reverse()
reverse() returns a string in which the order of all characters in the original string have been reversed.
Syntax: reverse(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
263
Query
RETURN reverse('anagram')
reverse('anagram')
"margana"
Rows: 1
4.8.5. right()
right() returns a string containing the specified number of rightmost characters of the original string.
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
RETURN right('hello', 3)
right('hello', 3)
"llo"
Rows: 1
264
4.8.6. rtrim()
rTrim() returns the original string with trailing whitespace removed.
Syntax: rTrim(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
rTrim('hello ')
"hello"
Rows: 1
4.8.7. split()
split() returns a list of strings resulting from the splitting of the original string around matches of the
given delimiter.
Returns:
A list of Strings.
Arguments:
Name Description
Considerations:
265
split(null, splitDelimiter) and split(original, null) both return null
Query
split('one,two', ',')
["one","two"]
Rows: 1
4.8.8. substring()
substring() returns a substring of the original string, beginning with a 0-based index start and length.
Returns:
A String.
Arguments:
Name Description
Considerations:
If length is omitted, the function returns the substring starting at the position given by start and extending to the end of
original.
Query
266
Table 314. Result
substring('hello', 1, 3) substring('hello', 2)
"ell" "llo"
Rows: 1
4.8.9. toLower()
toLower() returns the original string in lowercase.
Syntax: toLower(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
RETURN toLower('HELLO')
toLower('HELLO')
"hello"
Rows: 1
4.8.10. toString()
toString() converts an integer, float or boolean value to a string.
Syntax: toString(expression)
Returns:
A String.
Arguments:
267
Name Description
Considerations:
Query
RETURN toString(11.5),
toString('already a string'),
toString(true),
toString(date({year:1984, month:10, day:11})) AS dateString,
toString(datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14, millisecond: 341, timezone:
'Europe/Stockholm'})) AS datetimeString,
toString(duration({minutes: 12, seconds: -60})) AS durationString
Rows: 1
4.8.11. toUpper()
toUpper() returns the original string in uppercase.
Syntax: toUpper(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
RETURN toUpper('hello')
268
Table 317. Result
toUpper('hello')
"HELLO"
Rows: 1
4.8.12. trim()
trim() returns the original string with leading and trailing whitespace removed.
Syntax: trim(original)
Returns:
A String.
Arguments:
Name Description
Considerations:
Query
"hello"
Rows: 1
269
clocks, and truncation.
◦ selecting parts from a temporal value (such as selecting the Date from a DateTime); the
extractors — groups of components which can be selected — are:
▪ date — contains all components for a Date (conceptually year, month and day).
▪ time — contains all components for a Time (hour, minute, second, and sub-seconds; namely
millisecond, microsecond and nanosecond). If the type being created and the type from which
the time component is being selected both contain timezone (and a timezone is not explicitly
specified) the timezone is also selected.
▪ datetime — selects all components, and is useful for overriding specific components.
Analogously to time, if the type being created and the type from which the time component is
being selected both contain timezone (and a timezone is not explicitly specified) the timezone is
also selected.
◦ In effect, this allows for the conversion between different temporal types, and allowing for
'missing' components to be specified.
Creating a X X X
calendar-based
(Year-Month-Day)
value
Creating a week- X X X
based (Year-
Week-Day) value
Creating a quarter- X X X
based (Year-
Quarter-Day) value
Creating an ordinal X X X
(Year-Day) value
270
Function Date Time LocalTime DateTime LocalDateTime
Creating a value X X
from time
components
Creating a value X X X X X
from other
temporal values
using extractors
(i.e. converting
between different
types)
Creating a value X X X X X
from a string
Creating a value X
from a timestamp
All the temporal instant types — including those that do not contain time zone
information support such as Date, LocalTime and DateTime — allow for a time zone to
specified for the functions that retrieve the current instant. This allows for the retrieval of
the current instant in the specified time zone.
• transaction: The same instant is produced for each invocation within the same transaction. A different
time may be produced for different transactions.
• statement: The same instant is produced for each invocation within the same statement. A different
time may be produced for different statements within the same transaction.
• realtime: The instant produced will be the live clock of the system.
The following table lists the different sub-functions for specifying the clock to be used when creating the
current temporal instant value:
271
Truncating temporal values
A temporal instant value can be created by truncating another temporal instant value at the nearest
preceding point in time at a specified component boundary (namely, a truncation unit). A temporal instant
value created in this way will have all components which are less significant than the specified truncation
unit set to their default values.
It is possible to supplement the truncated value by providing a map containing components which are less
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components.
• millennium: Select the temporal instant corresponding to the millenium of the given instant.
• century: Select the temporal instant corresponding to the century of the given instant.
• decade: Select the temporal instant corresponding to the decade of the given instant.
• year: Select the temporal instant corresponding to the year of the given instant.
• weekYear: Select the temporal instant corresponding to the first day of the first week of the week-year
of the given instant.
• quarter: Select the temporal instant corresponding to the quarter of the year of the given instant.
• month: Select the temporal instant corresponding to the month of the given instant.
• week: Select the temporal instant corresponding to the week of the given instant.
• day: Select the temporal instant corresponding to the month of the given instant.
• hour: Select the temporal instant corresponding to the hour of the given instant.
• minute: Select the temporal instant corresponding to the minute of the given instant.
• second: Select the temporal instant corresponding to the second of the given instant.
• millisecond: Select the temporal instant corresponding to the millisecond of the given instant.
• microsecond: Select the temporal instant corresponding to the microsecond of the given instant.
The following table lists the supported truncation units and the corresponding sub-functions:
272
Truncation unit Date Time LocalTime DateTime LocalDateTime
• Truncating a Date
273
used.
Syntax: date([{timezone}])
Returns:
A Date.
Arguments:
Name Description
Considerations:
Query
currentDate
2021-05-05
Rows: 1
Query
currentDateInLA
2021-05-05
Rows: 1
date.transaction()
date.transaction() returns the current Date value using the transaction clock. This value will be the
same for each invocation within the same transaction. However, a different value may be produced for
different transactions.
Syntax: date.transaction([{timezone}])
274
Returns:
A Date.
Arguments:
Name Description
Query
currentDate
2021-05-05
Rows: 1
date.statement()
date.statement() returns the current Date value using the statement clock. This value will be the same for
each invocation within the same statement. However, a different value may be produced for different
statements within the same transaction.
Syntax: date.statement([{timezone}])
Returns:
A Date.
Arguments:
Name Description
Query
currentDate
2021-05-05
Rows: 1
275
date.realtime()
date.realtime() returns the current Date value using the realtime clock. This value will be the live clock of
the system.
Syntax: date.realtime([{timezone}])
Returns:
A Date.
Arguments:
Name Description
Query
currentDate
2021-05-05
Rows: 1
Query
currentDateInLA
2021-05-05
Rows: 1
Returns:
A Date.
Arguments:
276
Name Description
Considerations:
Query
UNWIND [
date({year:1984, month:10, day:11}),
date({year:1984, month:10}),
date({year:1984})
] as theDate
RETURN theDate
theDate
1984-10-11
1984-10-01
1984-01-01
Rows: 3
Returns:
A Date.
Arguments:
Name Description
277
Name Description
Considerations:
Query
UNWIND [
date({year:1984, week:10, dayOfWeek:3}),
date({year:1984, week:10}),
date({year:1984})
] as theDate
RETURN theDate
theDate
1984-03-07
1984-03-05
1984-01-01
Rows: 3
Returns:
A Date.
Arguments:
Name Description
278
Name Description
Considerations:
Query
UNWIND [
date({year:1984, quarter:3, dayOfQuarter: 45}),
date({year:1984, quarter:3}),
date({year:1984})
] as theDate
RETURN theDate
theDate
1984-08-14
1984-07-01
1984-01-01
Rows: 3
Returns:
A Date.
Arguments:
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
279
The ordinal day of the year component will default to 1 if ordinalDay is omitted.
Query
UNWIND [
date({year:1984, ordinalDay:202}),
date({year:1984})
] as theDate
RETURN theDate
theDate
1984-07-20
1984-01-01
Rows: 2
Syntax: date(temporalValue)
Returns:
A Date.
Arguments:
Name Description
Considerations:
temporalValue must denote a valid date; i.e. a temporalValue denoting 30 February 2001 is invalid.
Query
UNWIND [
date('2015-07-21'),
date('2015-07'),
date('201507'),
date('2015-W30-2'),
date('2015202'),
date('2015')
] as theDate
RETURN theDate
280
Table 330. Result
theDate
2015-07-21
2015-07-01
2015-07-01
2015-07-21
2015-07-21
2015-01-01
Rows: 6
Syntax: date({date [, year, month, day, week, dayOfWeek, quarter, dayOfQuarter, ordinalDay]})
Returns:
A Date.
Arguments:
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
281
If any of the optional parameters are provided, these will override the corresponding components of date.
Query
UNWIND [
date({year:1984, month:11, day:11}),
localdatetime({year:1984, month:11, day:11, hour:12, minute:31, second:14}),
datetime({year:1984, month:11, day:11, hour:12, timezone: '+01:00'})
] as dd
RETURN date({date: dd}) AS dateOnly,
date({date: dd, day: 28}) AS dateDay
dateOnly dateDay
1984-11-11 1984-11-28
1984-11-11 1984-11-28
1984-11-11 1984-11-28
Rows: 3
Truncating a Date
date.truncate() returns the Date value obtained by truncating a specified temporal instant value at the
nearest preceding point in time at the specified component boundary (which is denoted by the truncation
unit passed as a parameter to the function). In other words, the Date returned will have all components
that are less significant than the specified truncation unit set to their default values.
It is possible to supplement the truncated value by providing a map containing components which are less
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components. For example, day — with some value
x — may be provided when the truncation unit is year in order to ensure the returned value has the day set
to x instead of the default day (which is 1).
Returns:
A Date.
Arguments:
Name Description
282
Name Description
Considerations:
Any component that is provided in mapOfComponents must be less significant than unit; i.e. if unit is 'day', mapOfComponents
cannot contain information pertaining to a month.
Any component that is not contained in mapOfComponents and which is less significant than unit will be set to its minimal
value.
If mapOfComponents is not provided, all components of the returned value which are less significant than unit will be set to
their default values.
If temporalInstantValue is not provided, it will be set to the current date, i.e. date.truncate(unit) is equivalent of
date.truncate(unit, date()).
Query
Rows: 1
283
• Creating a DateTime from a timestamp
• Truncating a DateTime
Syntax: datetime([{timezone}])
Returns:
A DateTime.
Arguments:
Name Description
Considerations:
Query
The current date and time using the local time zone is returned.
currentDateTime
2021-05-05T08:11:18.228Z
Rows: 1
Query
currentDateTimeInLA
2021-05-05T01:11:18.238-07:00[America/Los_Angeles]
Rows: 1
284
datetime.transaction()
datetime.transaction() returns the current DateTime value using the transaction clock. This value will
be the same for each invocation within the same transaction. However, a different value may be produced
for different transactions.
Syntax: datetime.transaction([{timezone}])
Returns:
A DateTime.
Arguments:
Name Description
Query
currentDateTime
2021-05-05T08:11:18.249Z
Rows: 1
Query
currentDateTimeInLA
2021-05-05T01:11:18.258-07:00[America/Los_Angeles]
Rows: 1
datetime.statement()
datetime.statement() returns the current DateTime value using the statement clock. This value will be the
same for each invocation within the same statement. However, a different value may be produced for
different statements within the same transaction.
Syntax: datetime.statement([{timezone}])
Returns:
A DateTime.
285
Arguments:
Name Description
Query
currentDateTime
2021-05-05T08:11:18.269Z
Rows: 1
datetime.realtime()
datetime.realtime() returns the current DateTime value using the realtime clock. This value will be the
live clock of the system.
Syntax: datetime.realtime([{timezone}])
Returns:
A DateTime.
Arguments:
Name Description
Query
currentDateTime
2021-05-05T08:11:18.288331Z
Rows: 1
286
Returns:
A DateTime.
Arguments:
Name Description
Considerations:
The timezone component will default to the configured default time zone if timezone is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, month, day, hour, minute, and second may be omitted; i.e. it is possible to
specify only year, month and day, but specifying year, month, day and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
287
Query
UNWIND [
datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14, millisecond: 123, microsecond:
456, nanosecond: 789}),
datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14, millisecond: 645, timezone:
'+01:00'}),
datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14, nanosecond: 645876123, timezone:
'Europe/Stockholm'}),
datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14, timezone: '+01:00'}),
datetime({year:1984, month:10, day:11, hour:12, minute:31, second:14}),
datetime({year:1984, month:10, day:11, hour:12, minute:31, timezone: 'Europe/Stockholm'}),
datetime({year:1984, month:10, day:11, hour:12, timezone: '+01:00'}),
datetime({year:1984, month:10, day:11, timezone: 'Europe/Stockholm'})
] as theDate
RETURN theDate
theDate
1984-10-11T12:31:14.123456789Z
1984-10-11T12:31:14.645+01:00
1984-10-11T12:31:14.645876123+01:00[Europe/Stockholm]
1984-10-11T12:31:14+01:00
1984-10-11T12:31:14Z
1984-10-11T12:31+01:00[Europe/Stockholm]
1984-10-11T12:00+01:00
1984-10-11T00:00+01:00[Europe/Stockholm]
Rows: 8
Returns:
A DateTime.
Arguments:
Name Description
288
Name Description
Considerations:
The timezone component will default to the configured default time zone if timezone is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, week, dayOfWeek, hour, minute, and second may be omitted; i.e. it is possible
to specify only year, week and dayOfWeek, but specifying year, week, dayOfWeek and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
289
Query
UNWIND [
datetime({year:1984, week:10, dayOfWeek:3, hour:12, minute:31, second:14, millisecond: 645}),
datetime({year:1984, week:10, dayOfWeek:3, hour:12, minute:31, second:14, microsecond: 645876, timezone:
'+01:00'}),
datetime({year:1984, week:10, dayOfWeek:3, hour:12, minute:31, second:14, nanosecond: 645876123,
timezone: 'Europe/Stockholm'}),
datetime({year:1984, week:10, dayOfWeek:3, hour:12, minute:31, second:14, timezone:
'Europe/Stockholm'}),
datetime({year:1984, week:10, dayOfWeek:3, hour:12, minute:31, second:14}),
datetime({year:1984, week:10, dayOfWeek:3, hour:12, timezone: '+01:00'}),
datetime({year:1984, week:10, dayOfWeek:3, timezone: 'Europe/Stockholm'})
] as theDate
RETURN theDate
theDate
1984-03-07T12:31:14.645Z
1984-03-07T12:31:14.645876+01:00
1984-03-07T12:31:14.645876123+01:00[Europe/Stockholm]
1984-03-07T12:31:14+01:00[Europe/Stockholm]
1984-03-07T12:31:14Z
1984-03-07T12:00+01:00
1984-03-07T00:00+01:00[Europe/Stockholm]
Rows: 7
Returns:
A DateTime.
Arguments:
Name Description
290
Name Description
Considerations:
The timezone component will default to the configured default time zone if timezone is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, quarter, dayOfQuarter, hour, minute, and second may be omitted; i.e. it is
possible to specify only year, quarter and dayOfQuarter, but specifying year, quarter, dayOfQuarter and minute is not
permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
UNWIND [
datetime({year:1984, quarter:3, dayOfQuarter: 45, hour:12, minute:31, second:14, microsecond: 645876}),
datetime({year:1984, quarter:3, dayOfQuarter: 45, hour:12, minute:31, second:14, timezone: '+01:00'}),
datetime({year:1984, quarter:3, dayOfQuarter: 45, hour:12, timezone: 'Europe/Stockholm'}),
datetime({year:1984, quarter:3, dayOfQuarter: 45})
] as theDate
RETURN theDate
theDate
1984-08-14T12:31:14.645876Z
291
theDate
1984-08-14T12:31:14+01:00
1984-08-14T12:00+02:00[Europe/Stockholm]
1984-08-14T00:00Z
Rows: 4
Returns:
A DateTime.
Arguments:
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
The ordinal day of the year component will default to 1 if ordinalDay is omitted.
292
The hour component will default to 0 if hour is omitted.
The timezone component will default to the configured default time zone if timezone is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, ordinalDay, hour, minute, and second may be omitted; i.e. it is possible to
specify only year and ordinalDay, but specifying year, ordinalDay and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
UNWIND [
datetime({year:1984, ordinalDay:202, hour:12, minute:31, second:14, millisecond: 645}),
datetime({year:1984, ordinalDay:202, hour:12, minute:31, second:14, timezone: '+01:00'}),
datetime({year:1984, ordinalDay:202, timezone: 'Europe/Stockholm'}),
datetime({year:1984, ordinalDay:202})
] as theDate
RETURN theDate
theDate
1984-07-20T12:31:14.645Z
1984-07-20T12:31:14+01:00
1984-07-20T00:00+02:00[Europe/Stockholm]
1984-07-20T00:00Z
Rows: 4
Syntax: datetime(temporalValue)
Returns:
A DateTime.
Arguments:
Name Description
Considerations:
293
temporalValue must comply with the format defined for dates, times and time zones.
The timezone component will default to the configured default time zone if it is omitted.
temporalValue must denote a valid date and time; i.e. a temporalValue denoting 30 February 2001 is invalid.
Query
UNWIND [
datetime('2015-07-21T21:40:32.142+0100'),
datetime('2015-W30-2T214032.142Z'),
datetime('2015T214032-0100'),
datetime('20150721T21:40-01:30'),
datetime('2015-W30T2140-02'),
datetime('2015202T21+18:00'),
datetime('2015-07-21T21:40:32.142[Europe/London]'),
datetime('2015-07-21T21:40:32.142-04[America/New_York]')
] AS theDate
RETURN theDate
theDate
2015-07-21T21:40:32.142+01:00
2015-07-21T21:40:32.142Z
2015-01-01T21:40:32-01:00
2015-07-21T21:40-01:30
2015-07-20T21:40-02:00
2015-07-21T21:00+18:00
2015-07-21T21:40:32.142+01:00[Europe/London]
2015-07-21T21:40:32.142-04:00[America/New_York]
Rows: 8
Returns:
A DateTime.
Arguments:
Name Description
294
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
If any of the optional parameters are provided, these will override the corresponding components of datetime, date and/or
time.
Selecting a Time or DateTime value as the time component also selects its time zone. If a LocalTime or LocalDateTime is
selected instead, the default time zone is used. In any case, the time zone can be overridden explicitly.
Selecting a DateTime as the datetime component and overwriting the time zone will adjust the local time to keep the same
point in time.
295
Selecting a DateTime or Time as the time component and overwriting the time zone will adjust the local time to keep the
same point in time.
The following query shows the various usages of datetime({date [, year, …, timezone]})
Query
Rows: 1
The following query shows the various usages of datetime({time [, year, …, timezone]})
Query
Rows: 1
The following query shows the various usages of datetime({date, time [, year, …, timezone]}); i.e.
combining a Date and a Time value to create a single DateTime value:
Query
296
dateTime dateTimeTimezone dateTimeDDSS dateTimeDDSSTimezone
Rows: 1
The following query shows the various usages of datetime({datetime [, year, …, timezone]})
Query
Rows: 1
Conversions to other temporal instant types from UNIX epoch representations can be achieved by
transforming a DateTime value to one of these types.
Returns:
A DateTime.
Arguments:
Name Description
Considerations:
297
Query
theDate
2021-05-05T08:11:18.000000023Z
Rows: 1
Query
theDate
1983-06-18T15:15Z
Rows: 1
Truncating a DateTime
datetime.truncate() returns the DateTime value obtained by truncating a specified temporal instant value
at the nearest preceding point in time at the specified component boundary (which is denoted by the
truncation unit passed as a parameter to the function). In other words, the DateTime returned will have all
components that are less significant than the specified truncation unit set to their default values.
It is possible to supplement the truncated value by providing a map containing components which are less
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components. For example, day — with some value
x — may be provided when the truncation unit is year in order to ensure the returned value has the day set
to x instead of the default day (which is 1).
Returns:
A DateTime.
Arguments:
Name Description
298
Name Description
Considerations:
temporalInstantValue cannot be a Date value if unit is one of {hour, minute, second, millisecond, microsecond}.
The time zone of temporalInstantValue may be overridden; for example, datetime.truncate('minute', input,
{timezone:'+0200'}).
If temporalInstantValue is one of {Time, DateTime} — a value with a time zone — and the time zone is overridden, no time
conversion occurs.
If temporalInstantValue is one of {LocalDateTime, Date} — a value without a time zone — and the time zone is not
overridden, the configured default time zone will be used.
Any component that is provided in mapOfComponents must be less significant than unit; i.e. if unit is 'day', mapOfComponents
cannot contain information pertaining to a month.
Any component that is not contained in mapOfComponents and which is less significant than unit will be set to its minimal
value.
If mapOfComponents is not provided, all components of the returned value which are less significant than unit will be set to
their default values.
If temporalInstantValue is not provided, it will be set to the current date, time and timezone, i.e. datetime.truncate(unit) is
equivalent of datetime.truncate(unit, datetime()).
Query
Rows: 1
299
• Creating a calendar (Year-Month-Day) LocalDateTime
• Truncating a LocalDateTime
Syntax: localdatetime([{timezone}])
Returns:
A LocalDateTime.
Arguments:
Name Description
Considerations:
Query
The current local date and time (i.e. in the local time zone) is returned.
now
2021-05-05T08:11:18.655
Rows: 1
Query
300
Table 352. Result
now
2021-05-05T01:11:18.664
Rows: 1
localdatetime.transaction()
localdatetime.transaction() returns the current LocalDateTime value using the transaction clock. This
value will be the same for each invocation within the same transaction. However, a different value may be
produced for different transactions.
Syntax: localdatetime.transaction([{timezone}])
Returns:
A LocalDateTime.
Arguments:
Name Description
Query
now
2021-05-05T08:11:18.674
Rows: 1
localdatetime.statement()
localdatetime.statement() returns the current LocalDateTime value using the statement clock. This value
will be the same for each invocation within the same statement. However, a different value may be
produced for different statements within the same transaction.
Syntax: localdatetime.statement([{timezone}])
Returns:
A LocalDateTime.
Arguments:
301
Name Description
Query
now
2021-05-05T08:11:18.686
Rows: 1
localdatetime.realtime()
localdatetime.realtime() returns the current LocalDateTime value using the realtime clock. This value
will be the live clock of the system.
Syntax: localdatetime.realtime([{timezone}])
Returns:
A LocalDateTime.
Arguments:
Name Description
Query
now
2021-05-05T08:11:18.712368
Rows: 1
Query
nowInLA
2021-05-05T01:11:18.731573
302
nowInLA
Rows: 1
Returns:
A LocalDateTime.
Arguments:
Name Description
Considerations:
303
The second component will default to 0 if second is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, month, day, hour, minute, and second may be omitted; i.e. it is possible to
specify only year, month and day, but specifying year, month, day and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
theDate
1984-10-11T12:31:14.123456789
Rows: 1
Returns:
A LocalDateTime.
Arguments:
Name Description
304
Name Description
Considerations:
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, week, dayOfWeek, hour, minute, and second may be omitted; i.e. it is possible
to specify only year, week and dayOfWeek, but specifying year, week, dayOfWeek and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
theDate
1984-03-07T12:31:14.645
Rows: 1
Returns:
305
A LocalDateTime.
Arguments:
Name Description
Considerations:
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, quarter, dayOfQuarter, hour, minute, and second may be omitted; i.e. it is
possible to specify only year, quarter and dayOfQuarter, but specifying year, quarter, dayOfQuarter and minute is not
permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
306
Query
theDate
1984-08-14T12:31:14.645876123
Rows: 1
Returns:
A LocalDateTime.
Arguments:
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
307
The ordinal day of the year component will default to 1 if ordinalDay is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set year, ordinalDay, hour, minute, and second may be omitted; i.e. it is possible to
specify only year and ordinalDay, but specifying year, ordinalDay and minute is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
theDate
1984-07-20T12:31:14.645876
Rows: 1
Syntax: localdatetime(temporalValue)
Returns:
A LocalDateTime.
Arguments:
Name Description
Considerations:
temporalValue must comply with the format defined for dates and times.
temporalValue must denote a valid date and time; i.e. a temporalValue denoting 30 February 2001 is invalid.
308
Query
UNWIND [
localdatetime('2015-07-21T21:40:32.142'),
localdatetime('2015-W30-2T214032.142'),
localdatetime('2015-202T21:40:32'),
localdatetime('2015202T21')
] AS theDate
RETURN theDate
theDate
2015-07-21T21:40:32.142
2015-07-21T21:40:32.142
2015-07-21T21:40:32
2015-07-21T21:00
Rows: 4
Returns:
A LocalDateTime.
Arguments:
Name Description
309
Name Description
ordinalDay An integer between 1 and 366 that specifies the ordinal day
of the year.
Considerations:
If any of the optional parameters are provided, these will override the corresponding components of datetime, date and/or
time.
The following query shows the various usages of localdatetime({date [, year, …, nanosecond]})
Query
dateHHMMSS dateDDHHMMSS
1984-10-11T10:10:10 1984-10-28T10:10:10
Rows: 1
The following query shows the various usages of localdatetime({time [, year, …, nanosecond]})
310
Query
YYYYMMDDTime YYYYMMDDTimeSS
1984-10-11T12:31:14.645876 1984-10-11T12:31:42.645876
Rows: 1
The following query shows the various usages of localdatetime({date, time [, year, …,
nanosecond]}); i.e. combining a Date and a Time value to create a single LocalDateTime value:
Query
dateTime dateTimeDDSS
1984-10-11T12:31:14.645876 1984-10-28T12:31:42.645876
Rows: 1
The following query shows the various usages of localdatetime({datetime [, year, …, nanosecond]})
Query
dateTime dateTimeDDSS
1984-10-11T12:00 1984-10-28T12:00:42
Rows: 1
Truncating a LocalDateTime
localdatetime.truncate() returns the LocalDateTime value obtained by truncating a specified temporal
instant value at the nearest preceding point in time at the specified component boundary (which is
denoted by the truncation unit passed as a parameter to the function). In other words, the LocalDateTime
returned will have all components that are less significant than the specified truncation unit set to their
default values.
It is possible to supplement the truncated value by providing a map containing components which are less
311
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components. For example, day — with some value
x — may be provided when the truncation unit is year in order to ensure the returned value has the day set
to x instead of the default day (which is 1).
Returns:
A LocalDateTime.
Arguments:
Name Description
Considerations:
temporalInstantValue cannot be a Date value if unit is one of {hour, minute, second, millisecond, microsecond}.
Any component that is provided in mapOfComponents must be less significant than unit; i.e. if unit is 'day', mapOfComponents
cannot contain information pertaining to a month.
Any component that is not contained in mapOfComponents and which is less significant than unit will be set to its minimal
value.
If mapOfComponents is not provided, all components of the returned value which are less significant than unit will be set to
their default values.
If temporalInstantValue is not provided, it will be set to the current date and time, i.e. localdatetime.truncate(unit) is
equivalent of localdatetime.truncate(unit, localdatetime()).
Query
312
truncMillenium truncYear truncMonth truncDay truncHour truncSecond
Rows: 1
• Creating a LocalTime
• Truncating a LocalTime
Syntax: localtime([{timezone}])
Returns:
A LocalTime.
Arguments:
Name Description
Considerations:
Query
The current local time (i.e. in the local time zone) is returned.
313
now
08:11:18.941
Rows: 1
Query
nowInLA
01:11:18.950
Rows: 1
localtime.transaction()
localtime.transaction() returns the current LocalTime value using the transaction clock. This value will
be the same for each invocation within the same transaction. However, a different value may be produced
for different transactions.
Syntax: localtime.transaction([{timezone}])
Returns:
A LocalTime.
Arguments:
Name Description
Query
now
08:11:18.960
Rows: 1
localtime.statement()
localtime.statement() returns the current LocalTime value using the statement clock. This value will be
the same for each invocation within the same statement. However, a different value may be produced for
314
different statements within the same transaction.
Syntax: localtime.statement([{timezone}])
Returns:
A LocalTime.
Arguments:
Name Description
Query
now
08:11:18.969
Rows: 1
Query
nowInLA
01:11:18.978
Rows: 1
localtime.realtime()
localtime.realtime() returns the current LocalTime value using the realtime clock. This value will be the
live clock of the system.
Syntax: localtime.realtime([{timezone}])
Returns:
A LocalTime.
Arguments:
Name Description
315
Query
now
08:11:18.996236
Rows: 1
Creating a LocalTime
localtime() returns a LocalTime value with the specified hour, minute, second, millisecond, microsecond
and nanosecond component values.
Returns:
A LocalTime.
Arguments:
Name Description
Considerations:
316
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set hour, minute, and second may be omitted; i.e. it is possible to specify only hour
and minute, but specifying hour and second is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
UNWIND [
localtime({hour:12, minute:31, second:14, nanosecond: 789, millisecond: 123, microsecond: 456}),
localtime({hour:12, minute:31, second:14}),
localtime({hour:12})
] as theTime
RETURN theTime
theTime
12:31:14.123456789
12:31:14
12:00
Rows: 3
Syntax: localtime(temporalValue)
Returns:
A LocalTime.
Arguments:
Name Description
Considerations:
temporalValue must denote a valid time; i.e. a temporalValue denoting 13:46:64 is invalid.
317
Query
UNWIND [
localtime('21:40:32.142'),
localtime('214032.142'),
localtime('21:40'),
localtime('21')
] AS theTime
RETURN theTime
theTime
21:40:32.142
21:40:32.142
21:40
21:00
Rows: 4
Returns:
A LocalTime.
Arguments:
Name Description
318
Name Description
Considerations:
If any of the optional parameters are provided, these will override the corresponding components of time.
Query
timeOnly timeSS
12:31:14.645876 12:31:42.645876
Rows: 1
Truncating a LocalTime
localtime.truncate() returns the LocalTime value obtained by truncating a specified temporal instant
value at the nearest preceding point in time at the specified component boundary (which is denoted by the
truncation unit passed as a parameter to the function). In other words, the LocalTime returned will have all
components that are less significant than the specified truncation unit set to their default values.
It is possible to supplement the truncated value by providing a map containing components which are less
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components. For example, minute — with some value
x — may be provided when the truncation unit is hour in order to ensure the returned value has the minute
set to x instead of the default minute (which is 1).
Returns:
A LocalTime.
Arguments:
Name Description
319
Name Description
Considerations:
Truncating time to day — i.e. unit is 'day' — is supported, and yields midnight at the start of the day (00:00), regardless of
the value of temporalInstantValue. However, the time zone of temporalInstantValue is retained.
Any component that is provided in mapOfComponents must be less significant than unit; i.e. if unit is 'second',
mapOfComponents cannot contain information pertaining to a minute.
Any component that is not contained in mapOfComponents and which is less significant than unit will be set to its minimal
value.
If mapOfComponents is not provided, all components of the returned value which are less significant than unit will be set to
their default values.
If temporalInstantValue is not provided, it will be set to the current time, i.e. localtime.truncate(unit) is equivalent of
localtime.truncate(unit, localtime()).
Query
Rows: 1
• Creating a Time
• Truncating a Time
320
used.
Syntax: time([{timezone}])
Returns:
A Time.
Arguments:
Name Description
Considerations:
Query
The current time of day using the local time zone is returned.
currentTime
08:11:19.089Z
Rows: 1
Query
currentTimeInLA
01:11:19.098-07:00
Rows: 1
time.transaction()
time.transaction() returns the current Time value using the transaction clock. This value will be the
same for each invocation within the same transaction. However, a different value may be produced for
different transactions.
Syntax: time.transaction([{timezone}])
321
Returns:
A Time.
Arguments:
Name Description
Query
currentTime
08:11:19.109Z
Rows: 1
time.statement()
time.statement() returns the current Time value using the statement clock. This value will be the same for
each invocation within the same statement. However, a different value may be produced for different
statements within the same transaction.
Syntax: time.statement([{timezone}])
Returns:
A Time.
Arguments:
Name Description
Query
currentTime
08:11:19.118Z
Rows: 1
322
Query
currentTimeInLA
01:11:19.128-07:00
Rows: 1
time.realtime()
time.realtime() returns the current Time value using the realtime clock. This value will be the live clock
of the system.
Syntax: time.realtime([{timezone}])
Returns:
A Time.
Arguments:
Name Description
Query
currentTime
08:11:19.147394Z
Rows: 1
Creating a Time
time() returns a Time value with the specified hour, minute, second, millisecond, microsecond,
nanosecond and timezone component values.
Returns:
A Time.
Arguments:
323
Name Description
Considerations:
The timezone component will default to the configured default time zone if timezone is omitted.
If millisecond, microsecond and nanosecond are given in combination (as part of the same set of parameters), the individual
values must be in the range 0 to 999.
The least significant components in the set hour, minute, and second may be omitted; i.e. it is possible to specify only hour
and minute, but specifying hour and second is not permitted.
One or more of millisecond, microsecond and nanosecond can only be specified as long as second is also specified.
Query
UNWIND [
time({hour:12, minute:31, second:14, millisecond: 123, microsecond: 456, nanosecond: 789}),
time({hour:12, minute:31, second:14, nanosecond: 645876123}),
time({hour:12, minute:31, second:14, microsecond: 645876, timezone: '+01:00'}),
time({hour:12, minute:31, timezone: '+01:00'}),
time({hour:12, timezone: '+01:00'})
] AS theTime
RETURN theTime
theTime
12:31:14.123456789Z
12:31:14.645876123Z
324
theTime
12:31:14.645876+01:00
12:31+01:00
12:00+01:00
Rows: 5
Syntax: time(temporalValue)
Returns:
A Time.
Arguments:
Name Description
Considerations:
temporalValue must comply with the format defined for times and time zones.
The timezone component will default to the configured default time zone if it is omitted.
temporalValue must denote a valid time; i.e. a temporalValue denoting 15:67 is invalid.
Query
UNWIND [
time('21:40:32.142+0100'),
time('214032.142Z'),
time('21:40:32+01:00'),
time('214032-0100'),
time('21:40-01:30'),
time('2140-00:00'),
time('2140-02'),
time('22+18:00')
] AS theTime
RETURN theTime
theTime
21:40:32.142+01:00
21:40:32.142Z
21:40:32+01:00
325
theTime
21:40:32-01:00
21:40-01:30
21:40Z
21:40-02:00
22:00+18:00
Rows: 8
Returns:
A Time.
Arguments:
Name Description
Considerations:
If any of the optional parameters are provided, these will override the corresponding components of time.
326
Selecting a Time or DateTime value as the time component also selects its time zone. If a LocalTime or LocalDateTime is
selected instead, the default time zone is used. In any case, the time zone can be overridden explicitly.
Selecting a DateTime or Time as the time component and overwriting the time zone will adjust the local time to keep the
same point in time.
Query
Rows: 1
Truncating a Time
time.truncate() returns the Time value obtained by truncating a specified temporal instant value at the
nearest preceding point in time at the specified component boundary (which is denoted by the truncation
unit passed as a parameter to the function). In other words, the Time returned will have all components
that are less significant than the specified truncation unit set to their default values.
It is possible to supplement the truncated value by providing a map containing components which are less
significant than the truncation unit. This will have the effect of overriding the default values which would
otherwise have been set for these less significant components. For example, minute — with some value
x — may be provided when the truncation unit is hour in order to ensure the returned value has the minute
set to x instead of the default minute (which is 1).
Returns:
A Time.
Arguments:
Name Description
327
Considerations:
Truncating time to day — i.e. unit is 'day' — is supported, and yields midnight at the start of the day (00:00), regardless of
the value of temporalInstantValue. However, the time zone of temporalInstantValue is retained.
The time zone of temporalInstantValue may be overridden; for example, time.truncate('minute', input,
{timezone:'+0200'}).
If temporalInstantValue is one of {Time, DateTime} — a value with a time zone — and the time zone is overridden, no time
conversion occurs.
If temporalInstantValue is one of {LocalTime, LocalDateTime, Date} — a value without a time zone — and the time zone is
not overridden, the configured default time zone will be used.
Any component that is provided in mapOfComponents must be less significant than unit; i.e. if unit is 'second',
mapOfComponents cannot contain information pertaining to a minute.
Any component that is not contained in mapOfComponents and which is less significant than unit will be set to its minimal
value.
If mapOfComponents is not provided, all components of the returned value which are less significant than unit will be set to
their default values.
If temporalInstantValue is not provided, it will be set to the current time and timezone, i.e. time.truncate(unit) is
equivalent of time.truncate(unit, time()).
Query
Rows: 1
duration():
328
• Computing the Duration between two temporal instants
Information regarding specifying and accessing components of a Duration value can be found here.
• years
• quarters
• months
• weeks
• days
• hours
• minutes
• seconds
• milliseconds
• microseconds
• nanoseconds
Syntax: duration([ {years, quarters, months, weeks, days, hours, minutes, seconds,
milliseconds, microseconds, nanoseconds} ])
Returns:
A Duration.
Arguments:
Name Description
329
Name Description
Considerations:
At least one parameter must be provided (duration() and duration({}) are invalid).
It is possible to have a Duration where the amount of a smaller unit (e.g. seconds) exceeds the threshold of a larger unit (e.g.
days).
Query
UNWIND [
duration({days: 14, hours:16, minutes: 12}),
duration({months: 5, days: 1.5}),
duration({months: 0.75}),
duration({weeks: 2.5}),
duration({minutes: 1.5, seconds: 1, milliseconds: 123, microseconds: 456, nanoseconds: 789}),
duration({minutes: 1.5, seconds: 1, nanoseconds: 123456789})
] AS aDuration
RETURN aDuration
aDuration
P14DT16H12M
P5M1DT12H
P22DT19H51M49.5S
P17DT12H
PT1M31.123456789S
PT1M31.123456789S
Rows: 6
Syntax: duration(temporalAmount)
Returns:
A Duration.
330
Arguments:
Name Description
Considerations:
temporalAmount must comply with either the unit based form or date-and-time based form defined for Durations.
Query
UNWIND [
duration("P14DT16H12M"),
duration("P5M1.5D"),
duration("P0.75M"),
duration("PT0.75M"),
duration("P2012-02-02T14:37:21.545")
] AS aDuration
RETURN aDuration
aDuration
P14DT16H12M
P5M1DT12H
P22DT19H51M49.5S
PT45S
P2012Y2M2DT14H37M21.545S
Rows: 5
• duration.between(a, b): Computes the difference in multiple components between instant a and
instant b. This captures month, days, seconds and sub-seconds differences separately.
• duration.inMonths(a, b): Computes the difference in whole months (or quarters or years) between
instant a and instant b. This captures the difference as the total number of months. Any difference
smaller than a whole month is disregarded.
• duration.inDays(a, b): Computes the difference in whole days (or weeks) between instant a and
instant b. This captures the difference as the total number of days. Any difference smaller than a whole
day is disregarded.
• duration.inSeconds(a, b): Computes the difference in seconds (and fractions of seconds, or minutes
or hours) between instant a and instant b. This captures the difference as the total number of seconds.
331
duration.between()
duration.between() returns the Duration value equal to the difference between the two given instants.
Returns:
A Duration.
Arguments:
Name Description
Considerations:
If instant2 occurs earlier than instant1, the resulting Duration will be negative.
If instant1 has a time component and instant2 does not, the time component of instant2 is assumed to be midnight, and
vice versa.
If instant1 has a time zone component and instant2 does not, the time zone component of instant2 is assumed to be the
same as that of instant1, and vice versa.
If instant1 has a date component and instant2 does not, the date component of instant2 is assumed to be the same as that
of instant1, and vice versa.
Query
UNWIND [
duration.between(date("1984-10-11"), date("1985-11-25")),
duration.between(date("1985-11-25"), date("1984-10-11")),
duration.between(date("1984-10-11"), datetime("1984-10-12T21:40:32.142+0100")),
duration.between(date("2015-06-24"), localtime("14:30")),
duration.between(localtime("14:30"), time("16:30+0100")),
duration.between(localdatetime("2015-07-21T21:40:32.142"), localdatetime("2016-07-21T21:45:22.142")),
duration.between(datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/Stockholm'}),
datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/London'}))
] AS aDuration
RETURN aDuration
aDuration
P1Y1M14D
P-1Y-1M-14D
P1DT21H40M32.142S
PT14H30M
PT2H
332
aDuration
P1YT4M50S
PT1H
Rows: 7
duration.inMonths()
duration.inMonths() returns the Duration value equal to the difference in whole months, quarters or years
between the two given instants.
Returns:
A Duration.
Arguments:
Name Description
Considerations:
If instant2 occurs earlier than instant1, the resulting Duration will be negative.
If instant1 has a time component and instant2 does not, the time component of instant2 is assumed to be midnight, and
vice versa.
If instant1 has a time zone component and instant2 does not, the time zone component of instant2 is assumed to be the
same as that of instant1, and vice versa.
If instant1 has a date component and instant2 does not, the date component of instant2 is assumed to be the same as that
of instant1, and vice versa.
Query
UNWIND [
duration.inMonths(date("1984-10-11"), date("1985-11-25")),
duration.inMonths(date("1985-11-25"), date("1984-10-11")),
duration.inMonths(date("1984-10-11"), datetime("1984-10-12T21:40:32.142+0100")),
duration.inMonths(date("2015-06-24"), localtime("14:30")),
duration.inMonths(localdatetime("2015-07-21T21:40:32.142"), localdatetime("2016-07-21T21:45:22.142")),
duration.inMonths(datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/Stockholm'}),
datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/London'}))
] AS aDuration
RETURN aDuration
333
aDuration
P1Y1M
P-1Y-1M
PT0S
PT0S
P1Y
PT0S
Rows: 6
duration.inDays()
duration.inDays() returns the Duration value equal to the difference in whole days or weeks between the
two given instants.
Returns:
A Duration.
Arguments:
Name Description
Considerations:
If instant2 occurs earlier than instant1, the resulting Duration will be negative.
If instant1 has a time component and instant2 does not, the time component of instant2 is assumed to be midnight, and
vice versa.
If instant1 has a time zone component and instant2 does not, the time zone component of instant2 is assumed to be the
same as that of instant1, and vice versa.
If instant1 has a date component and instant2 does not, the date component of instant2 is assumed to be the same as that
of instant1, and vice versa.
334
Query
UNWIND [
duration.inDays(date("1984-10-11"), date("1985-11-25")),
duration.inDays(date("1985-11-25"), date("1984-10-11")),
duration.inDays(date("1984-10-11"), datetime("1984-10-12T21:40:32.142+0100")),
duration.inDays(date("2015-06-24"), localtime("14:30")),
duration.inDays(localdatetime("2015-07-21T21:40:32.142"), localdatetime("2016-07-21T21:45:22.142")),
duration.inDays(datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/Stockholm'}),
datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/London'}))
] AS aDuration
RETURN aDuration
aDuration
P410D
P-410D
P1D
PT0S
P366D
PT0S
Rows: 6
duration.inSeconds()
duration.inSeconds() returns the Duration value equal to the difference in seconds and fractions of
seconds, or minutes or hours, between the two given instants.
Returns:
A Duration.
Arguments:
Name Description
Considerations:
If instant2 occurs earlier than instant1, the resulting Duration will be negative.
If instant1 has a time component and instant2 does not, the time component of instant2 is assumed to be midnight, and
vice versa.
335
If instant1 has a time zone component and instant2 does not, the time zone component of instant2 is assumed to be the
same as that of instant1, and vice versa.
If instant1 has a date component and instant2 does not, the date component of instant2 is assumed to be the same as that
of instant1, and vice versa.
Query
UNWIND [
duration.inSeconds(date("1984-10-11"), date("1984-10-12")),
duration.inSeconds(date("1984-10-12"), date("1984-10-11")),
duration.inSeconds(date("1984-10-11"), datetime("1984-10-12T01:00:32.142+0100")),
duration.inSeconds(date("2015-06-24"), localtime("14:30")),
duration.inSeconds(datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/Stockholm'}),
datetime({year: 2017, month: 10, day: 29, hour: 0, timezone: 'Europe/London'}))
] AS aDuration
RETURN aDuration
aDuration
PT24H
PT-24H
PT25H32.142S
PT14H30M
PT1H
Rows: 5
Functions:
• distance()
• point() - WGS 84 2D
• point() - WGS 84 3D
• point() - Cartesian 2D
• point() - Cartesian 3D
336
Graph
N0 [
label = "{TrainStation|longitude = 12.56459\lcity = \'Copenhagen\'\llatitude = 55.672874\l}"
]
N0 -> N1 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "TRAVEL_ROUTE\n"
]
N1 [
label = "{Office|longitude = 12.994341\lcity = \'Malmo\'\llatitude = 55.611784\l}"
]
4.11.1. distance()
distance() returns a floating point number representing the geodesic distance between two points in the
same Coordinate Reference System (CRS).
• If the points are in the Cartesian CRS (2D or 3D), then the units of the returned distance will be the
same as the units of the points, calculated using Pythagoras' theorem.
• If the points are in the WGS-84 CRS (2D), then the units of the returned distance will be meters, based
on the haversine formula over a spherical earth approximation.
• If the points are in the WGS-84 CRS (3D), then the units of the returned distance will be meters.
◦ The distance is calculated in two steps.
▪ First, a haversine formula over a spherical earth is used, at the average height of the two
points.
▪ To account for the difference in height, Pythagoras' theorem is used, combining the previously
calculated spherical distance with the height difference.
◦ This formula works well for points close to the earth’s surface; for instance, it is well-suited for
calculating the distance of an airplane flight. It is less suitable for greater heights, however, such as
when calculating the distance between two satellites.
Returns:
A Float.
Arguments:
Name Description
Considerations:
distance(null, null), distance(null, point2) and distance(point1, null) all return null.
337
Attempting to use points with different Coordinate Reference Systems (such as WGS 84 2D and WGS 84 3D) will return
null.
Query
WITH point({x: 2.3, y: 4.5, crs: 'cartesian'}) AS p1, point({x: 1.1, y: 5.4, crs: 'cartesian'}) AS p2
RETURN distance(p1,p2) AS dist
dist
1.5
Rows: 1
Query
WITH point({longitude: 12.78, latitude: 56.7, height: 100}) as p1, point({latitude: 56.71, longitude:
12.79, height: 100}) as p2
RETURN distance(p1,p2) as dist
dist
1269.9148706779097
Rows: 1
Query
MATCH (t:TrainStation)-[:TRAVEL_ROUTE]->(o:Office)
WITH point({longitude: t.longitude, latitude: t.latitude}) AS trainPoint, point({longitude: o.longitude,
latitude: o.latitude}) AS officePoint
RETURN round(distance(trainPoint, officePoint)) AS travelDistance
The distance between the train station in Copenhagen and the Neo4j office in Malmo is returned.
travelDistance
27842.0
Rows: 1
Query
338
Table 396. Result
<null>
Rows: 1
Returns:
Arguments:
Name Description
Considerations:
If the coordinates are specified using latitude and longitude, the crs or srid fields are optional and inferred to be 'WGS-84'
(srid=4326).
If the coordinates are specified using x and y, then either the crs or srid field is required if a geographic CRS is desired.
Query
A 2D point with a longitude of 56.7 and a latitude of 12.78 in the WGS 84 CRS is returned.
point
Rows: 1
339
Query
x and y coordinates may be used in the WGS 84 CRS instead of longitude and latitude, respectively,
providing crs is set to 'WGS-84', or srid is set to 4326.
point
Rows: 1
Query
MATCH (p:Office)
RETURN point({longitude: p.longitude, latitude: p.latitude}) AS officePoint
A 2D point representing the coordinates of the city of Malmo in the WGS 84 CRS is returned.
officePoint
Rows: 1
Query
RETURN point(null) AS p
<null>
Rows: 1
Returns:
340
Arguments:
Name Description
Considerations:
If the height/z key and value is not provided, a 2D point in the WGS 84 CRS will be returned.
If the coordinates are specified using latitude and longitude, the crs or srid fields are optional and inferred to be 'WGS-84-
3D' (srid=4979).
If the coordinates are specified using x and y, then either the crs or srid field is required if a geographic CRS is desired.
Query
A 3D point with a longitude of 56.7, a latitude of 12.78 and a height of 8 meters in the WGS 84 CRS is
returned.
point
Rows: 1
Returns:
A 2D point in Cartesian.
341
Arguments:
Name Description
x A numeric expression
y A numeric expression
Considerations:
The crs or srid fields are optional and default to the Cartesian CRS (which means srid:7203).
Query
A 2D point with an x coordinate of 2.3 and a y coordinate of 4.5 in the Cartesian CRS is returned.
point
Rows: 1
Returns:
A 3D point in Cartesian.
Arguments:
Name Description
x A numeric expression
y A numeric expression
z A numeric expression
342
Name Description
Considerations:
If the z key and value is not provided, a 2D point in the Cartesian CRS will be returned.
The crs or srid fields are optional and default to the 3D Cartesian CRS (which means srid:9157).
Query
A 3D point with an x coordinate of 2.3, a y coordinate of 4.5 and a z coordinate of 2 in the Cartesian CRS
is returned.
point
Rows: 1
There are two main types of functions that can be developed and used:
Scalar For each row the function Using UDF Extending Neo4j (UDF)
takes parameters and returns
a result
Aggregating Consumes many rows and Using aggregating UDF Extending Neo4j
produces an aggregated (Aggregating UDF)
result
This example shows how you invoke a user-defined function called join from Cypher.
343
Call a user-defined function
This calls the user-defined function org.neo4j.procedure.example.join().
Query
members
"John,Paul,George,Ringo"
Rows: 1
For developing and deploying user-defined functions in Neo4j, see Extending Neo4j → User-defined
functions.
This example shows how you invoke a user-defined aggregation function called longestString from
Cypher.
Query
MATCH (n:Member)
RETURN org.neo4j.function.example.longestString(n.name) AS member
member
"George"
Rows: 1
The functions described on this page are only useful when run on a query that uses LOAD
CSV. In all other contexts they will always return null.
344
Functions:
• linenumber()
• file()
4.13.1. linenumber()
linenumber() returns the line number that LOAD CSV is currently using.
Syntax: linenumber()
Returns:
An Integer.
Considerations:
null will be returned if this function is called without a LOAD CSV context.
4.13.2. file()
file() returns the absolute path of the file that LOAD CSV is using.
Syntax: file()
Returns:
A String.
Considerations:
null will be returned if this function is called without a LOAD CSV context.
345
Chapter 5. Administration
This chapter explains how to use Cypher to administer Neo4j databases, such as creating
databases, managing indexes and constraints, and managing security.
Neo4j supports the management of multiple databases within the same DBMS. The metadata for these
databases, including the associated security model, is maintained in a special database called the system
database. Most administrative commands must be executed against the system database because they
involve editing the metadata for the entire system. This includes all commands related to managing
multiple databases as well as all commands for defining the security model: users, roles and privileges. The
administrative commands that are specific to the schema of an individual database are still executed
against that specific database. These include index and constraint management commands.
• Databases
◦ Introduction
◦ Listing databases
◦ Creating databases
◦ Stopping databases
◦ Starting databases
◦ Deleting databases
◦ Syntax
◦ Examples
• Constraints
◦ Introduction
◦ Syntax
◦ Examples
• Security
346
◦ Introduction
◦ Syntax summaries
◦ Read privileges
◦ Write privileges
◦ Security of administration
◦ Built-in roles
5.1. Databases
This section explains how to use Cypher to manage Neo4j databases: creating, deleting,
starting and stopping individual databases within a single server.
• Introduction
• Listing databases
• Creating databases
• Stopping databases
• Starting databases
• Deleting databases
5.1.1. Introduction
Neo4j supports the management of multiple databases within the same DBMS. The metadata for these
databases, including the associated security model, is maintained in a special database called the system
database. All multi-database administrative commands must be run against the system database. These
administrative commands are automatically routed to the system database when connected to the DBMS
over Bolt.
347
Command Syntax
Show Database
SHOW { DATABASE name | DATABASES | DEFAULT DATABASE }
[WHERE expression]
Create Database
CREATE DATABASE name [IF NOT EXISTS] [WAIT [n [SEC[OND[S]]]]|NOWAIT]
CREATE [OR REPLACE] DATABASE name [WAIT [n [SEC[OND[S]]]]|NOWAIT]
Stop Database
STOP DATABASE name [WAIT [n [SEC[OND[S]]]]|NOWAIT]
Start Database
START DATABASE name [WAIT [n [SEC[OND[S]]]]|NOWAIT]
Drop Database
DROP DATABASE name [IF EXISTS] [{DUMP|DESTROY} [DATA]] [WAIT [n [SEC[OND[S]]]]|NOWAIT]
All available databases can be seen using the command SHOW DATABASES.
Query
SHOW DATABASES
Rows: 4
348
Note that the results of this command are filtered according to the ACCESS privileges the
user has. However, a user with CREATE/DROP DATABASE or DATABASE MANAGEMENT privileges
can see all databases regardless of their ACCESS privileges. If a user has not been granted
ACCESS privilege to any databases, the command can still be executed but will only return
the system database, which is always visible.
The number of databases can be seen using a count() aggregation with YIELD and RETURN.
Query
count
Rows: 1
A particular database can be seen using the command SHOW DATABASE name.
Query
Rows: 1
The default database can be seen using the command SHOW DEFAULT DATABASE.
Query
Rows: 1
It is also possible to filter and sort the results by using YIELD, ORDER BY and WHERE.
Query
SHOW DATABASES YIELD name, currentStatus, requestedStatus ORDER BY currentStatus WHERE name CONTAINS 'e'
349
In this example:
• The number of columns returned has been reduced with the YIELD clause.
• The results have been filtered to only show database names containing 'e'.
• The results are ordered by the 'currentStatus' column using ORDER BY.
Rows: 3
Note that for failed databases, the currentStatus and requestedStatus are different.
This often implies an error, but does not always. For example, a database may take a
while to transition from offline to online due to performing recovery. Or, during normal
Query
350
Database names are subject to the standard Cypher restrictions on valid identifiers. The
following naming rules apply:
• Names that begin with an underscore or with the prefix system are reserved for
internal use.
When a database has been created, it will show up in the listing provided by the command SHOW
DATABASES.
Query
SHOW DATABASES
Rows: 5
This command is optionally idempotent, with the default behavior to throw an exception if the database
already exists. Appending IF NOT EXISTS to the command will ensure that no exception is thrown and
nothing happens should the database already exist. Adding OR REPLACE to the command will result in any
existing database being deleted and a new one created.
Query
Query
351
This is equivalent to running DROP DATABASE customers IF EXISTS followed by CREATE DATABASE
customers.
The IF NOT EXISTS and OR REPLACE parts of this command cannot be used together.
Query
The status of the stopped database can be seen using the command SHOW DATABASE name.
Query
Rows: 1
Query
The status of the started database can be seen using the command SHOW DATABASE name.
Query
352
name address role requestedStatu currentStatus error default
s
Rows: 1
Query
When a database has been deleted, it will no longer show up in the listing provided by the command SHOW
DATABASES.
Query
SHOW DATABASES
Rows: 4
This command is optionally idempotent, with the default behavior to throw an exception if the database
does not exists. Appending IF EXISTS to the command will ensure that no exception is thrown and
nothing happens should the database not exist.
Query
The DROP DATABASE command will remove a database entirely. However, you can request that a dump of
the store files is produced first, and stored in the path configured using the dbms.directories.dumps.root
setting (by default <neo4j-home>/data/dumps). This can be achieved by appending DUMP DATA to the
command (or DESTROY DATA to explicitly request the default behaviour). These dumps are equivalent to
353
those produced by neo4j-admin dump and can be similarly restored using neo4j-admin load.
Query
The options IF EXISTS and DUMP DATA/ DESTROY DATA can also be combined. An example could look like
this:
Query
• WAIT n SECONDS - Wait the specified number of seconds (n) for the command to complete before
returning.
• WAIT - Wait for the default period for the command to complete before returning.
The default behaviour is NOWAIT, so if no clause is specified the command will return immediately whilst the
action is performed in the background.
Query
Rows: 1
The success column provides an aggregate status of whether or not the command is considered
successful and thus every row will have the same value. The intention of this column is to make it easy to
determine, for example in a script, whether or not the command completed successfully without timing
out.
this event the command will continue to execute in the background and will not be
aborted.
354
5.2. Indexes for search performance
This section explains how to manage indexes used for search performance.
• Introduction
• Syntax
• Examples
◦ Create a single-property index
◦ Drop an index
◦ List indexes
◦ Deprecated syntax
This section describes how to manage indexes. For query performance purposes, it is important to also
understand how the indexes are used by the Cypher planner. Refer to Query tuning for examples and in-
depth discussions on how query plans result from different index and query scenarios. See specifically The
use of indexes for examples of how various index scenarios result in different query plans.
For information on index configuration and limitations, refer to Operations Manual → Index configuration.
5.2.1. Introduction
A database index is a redundant copy of some of the data in the database for the purpose of making
searches of related data more efficient. This comes at the cost of additional storage space and slower
writes, so deciding what to index and what not to index is an important and often non-trivial task.
Once an index has been created, it will be managed and kept up to date by the DBMS. Neo4j will
automatically pick up and start using the index once it has been created and brought online.
Cypher enables the creation of indexes on one or more properties for all nodes that have a given label:
• An index that is created on a single property for any given label is called a single-property index.
• An index that is created on more than one property for any given label is called a composite index.
Differences in the usage patterns between composite and single-property indexes are described in
Composite index limitations.
355
The following is true for indexes:
• Best practice is to give the index a name when it is created. If the index is not explicitly named, it will
get an auto-generated name.
• The index name must be unique among both indexes and constraints.
• Index creation is by default not idempotent, and an error will be thrown if you attempt to create the
same index twice. Using the keyword IF NOT EXISTS makes the command idempotent, and no error
will be thrown if you attempt to create the same index twice.
5.2.2. Syntax
Table 417. Syntax for managing indexes
356
Command Description Comment
Planner hints and the USING keyword describes how to make the Cypher planner use specific indexes
(especially in cases where the planner would not necessarily have used them).
However, predicates might be planned as existence check and a filter. For most predicates, this can be
avoided by following these restrictions:
• If there is any equality check and list membership check predicates, they need to be for the first
properties defined by the index.
357
• There can be any number of existence check predicates.
• Any predicate after a range search, prefix search or existence check predicate has to be an
existence check predicate.
However, the suffix search and substring search predicates are always planned as existence check and
a filter and any predicates following after will therefore also be planned as such.
WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND n.prop4 < 'e' AND n.prop5 = true AND
exists(n.prop6)
WHERE n.prop1 = 'x' AND n.prop2 = 1 AND n.prop3 > 5 AND exists(n.prop4) AND exists(n.prop5) AND
exists(n.prop6)
with filters on n.prop4 < 'e' and n.prop5 = true, since n.prop3 has a range search predicate.
with filters on n.prop1 ENDS WITH 'x' and n.prop2 = false, since n.prop1 has a suffix search predicate.
Composite indexes require predicates on all properties indexed. If there are predicates on only a subset of
the indexed properties, it will not be possible to use the composite index. To get this kind of fallback
behavior, it is necessary to create additional indexes on the relevant sub-set of properties or on single
properties.
5.2.4. Examples
Query
358
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
Query
Note that the index will not be created if there already exists an index with the same name, same schema
or both.
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
359
Try this query live
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
360
Try this query live
CREATE BTREE INDEX index_with_provider FOR (n:Label) ON (n.prop1) OPTIONS {indexProvider: 'native-btree-
1.0'}
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
361
Try this query live
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
362
Try this query live
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes added: 1
363
Try this query live
Drop an index
An index on all nodes that have a label and property/properties combination can be dropped using the
name with the DROP INDEX index_name command. The name of the index can be found using the SHOW
INDEXES command, given in the output column name.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1
364
Try this query live
Query
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
365
Try this query live
List indexes
Listing indexes can be done with SHOW INDEXES, which will produce a table with the following columns:
uniqueness Tells if the index is only meant to allow one value per key. +
+
366
Column Description Brief output Verbose output
The deprecated built-in procedures for listing indexes, such as db.indexes, work as
before and are not affected by the SHOW INDEXES privilege.
To list all indexes with the brief output columns, the SHOW INDEXES command can be used. If all columns are
wanted, use SHOW INDEXES VERBOSE. Filtering the output on index type is available for BTREE indexes, using
SHOW BTREE INDEXES.
Query
SHOW INDEXES
One of the output columns from SHOW INDEXES is the name of the index. This can be used to drop the index
with the DROP INDEX command.
Result
+---------------------------------------------------------------------------------------------------------
---------------------------------------+
| id | name | state | populationPercent | uniqueness | type | entityType |
labelsOrTypes | properties | indexProvider |
+---------------------------------------------------------------------------------------------------------
---------------------------------------+
| 2 | "index_58a1c03e" | "ONLINE" | 100.0 | "NONUNIQUE" | "BTREE" | "NODE" | ["Person"]
| ["location"] | "native-btree-1.0" |
| 3 | "index_d7c12ba3" | "ONLINE" | 100.0 | "NONUNIQUE" | "BTREE" | "NODE" | ["Person"]
| ["highScore"] | "native-btree-1.0" |
| 1 | "index_deeafdb2" | "ONLINE" | 100.0 | "NONUNIQUE" | "BTREE" | "NODE" | ["Person"]
| ["firstname"] | "native-btree-1.0" |
+---------------------------------------------------------------------------------------------------------
---------------------------------------+
3 rows
367
Try this query live
SHOW INDEXES
Deprecated syntax
An index on all nodes that have a label and single property combination can be dropped with DROP INDEX
ON :Label(property).
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1
368
Try this query live
A composite index on all nodes that have a label and multiple property combination can be dropped with
DROP INDEX ON :Label(prop1, …, propN). The following statement will drop a composite index on all
nodes labeled with Person and which have both an age and country property:
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Indexes removed: 1
369
Try this query live
5.3.1. Introduction
Full-text indexes are powered by the Apache Lucene indexing and search library, and can be used to index
nodes and relationships by string properties. A full-text index allows you to write queries that match
within the contents of indexed string properties. For instance, the btree indexes described in previous
sections can only do exact matching or prefix matches on strings. A full-text index will instead tokenize the
indexed string values, so it can match terms anywhere within the strings. How the indexed strings are
tokenized and broken into terms, is determined by what analyzer the full-text index is configured with. For
instance, the swedish analyzer knows how to tokenize and stem Swedish words, and will avoid indexing
Swedish stop words. The complete list of stop words for each analyzer is included in the result of the
db.index.fulltext.listAvailableAnalyzers procedure.
Full-text indexes:
• support configuring custom analyzers, including analyzers that are not included with Lucene itself.
370
• are kept up to date automatically, as nodes and relationships are added, removed, and modified.
• will automatically populate newly created indexes with the existing data in a store.
• can be checked by the consistency checker, and they can be rebuilt if there is a problem with them.
• are a projection of the store, and can only index nodes and relationships by the contents of their
properties.
• are created, dropped, and updated transactionally, and is automatically replicated throughout a cluster.
• can be configured to be eventually consistent, in which index updating is moved from the commit path
to a background thread. Using this feature, it is possible to work around the slow Lucene writes from
the performance critical commit process, thus removing the main bottlenecks for Neo4j write
performance.
At first sight, the construction of full-text indexes can seem similar to regular indexes. However there are
some things that are interesting to note: In contrast to btree indexes, a full-text index
• can be applied to more than one property at a time (similar to a composite index) but with an important
difference: While a composite index applies only to entities that match the indexed label and all of the
indexed properties, full-text index will index entities that have at least one of the indexed labels or
relationship types, and at least one of the indexed properties.
For information on how to configure full-text indexes, refer to Operations Manual → Indexes to support full-
text search.
The procedures for managing full-text indexes are listed in the table below:
371
Usage Procedure Description
Create full-text node index db.index.fulltext.createNodeIndex Create a node fulltext index for the
given labels and properties. The optional
'config' map parameter can be used to
supply settings to the index. Supported
settings are 'analyzer', for specifying
what analyzer to use when indexing and
querying. Use the
db.index.fulltext.listAvailableAnaly
zers procedure to see what options are
available. And 'eventually_consistent'
which can be set to 'true' to make this
index eventually consistent, such that
updates from committing transactions
are applied in a background thread.
Create full-text relationship index db.index.fulltext.createRelationship Create a relationship fulltext index for
Index the given relationship types and
properties. The optional 'config' map
parameter can be used to supply
settings to the index. Supported
settings are 'analyzer', for specifying
what analyzer to use when indexing and
querying. Use the
db.index.fulltext.listAvailableAnaly
zers procedure to see what options are
available. And 'eventually_consistent'
which can be set to 'true' to make this
index eventually consistent, such that
updates from committing transactions
are applied in a background thread.
List available analyzers db.index.fulltext.listAvailableAnaly List the available analyzers that the full-
zers text indexes can be configured with.
Use full-text node index db.index.fulltext.queryNodes Query the given full-text index. Returns
the matching nodes and their Lucene
query score, ordered by score.
Use full-text relationship index db.index.fulltext.queryRelationships Query the given full-text index. Returns
the matching relationships and their
Lucene query score, ordered by score.
Eventually consistent indexes db.index.fulltext.awaitEventuallyCon Wait for the updates from recently
sistentIndexRefresh committed transactions to be applied to
any eventually-consistent full-text
indexes.
372
then a list of property names.
Query
m.title
"The Matrix"
Rows: 1
Nodes created: 1
Properties set: 1
Labels added: 1
And we have a full-text index on the title and description properties of movies and books.
Query
Then our movie node from above will be included in the index, even though it only has one of the indexed
labels, and only one of the indexed properties:
Query
Rows: 1
The same is true for full-text indexes on relationships. Though a relationship can only have one type, a
relationship full-text index can index multiple types, and all relationships will be included that match one of
the relationship types, and at least one of the indexed properties.
373
Query
In this example, an eventually consistent relationship full-text index is created for the TAGGED_AS
relationship type, and the taggedByUser property, and the index uses the url_or_email analyzer. This
could, for instance, be a system where people are assigning tags to documents, and where the index on
the taggedByUser property will allow them to quickly find all of the documents they have tagged. Had it not
been for the relationship index, one would have had to add artificial connective nodes between the tags
and the documents in the data model, just so these nodes could be indexed.
Rows: 0
Query
node.title score
Rows: 4
Full-text indexes are powered by the Apache Lucene indexing and search library. This means that we can
use Lucene’s full-text query language to express what we wish to search for. For instance, if we are only
interested in exact matches, then we can quote the string we are searching for.
Query
374
When we put "Full Metal Jacket" in quotes, Lucene only gives us exact matches.
node.title score
Rows: 1
Lucene also allows us to use logical operators, such as AND and OR, to search for terms:
Query
Only the Full Metal Jacket movie in our database has both the words full and metal.
node.title score
Rows: 1
It is also possible to search for only specific properties, by putting the property name and a colon in front of
the text being searched for.
Query
"Metallica Through The Never" "The movie follows the young roadie 0.2615291476249695
Trip through his surreal adventure
with the band."
Rows: 1
A complete description of the Lucene query syntax can be found in the Lucene documentation.
In the following example, we will drop the taggedByRelationshipIndex that we created previously:
Query
CALL db.index.fulltext.drop("taggedByRelationshipIndex")
375
Table 426. Result
(empty result)
Rows: 0
5.4. Constraints
This section explains how to manage constraints used for ensuring data integrity.
• Introduction
• Syntax
• Examples
◦ Unique node property constraints
◦ List constraints
◦ Deprecated syntax
5.4.1. Introduction
The following constraint types are available:
i. All the properties exist on all the nodes with that label.
376
ii. The combination of the property values is unique.
• Create new nodes without all the properties or where the combination of property values is not
unique.
• Update the properties so that the combination of property values is no longer unique.
Node key constraints, node property existence constraints and relationship property
existence constraints are only available in Neo4j Enterprise Edition. Databases
containing one of these constraint types cannot be opened using Neo4j Community
Edition.
• Adding a unique property constraint on a property will also add a single-property index on that
property, so such an index cannot be added separately.
• Adding a node key constraint for a set of properties will also add a composite index on those
properties, so such an index cannot be added separately.
• Cypher will use these indexes for lookups just like other indexes. Refer to Indexes for search
performance for more details on indexes.
• If a unique property constraint is dropped and the single-property index on the property is still
required, the index will need to be created explicitly.
• If a node key constraint is dropped and the composite-property index on the properties is still required,
the index will need to be created explicitly.
• A given label can have multiple constraints, and unique and property existence constraints can be
combined on the same property.
• Adding constraints is an atomic operation that can take a while — all existing data has to be scanned
before Neo4j can turn the constraint 'on'.
• Best practice is to give the constraint a name when it is created. If the constraint is not explicitly
named, it will get an auto-generated name.
• The constraint name must be unique among both indexes and constraints.
• Constraint creation is by default not idempotent, and an error will be thrown if you attempt to create
the same constraint twice. Using the keyword IF NOT EXISTS makes the command idempotent, and no
error will be thrown if you attempt to create the same constraint twice.
5.4.2. Syntax
Table 427. Syntax for managing indexes
377
Command Description Comment
378
Command Description Comment
5.4.3. Examples
When creating a unique constraint, a name can be provided. The constraint ensures that your database
will never contain more than one node with a specific label and one property value.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Unique constraints added: 1
none
379
Create a unique constraint only if it does not already exist
If it is unknown if a constraint exists or not but we want to make sure it does, we add the IF NOT EXISTS.
The uniqueness constraint ensures that your database will never contain more than one node with a
specific label and one property value.
Query
Note no constraint will be created if any other constraint with that name or another uniqueness constraint
on the same schema already exists. Assuming no such constraints existed:
Result
+-------------------+
| No data returned. |
+-------------------+
Unique constraints added: 1
none
To create a unique constraint with a specific index provider and configuration for the backing index, the
OPTIONS clause is used. Valid values for the index provider is native-btree-1.0 and lucene+native-3.0,
default if nothing is specified is native-btree-1.0. Valid configuration settings are spatial.cartesian.min,
spatial.cartesian.max, spatial.cartesian-3d.min, spatial.cartesian-3d.max, spatial.wgs-84.min,
spatial.wgs-84.max, spatial.wgs-84-3d.min, and spatial.wgs-84-3d.max. Non-specified settings get their
respective default values.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Unique constraints added: 1
380
Try this query live
none
Create a Book node with an isbn that isn’t already in the database.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 1
Properties set: 2
Labels added: 1
Create a Book node with an isbn that is already used in the database.
Query
Error message
Node(0) already exists with label `Book` and property `isbn` = '1449356265'
Create a unique property constraint on the property isbn on nodes with the Book label when there are two
nodes with the same isbn.
Query
In this case the constraint can’t be created because it is violated by existing data. We may choose to use
Indexes for search performance instead or remove the offending nodes and then re-apply the constraint.
381
Error message
When creating a node property existence constraint, a name can be provided. The constraint ensures that
all nodes with a certain label have a certain property.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Property existence constraints added: 1
none
Create a node property existence constraint only if it does not already exist
If it is unknown if a constraint exists or not but we want to make sure it does, we add the IF NOT EXISTS.
The node property existence constraint ensures that all nodes with a certain label have a certain property.
Query
Note no constraint will be created if any other constraint with that name or another node property
existence constraint on the same schema already exists. Assuming a constraint with the name
constraint_name already existed:
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
none
382
Create a node that complies with property existence constraints
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 1
Properties set: 2
Labels added: 1
Trying to create a Book node without an isbn property, given a property existence constraint on
:Book(isbn).
Query
Error message
Trying to remove the isbn property from an existing node book, given a property existence constraint on
:Book(isbn).
Query
Error message
Create a constraint on the property isbn on nodes with the Book label when there already exists a node
without an isbn.
383
Query
In this case the constraint can’t be created because it is violated by existing data. We may choose to
remove the offending nodes and then re-apply the constraint.
Error message
When creating a relationship property existence constraint, a name can be provided. The constraint
ensures all relationships with a certain type have a certain property.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Property existence constraints added: 1
none
Create a relationship property existence constraint only if it does not already exist
If it is unknown if a constraint exists or not but we want to make sure it does, we add the IF NOT EXISTS.
The relationship property existence constraint ensures all relationships with a certain type have a certain
property.
Query
Note no constraint will be created if any other constraint with that name or another relationship property
existence constraint on the same schema already exists. Assuming a constraint with the name
constraint_name already existed:
384
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
none
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 2
Relationships created: 1
Properties set: 1
Labels added: 2
Trying to create a LIKED relationship without a day property, given a property existence constraint
:LIKED(day).
Query
CREATE (user:User)-[like:LIKED]->(book:Book)
Error message
Trying to remove the day property from an existing relationship like of type LIKED, given a property
existence constraint :LIKED(day).
Query
385
In this case the property is not removed.
Error message
Create a constraint on the property day on relationships with the LIKED type when there already exists a
relationship without a property named day.
Query
In this case the constraint can’t be created because it is violated by existing data. We may choose to
remove the offending relationships and then re-apply the constraint.
Error message
When creating a node key constraint, a name can be provided. The constraint ensures that all nodes with a
particular label have a set of defined properties whose combined value is unique and all properties in the
set are present.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Node key constraints added: 1
none
If it is unknown if a constraint exists or not but we want to make sure it does, we add the IF NOT EXISTS.
386
The node key constraint ensures that all nodes with a particular label have a set of defined properties
whose combined value is unique and all properties in the set are present.
Query
Note no constraint will be created if any other constraint with that name or another node key constraint on
the same schema already exists. Assuming a node key constraint on (:Person {firstname, surname})
already existed:
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
none
CREATE CONSTRAINT constraint_name IF NOT EXISTS ON (n:Person) ASSERT (n.firstname, n.surname) IS NODE KEY
To create a node key constraint with a specific index provider for the backing index, the OPTIONS clause is
used. Valid values for the index provider is native-btree-1.0 and lucene+native-3.0, default if nothing is
specified is native-btree-1.0.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Node key constraints added: 1
none
To create a node key constraint with a specific index configuration for the backing index, the OPTIONS
clause is used. Valid configuration settings are spatial.cartesian.min, spatial.cartesian.max,
387
spatial.cartesian-3d.min, spatial.cartesian-3d.max, spatial.wgs-84.min, spatial.wgs-84.max,
spatial.wgs-84-3d.min, and spatial.wgs-84-3d.max. Non-specified settings get their respective default
values.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Node key constraints added: 1
none
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 1
Properties set: 3
Labels added: 1
Trying to create a Person node without a surname property, given a node key constraint on
:Person(firstname, surname), will fail.
Query
388
Error message
Node(0) with label `Person` must have the properties (firstname, surname)
Trying to remove the surname property from an existing node Person, given a NODE KEY constraint on
:Person(firstname, surname).
Query
Error message
Node(0) with label `Person` must have the properties (firstname, surname)
Trying to create a node key constraint on the property surname on nodes with the Person label will fail
when a node without a surname already exists in the database.
Query
In this case the node key constraint can’t be created because it is violated by existing data. We may
choose to remove the offending nodes and then re-apply the constraint.
Error message
Drop a constraint
A constraint can be dropped using the name with the DROP CONSTRAINT constraint_name command. It is
the same command for unique property, property existence and node key constraints. The name of the
constraint can be found using the SHOW CONSTRAINTS command, given in the output column name.
Query
389
Result
+-------------------+
| No data returned. |
+-------------------+
Named constraints removed: 1
If it is uncertain if any constraint with a given name exists and you want to drop it if it does but not get an
error should it not, use IF EXISTS. It is the same command for unique property, property existence and
node key constraints.
Query
Result
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
List constraints
Listing constraints can be done with SHOW CONSTRAINTS, which will produce a table with the following
columns:
options The options passed to CREATE command, for the index associated to +
the constraint, or null if no index is associated with the constraint.
390
Column Description Brief output Verbose output
The deprecated built-in procedures for listing constraints, such as db.constraints, work
as before and are not affected by the SHOW CONSTRAINTS privilege.
To list all constraints with the brief output columns, the SHOW CONSTRAINTS command can be used. If all
columns are wanted, use SHOW CONSTRAINTS VERBOSE. Filtering the output on constraint type is available for
all types, the filtering keywords are listed in the syntax table. As an example, to show only unique
constraints, use SHOW UNIQUE CONSTRAINTS.
Query
SHOW CONSTRAINTS
One of the output columns from SHOW CONSTRAINTS is the name of the constraint. This can be used to drop
the constraint with the DROP CONSTRAINT command.
Result
+----------------------------------------------------------------------------------------------------+
| id | name | type | entityType | labelsOrTypes | properties | ownedIndexId |
+----------------------------------------------------------------------------------------------------+
| 2 | "constraint_ca412c3d" | "UNIQUENESS" | "NODE" | ["Book"] | ["isbn"] | 1 |
+----------------------------------------------------------------------------------------------------+
1 row
Deprecated syntax
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Unique constraints removed: 1
391
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Property existence constraints removed: 1
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Property existence constraints removed: 1
Use DROP CONSTRAINT to remove a node key constraint from the database.
Query
Result
+-------------------+
| No data returned. |
+-------------------+
Node key constraints removed: 1
5.5. Security
This section explains how to use Cypher to manage Neo4j role-based access control and
fine-grained security.
• Introduction
• Syntax summaries
392
▪ Listing current user
▪ Listing users
▪ Creating users
▪ Modifying users
▪ Deleting users
◦ Role management
▪ Listing roles
▪ Creating roles
▪ Deleting roles
▪ Assigning roles
▪ Revoking roles
◦ Listing privileges
• Read privileges
◦ The TRAVERSE privilege
• Write privileges
◦ The CREATE privilege
• Security of administration
◦ The admin role
◦ Database administration
393
▪ The INDEX MANAGEMENT privileges
◦ DBMS administration
• Built-in roles
5.5.1. Introduction
This section introduces the sections on how to manage Neo4j role-based access control
and fine-grained security.
Neo4j has a complex security model stored in the system graph, maintained in a special database called
the system database. All administrative commands need to be executing against the system database.
When connected to the DBMS over bolt, administrative commands are automatically routed to the system
database. For more information on how to manage multiple databases, refer to the section on
administering databases.
Neo4j 3.1 introduced the concept of role-based access control. It was possible to create users and assign
them to roles to control whether the users could read, write and administer the database. In Neo4j 4.0 this
model was enhanced significantly with the addition of privileges, which are the underlying access-control
rules by which the users rights are defined.
The original built-in roles still exist with almost the exact same access rights, but they are no-longer
statically defined (see Built-in roles). Instead they are defined in terms of their underlying privileges and
they can be modified by adding an removing these access rights.
In addition, any new roles created can by assigned any combination of privileges to create the specific
394
access control desired. A major additional capability is sub-graph access control whereby read-access to
the graph can be limited to specific combinations of label, relationship-type and property.
| Or, used to indicate alternative parts of a If the syntax needs to specify either a name
command. Needs to be part of a grouping. or *, this can be indicated with * | name.
{ and } Used to group parts of the command, To use the or in the syntax summary it
common together with |. needs to be in a group, {* | name}.
[ and ] Used to indicate an optional part of the If a keyword in the syntax can either be in
command. It also groups alternatives singular or plural, we can indicate that the S
together, when there can be either of the is optional with GRAPH[S].
alternatives or nothing.
… Repeated pattern, the command part A comma separated list of names would be
immediately before this is repeated. name[, …].
" When a special character is part of the To include { in the syntax use "{" { * |
syntax itself, we surround it with " to name } "}", here we get either {*} or
indicate this. {name}.
The special characters in the table above are the only ones that need to be escaped using " in the syntax
summaries.
An example that uses all special characters is granting the READ privilege:
GRANT READ
"{" { * | property[, ...] } "}"
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
Some things to notice about this command is that it includes { and } in the syntax, and between them has
a grouping of either a list of properties or the character *. It also has multiple optional parts, including the
395
entity part of the command which is the grouping following the graph name.
In difference, there is no need to escape any characters in the node property existence constraint creation
command. This is because ( and ) are not special characters, and the [ and ] indicate that the constraint
name is optional, and are not part of the command.
• User Management
◦ Listing current user
◦ Listing users
◦ Creating users
◦ Modifying users
◦ Deleting users
• Role management
◦ The PUBLIC role
◦ Listing roles
◦ Creating roles
◦ Deleting roles
◦ Assigning roles
◦ Revoking roles
User Management
Users can be created and managed using a set of Cypher administration commands executed against the
system database.
When connected to the DBMS over bolt, administration commands are automatically routed to the system
database.
396
Command Description Required privilege Commu Enterpris
nity e Edition
Edition
397
Command Description Required privilege Commu Enterpris
nity e Edition
Edition
The currently logged-in user can be seen using SHOW CURRENT USER which will produce a table with four
columns:
passwordCha If true, the user must change their password at the next login. +
ngeRequired +
Query
Rows: 1
This command is only supported for a logged-in user and will return an empty result if
authorization has been disabled.
Listing users
Available users can be seen using SHOW USERS which will produce a table of users with four columns:
398
Column Description Community Enterprise
Edition Edition
passwordCha If true, the user must change their password at the next login. +
ngeRequired +
Query
SHOW USERS
Rows: 1
When first starting a Neo4j DBMS, there is always a single default user neo4j with administrative
privileges. It is possible to set the initial password using neo4j-admin set-initial-password, otherwise it is
necessary to change the password after first login.
Creating users
Command syntax
If the optional SET PASSWORD CHANGE [NOT] REQUIRED is omitted then the default is CHANGE REQUIRED. The
default for SET STATUS is ACTIVE. The password can either be a string value or a string parameter. The
optional PLAINTEXT in SET PLAINTEXT PASSWORD has the same behaviour as SET PASSWORD. The optional
ENCRYPTED can be used to create a user when the plaintext password is unknown but the encrypted
password is available (e.g. from a database backup). With ENCRYPTED, the password string is expected to
be on the format <encryption-version>,<hash>,<salt>.
For example, we can create the user jake in a suspended state and the requirement to change his
password.
399
Query
CREATE USER jake SET PASSWORD 'abc' CHANGE REQUIRED SET STATUS SUSPENDED
The SET STATUS {ACTIVE | SUSPENDED} part of the command is only available in
Enterprise Edition.
The created user will appear on the list provided by SHOW USERS.
Query
SHOW USERS YIELD user, suspended, passwordChangeRequired, roles WHERE user = 'jake'
• Filter the results using a WHERE clause to show only the new user
Rows: 1
Query
SHOW USERS YIELD roles, user WHERE "PUBLIC" IN roles RETURN user as publicUsers
It is also possible to add a RETURN clause to further manipulate the results after filtering. In this case it is
used to filter out the roles column and rename the users column to publicUsers.
publicUsers
"jake"
"neo4j"
Rows: 2
In Neo4j Community Edition there are no roles, but all users have implied administrator
privileges. In Neo4j Enterprise Edition all users are automatically assigned the PUBLIC
role, giving them a base set of privileges.
The CREATE USER command is optionally idempotent, with the default behavior to throw an exception if the
user already exists. Appending IF NOT EXISTS to the command will ensure that no exception is thrown and
nothing happens should the user already exist. Adding OR REPLACE to the command will result in any
existing user being deleted and a new one created.
400
Query
0 rows
Query
This is equivalent to running DROP USER jake IF EXISTS followed by CREATE USER jake SET PASSWORD
'xyz'.
The IF NOT EXISTS and OR REPLACE parts of this command cannot be used together.
Modifying users
Command syntax
The password can either be a string value or a string parameter, and must not be identical to the old
password. The optional PLAINTEXT in SET PLAINTEXT PASSWORD has the same behaviour as SET PASSWORD.
The optional ENCRYPTED can be used to update a user’s password when the plaintext password is unknown
but the encrypted password is available (e.g. from a database backup). With ENCRYPTED, the password
string is expected to be on the format <encryption-version>,<hash>,<salt>.
For example, we can modify the user jake with a new password and active status as well as remove the
requirement to change his password.
Query
ALTER USER jake SET PASSWORD 'abc123' CHANGE NOT REQUIRED SET STATUS ACTIVE
When altering a user it is only necessary to specify the changes required. For example,
leaving out the CHANGE [NOT] REQUIRED part of the query will leave that unchanged.
The SET STATUS {ACTIVE | SUSPENDED} part of the command is only available in
Enterprise Edition.
401
The changes to the user will appear on the list provided by SHOW USERS.
Query
SHOW USERS
Rows: 2
Users can change their own password using ALTER CURRENT USER SET PASSWORD. The old password is
required in addition to the new one, and either or both can be a string value or a string parameter. When a
user executes this command it will change their password as well as set the CHANGE NOT REQUIRED flag.
Query
This command only works for a logged in user and cannot be run with auth disabled.
Deleting users
Query
When a user has been deleted, it will no longer appear on the list provided by SHOW USERS.
Query
SHOW USERS
402
user roles passwordChangeRequired suspended
Rows: 1
This command is optionally idempotent, with the default behavior to throw an exception if the user does
not exists. Appending IF EXISTS to the command will ensure that no exception is thrown and nothing
happens should the user not exist.
Query
0 rows
Role Management
Roles can be created and managed using a set of Cypher administration commands executed against the
system database.
When connected to the DBMS over bolt, administration commands are automatically routed to the system
database.
List roles and users assigned to SHOW ROLE and SHOW USER
SHOW [ALL|POPULATED] ROLES WITH USERS
[YIELD { * | field[, ...] } [ORDER BY them.
field[, ...]] [SKIP n] [LIMIT n]]
[WHERE expression] When using the RETURN clause, the
[RETURN field[, ...] [ORDER BY field[,
...]] [SKIP n] [LIMIT n]] YIELD clause is mandatory and
may not be omitted.
403
Command Description Required privilege
Create a new role, or if a role with CREATE ROLE and DROP ROLE
CREATE OR REPLACE ROLE name [AS COPY OF
name] the same name exists, replace it.
There exists a special built-in role, PUBLIC, which is assigned to all users. This role cannot be dropped or
revoked from any user, but its privileges may be modified. By default, it is assigned the ACCESS privilege
on the default database.
In contrast to the PUBLIC role, the other built-in roles can be granted, revoked, dropped and re-created.
Listing roles
Query
SHOW ROLES
This is the same command as SHOW ALL ROLES. When first starting a Neo4j DBMS there are a number of
built-in roles:
• PUBLIC - a role that all users have granted, by default it gives access to the default database
• reader - can perform traverse and read operations on all databases except system.
• editor - can perform traverse, read, and write operations on all databases except system, but cannot
make new labels or relationship types.
• publisher - can do the same as editor, but also create new labels and relationship types.
• architect - can do the same as publisher as well as create and manage indexes and constraints.
• admin - can do the same as all the above, as well as manage databases, users, roles, and privileges.
More information about the built-in roles can be found in Operations Manual → Built-in roles
404
role
"PUBLIC"
"admin"
"architect"
"editor"
"publisher"
"reader"
Rows: 6
There are multiple versions of this command, the default being SHOW ALL ROLES. To only show roles that
are assigned to users, the command is SHOW POPULATED ROLES. To see which users are assigned to roles
WITH USERS can be appended to the commands. This will give one result row for each user, so if a role is
assigned to two users then it will show up twice in the result.
Query
The table of results will show information about the role and what database it belongs to.
role member
"PUBLIC" "neo4j"
"PUBLIC" "jake"
"PUBLIC" "user1"
"PUBLIC" "user2"
"PUBLIC" "user3"
"admin" "neo4j"
Rows: 6
It is also possible to filter and sort the results by using YIELD, ORDER BY and WHERE.
Query
SHOW ROLES YIELD role ORDER BY role WHERE role ENDS WITH 'r'
In this example:
• The results have been filtered to only return the roles ending in 'r'.
• The results are ordered by the 'action' column using ORDER BY.
405
role
"editor"
"publisher"
"reader"
Rows: 3
Creating roles
Query
A role can also be copied, keeping its privileges, using CREATE ROLE AS COPY OF.
Query
The created roles will appear on the list provided by SHOW ROLES.
Query
SHOW ROLES
role
"PUBLIC"
"admin"
"architect"
"editor"
"myrole"
"mysecondrole"
406
role
"publisher"
"reader"
Rows: 8
These command versions are optionally idempotent, with the default behavior to throw an exception if the
role already exists. Appending IF NOT EXISTS to the command will ensure that no exception is thrown and
nothing happens should the role already exist. Adding OR REPLACE to the command will result in any
existing role being deleted and a new one created.
Query
0 rows
Query
This is equivalent to running DROP ROLE myrole IF EXISTS followed by CREATE ROLE myrole.
The IF NOT EXISTS and OR REPLACE parts of this command cannot be used together.
Deleting roles
Query
When a role has been deleted, it will no longer appear on the list provided by SHOW ROLES.
Query
SHOW ROLES
role
"PUBLIC"
"admin"
"architect"
407
role
"editor"
"publisher"
"reader"
Rows: 6
This command is optionally idempotent, with the default behavior to throw an exception if the role does
not exists. Appending IF EXISTS to the command will ensure that no exception is thrown and nothing
happens should the role not exist.
Query
0 rows
Users can be given access rights by assigning them roles using GRANT ROLE.
Query
The roles assigned to each user can be seen in the list provided by SHOW USERS.
Query
SHOW USERS
Rows: 5
Query
408
0 rows, System updates: 6
Query
SHOW USERS
Rows: 5
Users can lose access rights by revoking roles from them using REVOKE ROLE.
Query
The roles revoked from users can no longer be seen in the list provided by SHOW USERS.
Query
SHOW USERS
Rows: 5
Query
409
0 rows, System updates: 6
• Listing privileges
◦ Examples for listing all privileges
Privileges control the access rights to graph elements using a combined whitelist/blacklist mechanism. It is
possible to grant access, or deny access, or a combination of the two. The user will be able to access the
resource if they have a grant (whitelist) and do not have a deny (blacklist) relevant to that resource. All
other combinations of GRANT and DENY will result in the matching path being inaccessible. What this means
in practice depends on whether we are talking about a read privilege or a write privilege.
• If a entity is not accessible due to read privileges, the data will become invisible to attempts to read it.
It will appear to the user as if they have a smaller database (smaller graph).
• If an entity is not accessible due to write privileges, an error will occur on any attempt to write that
data.
In this document we will often use the terms 'allows' and 'enables' in seemingly identical
ways. However, there is a subtle difference. We will use 'enables' to refer to the
consequences of read privileges where a restriction will not cause an error, only a
reduction in the apparent graph size. We will use 'allows' to refer to the consequence of
write privileges where a restriction can result in an error.
If a user was not also provided with the database ACCESS privilege then access to the
entire database will be denied. Information about the database access privilege can be
found in The ACCESS privilege.
• the command:
◦ GRANT – gives privileges to roles.
410
◦ REVOKE – removes granted or denied privilege from roles.
• graph-privilege
◦ Can be either a read privilege or write privilege.
• name
◦ The graph or graphs to associate the privilege with. Because in Neo4j 4.2 you can have only one
graph per database, this command uses the database name to refer to that graph.
If you delete a database and create a new one with the same name, the new one
will NOT have the privileges assigned to the deleted graph.
◦ It can be * which means all graphs. Graphs created after this command execution will also be
associated with these privileges.
• entity
◦ The graph elements this privilege applies to:
◦ Some of the commands for write privileges do not allow an entity part, see Write privileges for
details.
• role[, …]
◦ The role or roles to associate the privilege with, comma-separated.
Command Description
411
Command Description
DENY does NOT erase a granted privilege; they both exist. Use REVOKE if you want to
remove a privilege.
The general grant and deny syntax is illustrated in the image below.
A more detailed syntax illustration would be the image below for graph privileges.
Figure 2. Syntax of GRANT and DENY Graph Privileges. The { and } are part of the syntax and not used
for grouping.
The below image shows the hierarchy between the different graph privileges.
412
Figure 3. Graph privileges hierarchy
Listing privileges
Available privileges can be displayed using the different SHOW PRIVILEGES commands.
Command Description
When using the RETURN clause, the YIELD clause is mandatory and may not be omitted.
Available privileges for all roles can be displayed using SHOW PRIVILEGES.
413
Command syntax
Query
SHOW PRIVILEGES
Lists all privileges for all roles. The table contains columns describing the privilege:
• action: which type of privilege this is: traverse, read, match, write, a database privilege, a dbms
privilege or admin
• resource: what type of scope this privilege applies to: the entire dbms, a database, a graph or sub-
graph access
• segment: for sub-graph access control, this describes the scope in terms of labels or relationship types
414
access action resource graph segment role
Rows: 36
It is also possible to filter and sort the results by using YIELD, ORDER BY and WHERE.
Query
SHOW PRIVILEGES YIELD role, access, action, segment ORDER BY action WHERE role = 'admin'
In this example:
• The number of columns returned has been reduced with the YIELD clause.
• The results have been filtered to only return the admin role using a WHERE clause.
• The results are ordered by the action column using ORDER BY.
415
role access action segment
Rows: 9
Query
In this example, the WHERE clause is used to filter privileges down to those that target specific graphs only.
Rows: 3
Aggregations in the RETURN clause can be used to group privileges. In this case, by user and granted /
denied.
Query
SHOW PRIVILEGES YIELD * RETURN role, access, collect([graph, resource, segment, action]) as privileges
416
role access privileges
Rows: 8
The RETURN clause can also be used to order and paginate the results, which is useful when combined with
YIELD and WHERE. In this example the query returns privileges for display five-per-page, and skips the first
five to display the second page.
Query
Rows: 5
417
Examples for listing privileges for specific roles
Available privileges for specific roles can be displayed using SHOW ROLE name PRIVILEGES.
Command syntax
Query
Rows: 1
Query
Rows: 2
Available privileges for roles can also be output as Cypher commands with the optional AS COMMAND[S].
Query
command
418
command
Rows: 11
Like other SHOW commands, the output can also be processed using YIELD / WHERE / RETURN.
Query
command
Rows: 2
It is also possible to have privileges output as revoke commands. For more on revoke commands, please
see The REVOKE command.
Query
command
Rows: 3
Available privileges for specific users can be displayed using SHOW USER name PRIVILEGES.
419
Please note that if a non-native auth provider like LDAP is in use, SHOW USER PRIVILEGES
will only work in a limited capacity; It is only possible for a user to show their own
privileges. Other users' privileges cannot be listed when using a non-native auth
provider.
Command syntax
Query
Rows: 4
Query
420
access action resource graph segment role user
Rows: 8
The same command can be used at all times to review available privileges for the current user. For this
purpose, a shorter form of the the command also exists: SHOW USER PRIVILEGES.
Query
Available privileges for users can also be output as Cypher commands with the optional AS COMMAND[S].
When showing user privileges as commands, the roles in the Cypher commands are
replaced with a parameter. This can be used to quickly create new roles based on the
privileges of specific users.
Query
command
Rows: 4
Like other SHOW commands, the output can also be processed using YIELD / WHERE / RETURN. Additionally, just
as with role privileges, it is also possible to show user privileges as revoke commands.
Query
SHOW USER jake PRIVILEGES AS REVOKE COMMANDS WHERE command CONTAINS 'EXECUTE'
command
Rows: 2
421
Command syntax
REVOKE
[ GRANT | DENY ] graph-privilege
FROM role[, ...]
Query
While it can be explicitly specified that revoke should remove a GRANT or DENY, it is also possible to revoke
either one by not specifying at all as the next example demonstrates. Because of this, if there happen to be
a GRANT and a DENY on the same privilege, it would remove both.
Query
• MATCH - combines both TRAVERSE and READ, enabling an entity to be found and its properties read.
422
Command syntax
GRANT TRAVERSE
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
For example, we can enable the user jake, who has role 'regularUsers' to find all nodes with the label Post.
Query
Command syntax
DENY TRAVERSE
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
For example, we can disable the user jake, who has role 'regularUsers' from finding all nodes with the
label Payments.
Query
Command syntax
GRANT READ
"{" { * | property[, ...] } "}"
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
423
For example, we can enable the user jake, who has role 'regularUsers' to read all properties on nodes with
the label Post. The * implies that the ability to read all properties also extends to properties that might be
added in the future.
Query
Granting property READ access does not imply that the entities with that property can be
found. For example, if there is also a DENY TRAVERSE present on the same entity as a
GRANT READ, the entity will not be found by a Cypher MATCH statement.
Command syntax
DENY READ
"{" { * | property[, ...] } "}"
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
Although we just granted the user 'jake' the right to read all properties, we may want to hide the secret
property. The following example shows how to do that.
Query
Command syntax
GRANT MATCH
"{" { * | property[, ...] } "}"
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
For example if you want to grant the ability to read the properties language and length for nodes with the
label Message, as well as the ability to find these nodes, to a role regularUsers you can use the following
424
GRANT MATCH query.
Query
Like all other privileges, the MATCH privilege can also be denied.
Command syntax
DENY MATCH
"{" { * | property[, ...] } "}"
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
[
ELEMENT[S] { * | label-or-rel-type[, ...] }
| NODE[S] { * | label[, ...] }
| RELATIONSHIP[S] { * | rel-type[, ...] }
]
TO role[, ...]
Please note that the effect of denying a MATCH privilege depends on whether concrete property keys are
specified or a *. If you specify concrete property keys then DENY MATCH will only deny reading those
properties. Finding the elements to traverse would still be enabled. If you specify * instead then both
traversal of the element and all property reads will be disabled. The following queries will show examples
for this.
Denying to read the property ´content´ on nodes with the label Message for the role regularUsers would
look like the following query. Although not being able to read this specific property, nodes with that label
can still be traversed (and, depending on other grants, other properties on it could still be read).
Query
The following query exemplifies how it would look like if you want to deny both reading all properties and
traversing nodes labeled with Account.
Query
425
• The DELETE privilege
• SET LABEL - allows setting the specified node labels using the SET clause.
• REMOVE LABEL - allows removing the specified node labels using the REMOVE clause.
There are also compound privileges which combine the above specific privileges:
• MERGE - allows match, create and set property to permit the MERGE command.
• ALL GRAPH PRIVILEGES - allows all read and write operation on an entire graph.
Command syntax
For example, granting the ability to create elements on the graph neo4j to the role regularUsers would be
achieved using:
Query
426
Command syntax
For example, denying the ability to create nodes with the label foo on all graphs to the role regularUsers
would be achieved using:
Query
If the user attempts to create nodes with a label that does not already exist in the
database, then the user must also possess the CREATE NEW LABEL privilege. The
same applies to new relationships - the CREATE NEW RELATIONSHIP TYPE privilege is
required.
Command syntax
For example, granting the ability to delete elements on the graph neo4j to the role regularUsers would be
achieved using:
Query
427
Command syntax
For example, denying the ability to delete relationships with the relationship type bar on all graphs to the
role regularUsers would be achieved using:
Query
Users with DELETE privilege, but restricted TRAVERSE privileges, will not be able to do
DETACH DELETE in all cases. See Operations Manual → Fine-grained access control for
more info.
Command syntax
For example, granting the ability to set any label on nodes of the graph neo4j to the role regularUsers
would be achieved using:
Query
Unlike many of the other read and write privileges, it is not possible to restrict the SET
LABEL privilege to specific ELEMENTS, NODES or RELATIONSHIPS.
Command syntax
428
For example, denying the ability to set the label foo on nodes of all graphs to the role regularUsers would
be achieved using:
Query
If no instances of this label exist in the database, then the CREATE NEW LABEL
privilege is also required.
Command syntax
For example, granting the ability to remove any label from nodes of the graph neo4j to the role
regularUsers would be achieved using:
Query
Unlike many of the other read and write privileges, it is not possible to restrict the REMOVE
LABEL privilege to specific ELEMENTS, NODES or RELATIONSHIPS.
Command syntax
For example, denying the ability to remove the label foo from nodes of all graphs to the role regularUsers
would be achieved using:
Query
429
The SET PROPERTY privilege
The SET PROPERTY privilege allows a user to set a property on a node or relationship element in a graph
using the SET clause.
Command syntax
For example, granting the ability to set any property on all elements of the graph neo4j to the role
regularUsers would be achieved using:
Query
Command syntax
For example, denying the ability to set the property foo on nodes with the label bar on all graphs to the
role regularUsers would be achieved using:
Query
If the users attempts to set a property with a property name that does not already exist
in the database the user must also possess the CREATE NEW PROPERTY NAME
privilege.
430
writes that require these privileges.
Command syntax
For example, granting MERGE on all elements of the graph neo4j to the role regularUsers would be
achieved using:
Query
It is not possible to deny the MERGE privilege. If it is desirable to prevent a users from creating elements and
setting properties, use DENY CREATE or DENY SET PROPERTY.
If the users attempts to create nodes with a label that does not already exist in the
database the user must also possess the CREATE NEW LABEL privilege. The same
applies to new relationships and properties - the CREATE NEW RELATIONSHIP TYPE
or CREATE NEW PROPERTY NAME privileges are required.
Command syntax
GRANT WRITE
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
TO role[, ...]
For example, granting the ability to write on the graph neo4j to the role regularUsers would be achieved
using:
Query
Unlike the more specific write commands, it is not possible to restrict WRITE privileges to
specific ELEMENTS, NODES or RELATIONSHIPS. If it is desirable to prevent a user from
writing to a subset of database objects, a GRANT WRITE can be combined with more
specific DENY commands to target these elements.
431
The WRITE privilege can also be denied.
Command syntax
DENY WRITE
ON {DEFAULT GRAPH | GRAPH[S] { * | name[, ...] }}
TO role[, ...]
For example, denying the ability to write on the graph neo4j to the role regularUsers would be achieved
using:
Query
Users with WRITE privilege but restricted TRAVERSE privileges will not be able to do DETACH
DELETE in all cases. See Operations Manual → Fine-grained access control for more info.
Command syntax
For example, granting all graph privileges on the graph neo4j to the role regularUsers would be achieved
using:
Query
Unlike the more specific read and write commands, it is not possible to restrict ALL GRAPH
PRIVILEGES privileges to specific ELEMENTS, NODES or RELATIONSHIPS. If it is
Command syntax
432
For example, denying all graph privileges on the graph neo4j to the role regularUsers would be achieved
using:
Query
All of the commands described in the enclosing Administration section require that the user executing the
commands has the rights to do so. These privileges can be conferred either by granting the user the admin
role, which enables all administrative rights, or by granting specific combinations of privileges.
• Database administration
◦ The database ACCESS privilege
• DBMS administration
◦ Using a custom role to manage DBMS privileges
433
◦ Granting ALL DBMS PRIVILEGES
• Manage database security for controlling the rights to perform actions on specific databases:
◦ Manage access to a database and the right to start and stop a database
◦ Manage transactions
• Manage DBMS security for controlling the rights to perform actions on the entire system:
◦ Manage multiple databases
These rights are conferred using privileges that can be managed using GRANT, DENY and REVOKE commands.
Query
Rows: 9
If the built-in admin role has been altered or dropped, and needs to be restored to its original state, see
Operations Manual → Password and user recovery.
434
Database administration
The administrators can use the following Cypher commands to manage Neo4j database administrative
rights. The components of the database privilege commands are:
• the command:
◦ GRANT – gives privileges to roles.
• database-privilege
◦ ACCESS - allows access to a specific database.
◦ INDEX [MANAGEMENT] - allows indexes to be created, deleted, and listed on the specified database.
◦ CONSTRAINT [MANAGEMENT] - allows constraints to be created, deleted, and listed on the specified
database.
◦ CREATE NEW [NODE] LABEL - allows labels to be created so that future nodes can be assigned them.
◦ CREATE NEW [RELATIONSHIP] TYPE - allows relationship types to be created, so that future
relationships can be created with these types.
◦ CREATE NEW [PROPERTY] NAME - allows property names to be created, so that nodes and
relationships can have properties with these names assigned.
◦ NAME [MANAGEMENT] - allows all of the name management capabilities: node labels, relationship
types, and property names.
◦ ALL [[DATABASE] PRIVILEGES] - allows access, index, constraint, and name management for the
specified database.
◦ SHOW TRANSACTION - allows listing transactions and queries for the specified users on the specified
database.
◦ TERMINATE TRANSACTION - allows ending transactions and queries for the specified users on the
specified database.
◦ TRANSACTION [MANAGEMENT] - allows listing and ending transactions and queries for the specified
users on the specified database.
• name
435
◦ The database to associate the privilege with.
If you delete a database and create a new one with the same name, the new one
will NOT have the privileges assigned to the deleted database.
◦ The name component can be *, which means all databases. Databases created after this command
execution will also be associated with these privileges.
◦ The DATABASE[S] name part of the command can be replaced by DEFAULT DATABASE. If you restart
the server and choose a new default database after this command execution, the new one will be
associated with these privileges.
• role[, …]
◦ The role or roles to associate the privilege with, comma-separated.
Command Description
DENY does NOT erase a granted privilege; they both exist. Use REVOKE if you want to
remove a privilege.
The hierarchy between the different database privileges is shown in the image below.
436
Figure 4. Database privileges hierarchy
Command Description
437
Command Description
The ACCESS privilege enables users to connect to a database. With ACCESS you can run calculations, for
example, RETURN 2*5 AS answer or call functions RETURN timestamp() AS time.
438
Command syntax
GRANT ACCESS
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, granting the ability to access the database neo4j to the role regularUsers is done using the
following query.
Query
Command syntax
DENY ACCESS
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, denying the ability to access to the database neo4j to the role regularUsers is done using the
following query.
Query
The privileges granted can be seen using the SHOW PRIVILEGES command:
Query
Rows: 2
The START privilege can be used to enable the ability to start a database.
439
Command syntax
GRANT START
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, granting the ability to start the database neo4j to the role regularUsers is done using the
following query.
Query
Command syntax
DENY START
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, denying the ability to start to the database neo4j to the role regularUsers is done using the
following query.
Query
The STOP privilege can be used to enable the ability to stop a database.
Command syntax
GRANT STOP
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, granting the ability to stop the database neo4j to the role regularUsers is done using the
following query.
Query
440
Command syntax
DENY STOP
ON {DEFAULT DATABASE | DATABASE[S] {* | name[, ...]}}
TO role[, ...]
For example, denying the ability to stop to the database neo4j to the role regularUsers is done using the
following query.
Query
The privileges granted can be seen using the SHOW PRIVILEGES command:
Query
Rows: 6
Note that START and STOP privileges are not included in the ALL DATABASE PRIVILEGES.
Indexes can be created, deleted, or listed with the CREATE INDEX, DROP INDEX, and SHOW INDEXES
commands. The privilege to do this can be granted with GRANT CREATE INDEX, GRANT DROP INDEX, and GRANT
SHOW INDEX commands. The privilege to do all three can be granted with GRANT INDEX MANAGEMENT
command.
Command Description
441
Command Description
For example, granting the ability to create indexes on the database neo4j to the role regularUsers is done
using the following query.
Query
The SHOW INDEXES privilege only affects the SHOW INDEXES command and not the old procedures for listing
indexes, such as db.indexes.
Constraints can be created, deleted, or listed with the CREATE CONSTRAINT, DROP CONSTRAINT and SHOW
CONSTRAINTS commands. The privilege to do this can be granted with GRANT CREATE CONSTRAINT, GRANT
DROP CONSTRAINT, GRANT SHOW CONSTRAINT commands. The privilege to do all three can be granted with
GRANT CONSTRAINT MANAGEMENT command.
Command Description
For example, granting the ability to create constraints on the database neo4j to the role regularUsers is
done using the following query.
Query
The SHOW CONSTRAINTS privilege only affects the SHOW CONSTRAINTS command and not the old procedures
for listing constraints, such as db.constraints.
442
The NAME MANAGEMENT privileges
The right to create new labels, relationship types, and property names is different from the right to create
nodes, relationships, and properties. The latter is managed using database WRITE privileges, while the
former is managed using specific GRANT/DENY CREATE NEW … commands for each type.
Table 471. Label, relationship type and property name management command syntax
Command Description
For example, granting the ability to create new properties on nodes or relationships in the database neo4j
to the role regularUsers is done using the following query.
Query
The right to access a database, create and drop indexes and constraints and create new labels,
relationship types or property names can be achieved with a single command:
Command syntax
443
Note that the privileges for starting and stopping all databases, and transaction
management, are not included in the ALL DATABASE PRIVILEGES grant. These privileges
are associated with administrators while other database privileges are of use to domain
and application developers.
For example, granting the abilities above on the database neo4j to the role databaseAdminUsers is done
using the following query.
Query
The privileges granted can be seen using the SHOW PRIVILEGES command:
Query
Rows: 1
Command Description
444
Note that the TRANSACTION MANAGEMENT privileges are not included in the ALL DATABASE
PRIVILEGES.
For example, granting the ability to list transactions for user jake in the database neo4j to the role
regularUsers is done using the following query.
Query
DBMS administration
All DBMS privileges are relevant system-wide. Like user management, they do not belong to one specific
database or graph. For more details on the differences between graphs, databases and the DBMS, refer to
Neo4j databases and graphs.
As described above, the admin role has a number of built-in privileges. These include:
445
• Create and drop databases
• Manage transactions
The easiest way to enable a user to perform these tasks is to grant them the admin role. All of these
privileges are also assignable using Cypher commands. See the sections on role management, user
management, database management, privilege management, transaction management and procedure and
user defined function security for details. It is possible to make a custom role with a subset of these
privileges.
If it is desired to have an administrator with a subset of privileges that includes all DBMS privileges, but
not all database privileges, this can be achieved by copying the admin role and revoking or denying some
privileges.
Query
Query
Query
Query
446
And DENY index and constraint management:
Query
Query
Query
The resulting role should have privileges that only allow the DBMS capabilities, like user and role
management:
Query
447
access action resource graph segment role
Rows: 15
The dbms privileges for role management are assignable using Cypher administrative commands. They
can be granted, denied and revoked like other privileges.
Command Description
The ability to add roles can be granted via the CREATE ROLE privilege. The following query shows an
example of this:
Query
448
The resulting role should have privileges that only allow adding roles:
Query
Rows: 1
The ability to delete roles can be granted via the DROP ROLE privilege. The following query shows an
example of this:
Query
The resulting role should have privileges that only allow deleting roles:
Query
Rows: 1
The ability to assign roles to users can be granted via the ASSIGN ROLE privilege. The following query
shows an example of this:
Query
The resulting role should have privileges that only allow assigning/granting roles:
449
Query
Rows: 1
The ability to remove roles from users can be granted via the REMOVE ROLE privilege. The following query
shows an example of this:
Query
The resulting role should have privileges that only allow removing/revoking roles:
Query
Rows: 1
The ability to show roles can be granted via the SHOW ROLE privilege. A user with this privilege is allowed to
execute the SHOW ROLES and SHOW POPULATED ROLES administration commands. For the SHOW ROLES WITH
USERS and SHOW POPULATED ROLES WITH USERS administration commands, both this privilege and the SHOW
USER privilege are required. The following query shows an example of how to grant the SHOW ROLE privilege:
Query
The resulting role should have privileges that only allow showing roles:
450
Query
Rows: 1
The privileges to create, delete, assign, remove, and list roles can be granted via the ROLE MANAGEMENT
privilege. The following query shows an example of this:
Query
Query
Rows: 1
The dbms privileges for user management are assignable using Cypher administrative commands. They
can be granted, denied and revoked like other privileges.
Command Description
451
Command Description
The ability to add users can be granted via the CREATE USER privilege. The following query shows an
example of this:
Query
The resulting role should have privileges that only allow adding users:
Query
452
access action resource graph segment role
Rows: 1
The ability to delete users can be granted via the DROP USER privilege. The following query shows an
example of this:
Query
The resulting role should have privileges that only allow deleting users:
Query
Rows: 1
The ability to modify users can be granted via the ALTER USER privilege. The following query shows an
example of this:
Query
The resulting role should have privileges that only allow modifying users:
Query
Rows: 1
A user that is granted ALTER USER is allowed to run the ALTER USER administration command with one or
453
several of the SET PASSWORD, SET PASSWORD CHANGE [NOT] REQUIRED and SET STATUS parts:
Query
The ability to modify users' passwords and whether those passwords must be changed upon first login
can be granted via the SET PASSWORDS privilege. The following query shows an example of this:
Query
The resulting role should have privileges that only allow modifying users' passwords and whether those
passwords must be changed upon first login:
Query
Rows: 1
A user that is granted SET PASSWORDS is allowed to run the ALTER USER administration command with one
or both of the SET PASSWORD and SET PASSWORD CHANGE [NOT] REQUIRED parts:
Query
The ability to modify the account status of users can be granted via the SET USER STATUS privilege. The
following query shows an example of this:
Query
454
The resulting role should have privileges that only allow modifying the account status of users:
Query
Rows: 1
A user that is granted SET USER STATUS is allowed to run the ALTER USER administration command with
only the SET STATUS part:
Query
Note that the combination of the SET PASSWORDS and the SET USER STATUS privilege
actions is equivalent to the ALTER USER privilege action.
The ability to show users can be granted via the SHOW USER privilege. The following query shows an
example of this:
Query
The resulting role should have privileges that only allow showing users:
Query
Rows: 1
The privileges to create, delete, modify, and list users can be granted via the USER MANAGEMENT privilege.
455
The following query shows an example of this:
Query
Query
Rows: 1
The dbms privileges for database management are assignable using Cypher administrative commands.
They can be granted, denied and revoked like other privileges.
Command Description
The ability to create databases can be granted via the CREATE DATABASE privilege. The following query
shows an example of this:
Query
456
0 rows, System updates: 1
The resulting role should have privileges that only allow creating databases:
Query
Rows: 1
The ability to delete databases can be granted via the DROP DATABASE privilege. The following query shows
an example of this:
Query
The resulting role should have privileges that only allow deleting databases:
Query
Rows: 1
The privileges to create and delete databases can be granted via the DATABASE MANAGEMENT privilege. The
following query shows an example of this:
Query
457
Query
Rows: 1
The dbms privileges for privilege management are assignable using Cypher administrative commands.
They can be granted, denied and revoked like other privileges.
Command Description
The ability to list privileges can be granted via the SHOW PRIVILEGE privilege. A user with this privilege is
allowed to execute the SHOW PRIVILEGES and SHOW ROLE roleName PRIVILEGES administration commands.
For the SHOW USER username PRIVILEGES administration command, both this privilege and the SHOW USER
privilege are required. The following query shows an example of how to grant the SHOW PRIVILEGE
privilege:
Query
458
0 rows, System updates: 1
The resulting role should have privileges that only allow showing privileges:
Query
Rows: 1
Note that no specific privileges are required for showing the current user’s privileges
using either SHOW USER username PRIVILEGES, or SHOW USER PRIVILEGES.
Please note that if a non-native auth provider like LDAP is in use, SHOW USER PRIVILEGES
will only work in a limited capacity; It is only possible for a user to show their own
privileges. Other users' privileges cannot be listed when using a non-native auth
provider.
The ability to assign privileges to roles can be granted via the ASSIGN PRIVILEGE privilege. A user with this
privilege is allowed to execute GRANT and DENY administration commands. The following query shows
an example of how to grant this privilege:
Query
The resulting role should have privileges that only allow assigning privileges:
Query
Rows: 1
The ability to remove privileges from roles can be granted via the REMOVE PRIVILEGE privilege. A user with
459
this privilege is allowed to execute REVOKE administration commands. The following query shows an
example of how to grant this privilege:
Query
The resulting role should have privileges that only allow removing privileges:
Query
Rows: 1
The privileges to list, assign, and remove privileges can be granted via the PRIVILEGE MANAGEMENT privilege.
The following query shows an example of this:
Query
Query
Rows: 1
The dbms privileges for procedure and user defined function execution are assignable using Cypher
460
administrative commands. They can be granted, denied and revoked like other privileges.
Command Description
The ability to execute a procedure can be granted via the EXECUTE PROCEDURE privilege. A user with this
privilege is allowed to execute the procedures matched by the name-globbing. The following query shows
an example of how to grant this privilege:
Query
Users with the role 'procedureExecutor' can then run any procedure in the db.schema namespace. The
procedure will be run using the users own privileges.
The resulting role should have privileges that only allow executing procedures in the db.schema
461
namespace:
Query
Rows: 1
If we want to allow executing all but a few procedures, we can grant EXECUTE PROCEDURES * and deny the
unwanted procedures. For example, the following queries allows for executing all procedures except
dbms.killTransaction and dbms.killTransactions:
Query
Query
The resulting role should have privileges that only allow executing all procedures except
dbms.killTransaction and dbms.killTransactions:
Query
Rows: 2
462
The EXECUTE BOOSTED PROCEDURE privilege
The ability to execute a procedure with elevated privileges can be granted via the EXECUTE BOOSTED
PROCEDURE privilege. A user with this privilege is allowed to execute the procedures matched by the name-
globbing without the execution being restricted to their other privileges. The following query shows an
example of how to grant this privilege:
Query
Users with the role 'boostedProcedureExecutor' can then run db.labels and db.relationshipTypes with
full privileges, seeing everything in the graph not just the labels and types that the user has TRAVERSE
privilege on.
The resulting role should have privileges that only allow executing procedures db.labels and
db.relationshipTypes, but with elevated execution:
Query
Rows: 2
While granting EXECUTE BOOSTED PROCEDURE on its own allows the procedure to be both executed and
given elevated privileges during the execution, the deny behaves slightly different and only denies the
elevation and not the execution. However, having only a granted EXECUTE BOOSTED PROCEDURE and a deny
EXECUTE BOOSTED PROCEDURE will deny the execution as well. This is explained through the examples below:
Query
Query
463
0 rows, System updates: 1
The resulting role should have privileges that allow executing all procedures using the users own
privileges, as well as blocking db.labels from being elevated. The deny EXECUTE BOOSTED PROCEDURE does
not block execution of db.labels.
Query
Rows: 2
Query
Query
The resulting role should have privileges that allow executing all procedures with elevated privileges
except db.labels which is not allowed to execute at all:
Query
464
access action resource graph segment role
Rows: 2
Example 3: Grant EXECUTE BOOSTED PROCEDURE and deny EXECUTE BOOSTED PROCEDURE
Query
Query
The resulting role should have privileges that allow executing all procedures with elevated privileges
except db.labels which is not allowed to execute at all:
Query
Rows: 2
Example 4: Grant EXECUTE PROCEDURE and EXECUTE BOOSTED PROCEDURE and deny EXECUTE BOOSTED
PROCEDURE
Query
Query
465
Query
The resulting role should have privileges that allow executing all procedures with elevated privileges
except db.labels which is only allowed to execute using the users own privileges:
Query
Rows: 3
The ability to execute admin procedures (annotated with @Admin) can be granted via the EXECUTE ADMIN
PROCEDURES privilege. This privilege is equivalent with granting the EXECUTE BOOSTED PROCEDURE privilege on
each of the admin procedures. Any new admin procedures that gets added are automatically included in
this privilege. The following query shows an example of how to grant this privilege:
Query
Users with the role 'adminProcedureExecutor' can then run any admin procedure with elevated privileges.
The resulting role should have privileges that allows executing all admin procedures:
Query
466
access action resource graph segment role
Rows: 1
The ability to execute a user defined function (UDF) can be granted via the EXECUTE USER DEFINED
FUNCTION privilege. A user with this privilege is allowed to execute the UDFs matched by the name-
globbing. The following query shows an example of how to grant this privilege:
Query
Users with the role 'functionExecutor' can then run any UDF in the apoc.coll namespace. The function will
be run using the users own privileges.
The resulting role should have privileges that only allow executing UDFs in the apoc.coll namespace:
Query
Rows: 1
If we want to allow executing all but a few UDFs, we can grant EXECUTE USER DEFINED FUNCTIONS * and
deny the unwanted functions. For example, the following queries allows for executing all UDFs except
apoc.any.property and apoc.any.properties:
Query
Query
467
The resulting role should have privileges that only allow executing all procedures except
apoc.any.property and apoc.any.properties:
Query
Rows: 2
The ability to execute a user defined function (UDF) with elevated privileges can be granted via the
EXECUTE BOOSTED USER DEFINED FUNCTION privilege. A user with this privilege is allowed to execute the
UDFs matched by the name-globbing without the execution being restricted to their other privileges. The
following query shows an example of how to grant this privilege:
Query
Users with the role 'boostedFunctionExecutor' can then run apoc.any.properties with full privileges,
seeing every property on the node/relationship not just the properties that the user has READ privilege on.
The resulting role should have privileges that only allow executing the UDF apoc.any.properties, but with
elevated execution:
Query
Rows: 1
While granting EXECUTE BOOSTED USER DEFINED FUNCTION on its own allows the UDF to be both executed
468
and given elevated privileges during the execution, the deny behaves slightly different and only denies the
elevation and not the execution. However, having only a granted EXECUTE BOOSTED USER DEFINED FUNCTION
and a deny EXECUTE BOOSTED USER DEFINED FUNCTION will deny the execution as well. This is the same
behaviour as for EXECUTE BOOSTED PROCEDURE, for examples see The EXECUTE BOOSTED PROCEDURE privilege
The name-globbing for procedure and user defined function names is a simplified version of globbing for
filename expansions, only allowing two wildcard characters — * and ?. They are used for multiple and
single character matches, where * means 0 or more characters and ? matches exactly one character.
The examples below only use procedures but the same rules apply to user defined function names. For the
examples below, assume we have the following procedures:
• mine.public.exampleProcedure
• mine.public.exampleProcedure1
• mine.public.exampleProcedure42
• mine.private.exampleProcedure
• mine.private.exampleProcedure1
• mine.private.exampleProcedure2
• your.exampleProcedure
Query
Users with the role 'globbing1' can then run procedures all the procedures.
Query
Users with the role 'globbing2' can then run procedures mine.public.exampleProcedure and
mine.private.exampleProcedure, but none of the others.
Query
Users with the role 'globbing3' can then run procedures mine.public.exampleProcedure1,
mine.private.exampleProcedure1 and mine.private.exampleProcedure2, but none of the others.
469
Query
Users with the role 'globbing4' can then run procedures your.exampleProcedure,
mine.public.exampleProcedure and mine.private.exampleProcedure, but none of the others.
Query
Users with the role 'globbing5' can then run procedures mine.public.exampleProcedure,
mine.public.exampleProcedure1 and mine.public.exampleProcedure42, but none of the others.
The right to perform the following privileges can be achieved with a single command:
• create roles
• drop roles
• assign roles
• remove roles
• show roles
• create users
• alter users
• drop users
• show users
• create databases
• drop databases
• show privileges
• assign privileges
• remove privileges
Command syntax
470
For example, granting the abilities above to the role dbmsManager is done using the following query.
Query
The privileges granted can be seen using the SHOW PRIVILEGES command:
Query
Rows: 1
All of the commands described in this chapter require that the user executing the commands has the rights
to do so. The privileges listed in the following sections are the default set of privileges for each built-in role:
Query
471
Table 512. Result
Rows: 3
The PUBLIC role can not be dropped and thus there is no need to recreate the role itself. To restore the role
to its original capabilities, two steps are needed. First, all GRANT or DENY privileges on this role should be
revoked (see output of SHOW ROLE PUBLIC PRIVILEGES AS REVOKE COMMANDS on what to revoke). Secondly,
the following queries must be run:
Query
Query
Query
The resulting PUBLIC role now has the same privileges as the original built-in PUBLIC role.
Query
472
access action resource graph segment role
Rows: 3
To restore the role to its original capabilities two steps are needed. First, if not already done, execute DROP
ROLE reader. Secondly, the following queries must be run:
Query
Query
Query
The resulting reader role now has the same privileges as the original built-in reader role.
Query
473
access action resource graph segment role
Rows: 5
To restore the role to its original capabilities two steps are needed. First, if not already done, execute DROP
ROLE editor. Secondly, the following queries must be run:
Query
Query
Query
Query
The resulting editor role now has the same privileges as the original built-in editor role.
Query
474
access action resource graph segment role
Rows: 6
To restore the role to its original capabilities two steps are needed. First, if not already done, execute DROP
ROLE publisher. Secondly, the following queries must be run:
Query
Query
Query
Query
Query
The resulting publisher role now has the same privileges as the original built-in publisher role.
475
The architect role
The architect role can do the same as the publisher, as well as create and manage indexes and
constraints.
Query
Rows: 8
To restore the role to its original capabilities two steps are needed. First, if not already done, execute DROP
ROLE architect. Secondly, the following queries must be run:
Query
Query
Query
476
Query
Query
Query
Query
The resulting architect role now has the same privileges as the original built-in architect role.
Query
477
access action resource graph segment role
Rows: 9
To restore the role to its original capabilities two steps are needed. First, if not already done, execute DROP
ROLE admin. Secondly, the following queries must be run in order to set up the privileges:
Query
Query
Query
Query
Query
Query
Query
478
Query
The queries above are enough to grant most of the full admin capabilities. Please note that the result of
executing SHOW ROLE admin PRIVILEGES now appears to be slightly different from the privileges shown for
the original built-in admin role. This does not make any functional difference.
Query
Rows: 9
Additional information about restoring the admin role can be found in the Operations Manual → Recover
the admin role.
479
However, this rule is not fully obeyed by Indexes for full-text search. These specific indexes are backed by
Lucene internally. It is therefore not possible to know for certain whether a security violation occurred for
each specific entry returned from the index. As a result, Neo4j will return zero results from full-text indexes
if it is determined that any result might violate the security privileges active for that query.
Since full-text indexes are not automatically used by Cypher, this does not lead to the case where the
same Cypher query would return different results simply because such an index got created. Users need to
explicitly call procedures to use these indexes. The problem is only that if this behavior is not understood
by the user, they might expect the full text index to return the same results that a different, but
semantically similar, Cypher query does.
Consider the following example. The database has nodes with labels :User and :Person, and these have
properties name and surname. We have indexes on both properties:
Full-text indexes support multiple labels. See Indexes for full-text search for more details
on creating and using full-text indexes.
After creating these indexes, it would appear that the latter two indexes accomplish the same thing.
However, this is not completely accurate. The composite and fulltext indexes behave in different ways and
are focused on different use cases. A key difference is that full-text indexes are backed by Lucene, and will
use the Lucene syntax for querying the index.
This has consequences for users restricted on the labels or properties involved in the indexes. Ideally, if the
labels and properties in the index are denied, we can correctly return zero results from both native indexes
and full-text indexes. However, there are borderline cases where this is not as simple.
If the user runs a query that uses the native single property index on name:
480
• do a scan on the index to create a stream of results of nodes with the name property, which leads to
five results
• filter the results to include only nodes where n.name CONTAINS 'ndy', filtering out Mark and Joe so we
have three results
• filter the results to exclude nodes that also have the denied label :Person, filtering out Mandy so we
have two results
For the above dataset, we can see we will get two results and that only one of these has the surname
property.
To use the native composite index on name and surname, the query needs to include a predicate on the
surname property as well:
MATCH (n:User) WHERE n.name CONTAINS 'ndy' AND n.surname IS NOT NULL RETURN n.name;
This query performs several checks, almost identical to the single property index query:
• do a scan on the index to create a stream of results of nodes with the name and surname property,
which leads to four results
• filter the results to include only nodes where n.name CONTAINS 'ndy', filtering out Mark and Joe so we
have two results
• filter the results to exclude nodes that also have the denied label :Person, filtering out Mandy so we only
have one result
For the above dataset, we can see we will get one result.
The problem now is that we do not know if the results provided by the index were because of a match to
the name or the surname property. The steps taken by the query engine would be:
• run a Lucene query on the full-text index to produce results containing ndy in either property, leading
to five results.
• filter the results to exclude nodes that also have the label :Person, filtering out Mandy and Joe so we
have three results.
This difference in results is due to the OR relationship between the two properties in the index creation.
Denying properties
481
Now we run the same queries again:
This query operates exactly as before, returning the same two results, because nothing in this query
relates to the denied property.
However, for the query targeting the composite index, things have changed.
MATCH (n:User) WHERE n.name CONTAINS 'ndy' AND n.surname IS NOT NULL RETURN n.name;
Since the surname property is denied, it will appear to always be null and the composite index empty.
Therefore, the query returns no result.
The problem remains, we do not know if the results provided by the index were because of a match on the
name or the surname property. Results from the surname now need to be excluded by the security rules,
because they require that the user cannot see any surname properties. However, the security model is not
able to introspect the Lucene query to know what it will actually do, whether it works only on the allowed
name property, or also on the disallowed surname property. We know that the earlier query returned a
match for Joe Andy which should now be filtered out. So, in order to never return results the user should
not be able to see, we have to block all results. The steps taken by the query engine would be:
The query will therefore return zero results in this case, rather than simply returning the results Andy and
Sandy which might be expected.
The general influence of access control privileges on graph traversal is described in detail in Graph and
sub-graph access control. The following section will only focus on nodes because of their ability to have
multiple labels. Relationships can only ever have one type and thus they do not exhibit the behavior this
section aims to clarify. While this section will not mention relationships further, the general function of the
traverse privilege also applies to them.
For any node that is traversable, due to GRANT TRAVERSE or GRANT MATCH, the user can get information about
the labels attached to the node by calling the built-in labels() function. In the case of nodes with multiple
labels, this can seemingly result in labels being returned to which the user wasn’t directly granted access
to.
482
To give an illustrative example, imagine a graph with three nodes: one labeled :A, one labeled :B and one
with :A :B. We also have a user with a role custom as defined by:
they would be returned two nodes: the node that was labeled with :A and the node with labels :A :B.
In contrast, executing
will return only the one node that has both labels: :A :B. Even though :B was not allowed access for
traversal, there is one node with that label accessible in the data because of the allowlisted label :A that is
attached to the same node.
If a user is denied traverse on a label they will never get results from any node that has this label attached
to it. Thus, the label name will never show up for them. For our example this can be done by executing:
The query
will now return the node only labeled with :A, while the query
In contrast to the normal graph traversal described in the previous section, the built-in db.labels()
procedure is not processing the data graph itself but the security rules defined on the system graph. That
means:
• if a label is denied or isn’t explicitly allowed it will not be returned by this procedure.
To reuse the example of the previous section: imagine a graph with three nodes: one labeled :A, one
labeled :B and one with :A :B. We also have a user with a role custom as defined by:
483
GRANT TRAVERSE ON GRAPH * NODES A TO custom;
CALL db.labels();
will only return label :A because that is the only label for which traversal was granted.
Let’s look at the following security rules that set up a restricted and a free role as an example:
Now, let’s look at what the database needs to do in order to execute the following query:
For both roles the execution plan will look like this:
+--------------------------+
| Operator |
+--------------------------+
| +ProduceResults |
| | +
| +NodeCountFromCountStore |
+--------------------------+
Internally however, very different operations need to be executed. The following table illustrates the
difference.
The database can access the count store and retrieve the The database cannot just access the count store because it
total number of nodes with the label :Person. must make sure that only traversable nodes with the desired
label :Person are counted. Due to this, each node with the
This is a very quick operation. :Person label needs to be accessed and examined to make
sure that it does not also have a denylisted label, such as
:Customer.
484
Chapter 6. Query tuning
This section describes query tuning for the Cypher query language.
However, when optimizing for maximum query execution performance, it may be helpful to rephrase
queries using knowledge about the domain and the application.
The overall goal of manual query performance optimization is to ensure that only necessary data is
retrieved from the graph. At the very least, data should get filtered out as early as possible in order to
reduce the amount of work that has to be done in the later stages of query execution. This also applies to
what gets returned: returning whole nodes and relationships ought to be avoided in favour of selecting
and returning only the data that is needed. You should also make sure to set an upper limit on variable
length patterns, so they don’t cover larger portions of the dataset than needed.
Each Cypher query gets optimized and transformed into an execution plan by the Cypher query planner.
To minimize the resources used for this, try to use parameters instead of literals when possible. This allows
Cypher to re-use your queries instead of having to parse and build new execution plans.
To read more about the execution plan operators mentioned in this chapter, see Execution plans.
◦ Cypher runtime
◦ Cypher replanning
• Profiling a query
◦ Index-backed property-lookup
◦ Index-backed order by
485
◦ Introduction
◦ Index hints
◦ Scan hints
◦ Join hints
Query execution can be fine-tuned through the use of query options. In order to use one or more of these
options, the query must be prepended with CYPHER, followed by the query option(s), as exemplified thus:
CYPHER query-option [further-query-options] query.
In Neo4j 4.2, the support for Cypher 3.5 is provided only at the parser level. The
consequence is that some underlying features available in Neo4j 3.5 are no longer
available and will result in runtime errors.
Please refer to the discussion in Cypher Compatibility for more information on which
features are affected.
Interpreted
In this runtime, the operators in the execution plan are chained together in a tree, where each non-leaf
486
operator feeds from one or two child operators. The tree thus comprises nested iterators, and the
records are streamed in a pipelined manner from the top iterator, which pulls from the next iterator and
so on.
Slotted
This is very similar to the interpreted runtime, except that there are additional optimizations regarding
the way in which the records are streamed through the iterators. This results in improvements to both
the performance and memory usage of the query. In effect, this can be thought of as the 'faster
interpreted' runtime.
Pipelined
The pipelined runtime was introduced in Neo4j 4.0 as a replacement for the older compiled runtime
used in the Neo4j 3.x versions. It combines some of the advantages of the compiled runtime in a new
architecture that allows for support of a wider range of queries.
Algorithms are employed to intelligently group the operators in the execution plan in order to generate
new combinations and orders of execution which are optimised for performance and memory usage.
While this should lead to superior performance in most cases (over both the interpreted and slotted
runtimes), it is still under development and does not support all possible operators or queries (the
slotted runtime covers all operators and queries).
runtime=interpreted This will force the query planner This is not used in Enterprise
to use the interpreted runtime. Edition unless explicitly asked for.
It is the only option for all queries
in Community Edition—it is not
necessary to specify this option in
Community Edition.
runtime=slotted This will cause the query planner This is the default option for all
to use the slotted runtime. queries which are not supported
by runtime=pipelined in
Enterprise Edition.
runtime=pipelined This will cause the query planner This is the default option for some
to use the pipelined runtime if it queries in Enterprise Edition.
supports the query. If the
pipelined runtime does not
support the query, the planner
will fall back to the slotted
runtime.
In Enterprise Edition, the Cypher query planner selects the runtime, falling back to alternative runtimes as
follows:
487
• If the pipelined runtime does not support the query, then fall back to use the slotted runtime.
• Finally, if the slotted runtime does not support the query, fall back to the interpreted runtime. The
interpreted runtime supports all queries, and is the only option in Neo4j Community Edition.
This table describes the available query options for the connect-components planner:
488
Query option Description Default
489
6.1.7. Cypher operator engine
This query option affects whether the pipelined runtime attempts to generate compiled code for groups of
operators.
interpretedPipesFallback=default Equivalent to X
interpretedPipesFallback=white
listed_plans_only
490
Query option Description Default
This setting is
experimental,
and using it in a
production
environment is
discouraged.
• When the query is not in the cache. This can either be when the server is first started or restarted, if
the cache has recently been cleared, or if dbms.query_cache_size was exceeded.
• When the time has past the cypher.min_replan_interval value, and the database statistics have
changed more than the cypher.statistics_divergence_threshold value.
There may be situations where Cypher query planning can occur at a non-ideal time. For example, when a
query must be as fast as possible and a valid plan is already in place.
Replanning is not performed for all queries at once; it is performed in the same thread as
running the query, and can block the query. However, replanning one query does not
replan any other queries.
491
Option Description Default
In a mixed workload, you can force replanning by using the Cypher EXPLAIN commands. This can be useful
to schedule replanning of queries which are expensive to plan, at known times of low load. Using EXPLAIN
will make sure the query is only planned, but not executed. For example:
During times of known high load, replan=skip can be useful to not introduce unwanted latency spikes.
EXPLAIN
If you want to see the execution plan but not run the statement, prepend your Cypher statement with
EXPLAIN. The statement will always return an empty result and make no changes to the database.
PROFILE
If you want to run the statement and see which operators are doing most of the work, use PROFILE. This
will run your statement and keep track of how many rows pass through each operator, and how much
each operator needs to interact with the storage layer to retrieve the necessary data. Please note that
profiling your query uses more resources, so you should not profile unless you are actively working on a
query.
See Execution plans for a detailed explanation of each of the operators contained in an execution plan.
492
Being explicit about what types and labels you expect relationships and nodes to have in
your query helps Neo4j use the best possible statistical information, which leads to
better execution plans. This means that when you know that a relationship can only be
of a certain type, you should add that to the query. The same goes for labels, where
declaring labels on both the start and end nodes of a relationship helps Neo4j find the
best way to execute the statement.
The task of tuning calls for different indexes depending on what the queries look like. Therefore, it is
important to have a fundamental understanding of how the indexes operate. This section describes the
query plans that result from different index scenarios.
Please refer to Indexes for search performance for instructions on how to create and maintain the indexes
themselves.
Query
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Page Cache Hits/Misses |
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
| +ProduceResults | person | 1 | 1 |
0 | 0/0 |
| | +----------------------------------------------------------+----------------+------
+---------+------------------------+
| +NodeIndexSeek | person:Person(firstname) WHERE firstname = $autostring_0 | 1 | 1 |
2 | 0/0 |
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
493
6.3.2. Equality check using WHERE (single-property index)
A query containing equality comparisons of a single indexed property in the WHERE clause is backed
automatically by the index. It is also possible for a query with multiple OR predicates to use multiple
indexes, if indexes exist on the properties. For example, if indexes exist on both :Label(p1) and
:Label(p2), MATCH (n:Label) WHERE n.p1 = 1 OR n.p2 = 2 RETURN n will use both indexes.
Query
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Page Cache Hits/Misses |
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
| +ProduceResults | person | 1 | 1 |
0 | 0/0 |
| | +----------------------------------------------------------+----------------+------
+---------+------------------------+
| +NodeIndexSeek | person:Person(firstname) WHERE firstname = $autostring_0 | 1 | 1 |
2 | 0/0 |
+-----------------+----------------------------------------------------------+----------------+------
+---------+------------------------+
Query
However, the query MATCH (n:Person) WHERE n.age = 35 RETURN n will not be backed by the composite
index, as the query does not contain a predicate on the country property. It will only be backed by an index
on the Person label and age property defined thus: :Person(age); i.e. a single-property index.
494
Result
+-------------------------------------------------------------------------------------------+
| n |
+-------------------------------------------------------------------------------------------+
| Node[0]{country:"UK",highScore:54321,firstname:"John",surname:"Smith",name:"john",age:35} |
+-------------------------------------------------------------------------------------------+
1 row
Query
495
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------------+----------------------------------------------------------+----------------
+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows
| DB Hits | Page Cache Hits/Misses |
+-----------------------+----------------------------------------------------------+----------------
+------+---------+------------------------+
| +ProduceResults | person | 1 | 1
| 0 | 0/0 |
| | +----------------------------------------------------------+----------------
+------+---------+------------------------+
| +NodeIndexSeekByRange | person:Person(firstname) WHERE firstname > $autostring_0 | 1 | 1
| 2 | 0/0 |
+-----------------------+----------------------------------------------------------+----------------
+------+---------+------------------------+
Query
MATCH (person:Person) WHERE person.firstname > 'B' AND person.highScore > 10000 RETURN person
496
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 0 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Filter | cache[person.highScore] > $autoint_1
| 0 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | person:Person(firstname, highScore) WHERE firstname > $autostring_0 AND
exists(highScore), cache[per | 0 | 1 | 2 | 0/0 |
| | son.highScore]
| | | | |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (person:Person) WHERE 10000 < person.highScore < 20000 RETURN person
497
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------------
+----------------------------------------------------------------------------------+----------------
+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------------
+----------------------------------------------------------------------------------+----------------
+------+---------+------------------------+
| +ProduceResults | person
| 1 | 1 | 0 | 0/0 |
| |
+----------------------------------------------------------------------------------+----------------
+------+---------+------------------------+
| +NodeIndexSeekByRange | person:Person(highScore) WHERE highScore > $autoint_0 AND highScore < $autoint_1
| 1 | 1 | 2 | 0/0 |
+-----------------------
+----------------------------------------------------------------------------------+----------------
+------+---------+------------------------+
Query
MATCH (person:Person) WHERE 10000 < person.highScore < 20000 AND exists(person.name) RETURN person
498
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 1 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | person:Person(highScore, name) WHERE highScore > $autoint_0 AND highScore < $autoint_1
AND exists(na | 1 | 1 | 2 | 0/0 |
| | me)
| | | | |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+---------------------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Page Cache Hits/Misses |
+-----------------+---------------------------------------------------------+----------------+------
+---------+------------------------+
| +ProduceResults | person | 24 | 2 |
0 | 0/0 |
| | +---------------------------------------------------------+----------------+------
+---------+------------------------+
| +NodeIndexSeek | person:Person(firstname) WHERE firstname IN $autolist_0 | 24 | 2 |
4 | 0/0 |
+-----------------+---------------------------------------------------------+----------------+------
+---------+------------------------+
499
6.3.9. List membership check using IN (composite index)
The IN predicates on person.age and person.country in the following query will use the composite index
Person(age, country) if it exists.
Query
MATCH (person:Person) WHERE person.age IN [10, 20, 35] AND person.country IN ['Sweden', 'USA',
'UK'] RETURN person
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+---------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details |
Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+---------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person |
451 | 1 | 0 | 0/0 |
| | +---------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | person:Person(age, country) WHERE age IN $autolist_0 AND country IN $autolist_1 |
451 | 1 | 10 | 0/0 |
+-----------------+---------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
500
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person |
2 | 1 | 0 | 0/0 |
| | +--------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeekByRange | person:Person(firstname) WHERE firstname STARTS WITH $autostring_0 |
2 | 1 | 2 | 0/0 |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (person:Person) WHERE person.firstname STARTS WITH 'And' AND exists(person.surname) RETURN
person
501
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+-------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+-------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 1 | 1 | 0 | 0/0 |
| |
+-------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | person:Person(firstname, surname) WHERE firstname STARTS WITH $autostring_0 AND
exists(surname) | 1 | 1 | 2 | 0/0 |
+-----------------
+-------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
502
Query Plan
Planner COST
Runtime INTERPRETED
+------------------------+------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Page Cache Hits/Misses |
+------------------------+------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person |
2 | 1 | 0 | 0/0 |
| | +------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexEndsWithScan | person:Person(firstname) WHERE firstname ENDS WITH $autostring_0 |
2 | 1 | 2 | 0/0 |
+------------------------+------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (person:Person) WHERE person.surname ENDS WITH '300' AND exists(person.age) RETURN person
503
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 11 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Filter | cache[person.surname] ENDS WITH $autostring_0
| 11 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexScan | person:Person(surname, age) WHERE exists(surname) AND exists(age),
cache[person.surname] | 106 | 303 | 304 | 0/0 |
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
504
Query Plan
Planner COST
Runtime INTERPRETED
+------------------------+-----------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Page Cache Hits/Misses |
+------------------------+-----------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person |
2 | 1 | 0 | 0/0 |
| | +-----------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexContainsScan | person:Person(firstname) WHERE firstname CONTAINS $autostring_0 |
2 | 1 | 2 | 0/0 |
+------------------------+-----------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (person:Person) WHERE person.surname CONTAINS '300' AND exists(person.age) RETURN person
505
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 11 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Filter | cache[person.surname] CONTAINS $autostring_0
| 11 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexScan | person:Person(surname, age) WHERE exists(surname) AND exists(age),
cache[person.surname] | 106 | 303 | 304 | 0/0 |
+-----------------
+------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
506
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+---------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page
Cache Hits/Misses |
+-----------------+---------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | p | 2 | 2 | 0 |
0/0 |
| | +---------------------------------------------+----------------+------+---------
+------------------------+
| +NodeIndexScan | p:Person(firstname) WHERE exists(firstname) | 2 | 2 | 3 |
0/0 |
+-----------------+---------------------------------------------+----------------+------+---------
+------------------------+
Query
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------+--------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+--------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | p |
1 | 2 | 0 | 0/0 |
| | +--------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexScan | p:Person(firstname, surname) WHERE exists(firstname) AND exists(surname) |
1 | 2 | 3 | 0/0 |
+-----------------+--------------------------------------------------------------------------
+----------------+------+---------+------------------------+
507
6.3.18. Spatial distance searches (single-property index)
If a property with point values is indexed, the index is used for spatial distance searches as well as for
range queries.
Query
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | `p.location`
| 0 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Projection | cache[p.location] AS `p.location`
| 0 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Filter | distance(cache[p.location], point({x: $autoint_0, y: $autoint_1})) < $autoint_2
| 0 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeekByRange | p:Person(location) WHERE distance(location, point($autoint_0, $autoint_1)) <
$autoint_2, cache[p.loc | 0 | 9 | 10 | 0/0 |
| | ation]
| | | | |
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (p:Person) WHERE distance(p.place, point({x: 1, y: 2})) < 2 AND exists(p.name) RETURN p.place
508
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | `p.place`
| 69 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Projection | cache[p.place] AS `p.place`
| 69 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +Filter | distance(cache[p.place], point({x: $autoint_0, y: $autoint_1})) < $autoint_2
| 69 | 9 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | p:Person(place, name) WHERE distance(place, point($autoint_0, $autoint_1)) <
$autoint_2 AND exists(n | 69 | 9 | 10 | 0/0 |
| | ame), cache[p.place]
| | | | |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
MATCH (person:Person) WHERE point({x: 1, y: 5}) < person.location < point({x: 2, y: 6}) RETURN person
509
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 0 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeekByRange | person:Person(location) WHERE location > point({x: $autoint_0, y: $autoint_1})
AND location < point( | 0 | 1 | 2 | 0/0 |
| | {x: $autoint_2, y: $autoint_3})
| | | | |
+-----------------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Query
510
Query Plan
Planner COST
Runtime INTERPRETED
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +ProduceResults | person
| 0 | 1 | 0 | 0/0 |
| |
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
| +NodeIndexSeek | person:Person(place, firstname) WHERE place > point({x: $autoint_0, y: $autoint_1})
AND place < poin | 0 | 1 | 2 | 0/0 |
| | t({x: $autoint_2, y: $autoint_3}) AND exists(firstname)
| | | | |
+-----------------
+------------------------------------------------------------------------------------------------------
+----------------+------+---------+------------------------+
Let’s say we want to write a query to find 'Tom Hanks'. The naive way of doing this would be to write the
following:
511
This query will find the 'Tom Hanks' node but as the number of nodes in the database increase it will
become slower and slower. We can profile the query to find out why that is.
You can learn more about the options for profiling queries in Profiling a query but in this case we’re going
to prefix our query with PROFILE:
PROFILE
MATCH (p { name: 'Tom Hanks' })
RETURN p
Planner COST
Runtime INTERPRETED
+-----------------+------------------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+------------------------+----------------+------+---------+------------------------+
| +ProduceResults | p | 16 | 1 | 0 | 0/0 |
| | +------------------------+----------------+------+---------+------------------------+
| +Filter | p.name = $autostring_0 | 16 | 1 | 163 | 0/0 |
| | +------------------------+----------------+------+---------+------------------------+
| +AllNodesScan | p | 163 | 163 | 164 | 0/0 |
+-----------------+------------------------+----------------+------+---------+------------------------+
The first thing to keep in mind when reading execution plans is that you need to read from the bottom up.
In that vein, starting from the last row, the first thing we notice is that the value in the Rows column seems
high given there is only one node with the name property 'Tom Hanks' in the database. If we look across
to the Operator column we’ll see that AllNodesScan has been used which means that the query planner
scanned through all the nodes in the database.
This seems like an inefficient way of finding 'Tom Hanks' given that we are looking at many nodes that
aren’t even people and therefore aren’t what we’re looking for.
The solution to this problem is that whenever we’re looking for a node we should specify a label to help
the query planner narrow down the search space. For this query we’d need to add a Person label.
This query will be faster than the first one but as the number of people in our database increase we again
notice that the query slows down.
PROFILE
MATCH (p:Person { name: 'Tom Hanks' })
RETURN p
512
Compiler CYPHER 4.2
Planner COST
Runtime INTERPRETED
+------------------+------------------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+------------------+------------------------+----------------+------+---------+------------------------+
| +ProduceResults | p | 13 | 1 | 0 | 0/0 |
| | +------------------------+----------------+------+---------+------------------------+
| +Filter | p.name = $autostring_0 | 13 | 1 | 125 | 0/0 |
| | +------------------------+----------------+------+---------+------------------------+
| +NodeByLabelScan | p:Person | 125 | 125 | 126 | 0/0 |
+------------------+------------------------+----------------+------+---------+------------------------+
This time the Rows value on the last row has reduced so we’re not scanning some nodes that we were
before which is a good start. The NodeByLabelScan operator indicates that we achieved this by first doing
a linear scan of all the Person nodes in the database.
Once we’ve done that we again scan through all those nodes using the Filter operator, comparing the
name property of each one.
This might be acceptable in some cases but if we’re going to be looking up people by name frequently
then we’ll see better performance if we create an index on the name property for the Person label:
PROFILE
MATCH (p:Person { name: 'Tom Hanks' })
RETURN p
513
Compiler CYPHER 4.2
Planner COST
Runtime INTERPRETED
+-----------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page
Cache Hits/Misses |
+-----------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | p | 1 | 1 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
0/0 |
+-----------------+-------------------------------------------+----------------+------+---------
+------------------------+
Our execution plan is down to a single row and uses the Node Index Seek operator which does an index
seek (see Indexes for search performance) to find the appropriate node.
• Introduction
• Index-backed property-lookup
◦ Aggregating functions
• Index-backed order by
◦ min() and max()
◦ Restrictions
6.5.1. Introduction
One of the most important and useful ways of optimizing Cypher queries involves creating appropriate
indexes. This is described in more detail in Indexes for search performance, and demonstrated in Basic
query tuning example. In summary, an index will be based on the combination of a Label and a property.
Any Cypher query that searches for nodes with a specific label and some predicate on the property
(equality, range or existence) will be planned to use the index if the cost planner deems that to be the most
efficient solution.
In order to benefit from enhancements provided by native indexes, it is useful to understand when index-
backed property lookup and index-backed order by will come into play. In Neo4j 3.4 and earlier, the fact
514
that the index contains the property value, and the results are returned in a specific order, was not used
improve the performance of any later part of the query that might depend on the property value or result
order.
Let’s explain how to use these features with a more advanced query tuning example.
If you are upgrading an existing store to 4.2.5, it may be necessary to drop and re-create
existing indexes. For information on native index support and upgrade considerations
regarding indexes, see Operations Manual → Indexes.
CALL db.awaitIndexes
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name STARTS WITH 'Tom'
RETURN p.name, count(m)
515
+---------------------------+
| p.name | count(m) |
+---------------------------+
| "Tom Cruise" | 3 |
| "Tom Hanks" | 12 |
| "Tom Skerritt" | 1 |
+---------------------------+
3 rows
We have asked the database to return all the actors with the first name 'Tom'. There are three of them:
'Tom Cruise', 'Tom Skerritt' and 'Tom Hanks'. In previous versions of Neo4j, the final clause RETURN p.name
would cause the database to take the node p and look up its properties and return the value of the
property name. With native indexes, however, we can leverage the fact that indexes store the property
values. In this case, it means that the names can be looked up directly from the index. This allows Cypher
to avoid the second call to the database to find the property, which can save time on very large queries.
If we profile the above query, we see that the NodeIndexSeekByRange in the Details column contains
cache[p.name], which means that p.name is retrieved from the index. We can also see that the
OrderedAggregation has no DB Hits, which means it does not have to access the database again.
PROFILE
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name STARTS WITH 'Tom'
RETURN p.name, count(m)
Planner COST
Runtime INTERPRETED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Ordered by |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +ProduceResults | `p.name`, `count(m)` |
1 | 3 | 0 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +OrderedAggregation | cache[p.name] AS `p.name`, count(m) AS `count(m)` |
1 | 3 | 0 | 0 | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +Filter | m:Movie |
1 | 16 | 16 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +Expand(All) | (p)-[anon_17:ACTED_IN]->(m) |
1 | 16 | 20 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
1 | 4 | 5 | | 0/0 | p.name ASC |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
If we change the query, such that it can no longer use an index, we will see that there will be no
cache[p.name] in the Variables, and that the EagerAggregation now has DB Hits, since it accesses the
516
database again to retrieve the name.
PROFILE
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, count(m)
Planner COST
Runtime INTERPRETED
+-------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses |
+-------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +ProduceResults | `p.name`, `count(m)` | 13 | 102 | 0 |
| 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +EagerAggregation | p.name AS `p.name`, count(m) AS `count(m)` | 13 | 102 | 172 |
13280 | 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +Filter | p:Person | 172 | 172 | 172 |
| 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +Expand(All) | (m)<-[anon_17:ACTED_IN]-(p) | 172 | 172 | 210 |
| 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +NodeByLabelScan | m:Movie | 38 | 38 | 39 |
| 0/0 |
+-------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
For non-native indexes there will still be a second database access to retrieve those values.
• Range (e.g. WHERE n.uid > 1000 AND n.uid < 2000)
• Several predicates of the above types combined using OR, given that all of them are on the same
property (e.g. WHERE n.prop < 10 OR n.prop = 'infinity' )
517
Aggregating functions
For all built-in aggregating functions in Cypher, the index-backed property-lookup optimization can be
used even without a predicate. Consider this query which returns the number of distinct names of people
in the movies dataset:
PROFILE
MATCH (p:Person)
RETURN count(DISTINCT p.name) AS numberOfNames
Planner COST
Runtime INTERPRETED
+-------------------+--------------------------------------------------+----------------+------+---------
+----------------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses |
+-------------------+--------------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +ProduceResults | numberOfNames | 1 | 1 | 0 |
| 0/0 |
| | +--------------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +EagerAggregation | count(DISTINCT cache[p.name]) AS numberOfNames | 1 | 1 | 0 |
9856 | 0/0 |
| | +--------------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +NodeIndexScan | p:Person(name) WHERE exists(name), cache[p.name] | 125 | 125 | 126 |
| 0/0 |
+-------------------+--------------------------------------------------+----------------+------+---------
+----------------+------------------------+
Note that the NodeIndexScan in the Variables column contains cache[p.name] and that the
EagerAggregation has no DB Hits. In this case, the semantics of aggregating functions works like an
implicit existence predicate because Person nodes without the property name will not affect the result of an
aggregation.
PROFILE
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name STARTS WITH 'Tom'
RETURN p.name, count(m)
ORDER BY p.name
518
Compiler CYPHER 4.2
Planner COST
Runtime INTERPRETED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Ordered by |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +ProduceResults | `p.name`, `count(m)` |
1 | 3 | 0 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +OrderedAggregation | cache[p.name] AS `p.name`, count(m) AS `count(m)` |
1 | 3 | 0 | 0 | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +Filter | m:Movie |
1 | 16 | 16 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +Expand(All) | (p)-[anon_17:ACTED_IN]->(m) |
1 | 16 | 20 | | 0/0 | p.name ASC |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
1 | 4 | 5 | | 0/0 | p.name ASC |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+------------+
We are asking for the results in ascending alphabetical order. The native index happens to store String
properties in ascending alphabetical order, and Cypher knows this. In Neo4j 3.4 and earlier, Cypher would
plan a Sort operation to sort the results, which means building a collection in memory and running a sort
algorithm on it. For large result sets this can be expensive in terms of both memory and time. In Neo4j 3.5
and later, Cypher will recognize that the index already returns data in the correct order, and skip the Sort
operation.
The Order column describes the order of rows after each operator. We see that the Order column contains
p.name ASC from the index seek operation, meaning that the rows are ordered by p.name in ascending
order.
Index-backed order by can also be used for queries that expect their results is descending order, but with
slightly lower performance.
In cases where the Cypher planner is unable to remove the Sort operator, the planner
can utilize knowledge of the ORDER BY clause to plan the Sort operator at a point in the
plan with optimal cardinality.
519
PROFILE
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN min(p.name) AS name
+----------------+
| name |
+----------------+
| "Aaron Sorkin" |
+----------------+
1 row
Aggregations are usually using the EagerAggregation operation. This would mean scanning all nodes in
the index to find the name that is first in alphabetic order. Instead, the query is planned with Projection,
followed by Limit, followed by Optional. This will simply pick the first value from the index.
Planner COST
Runtime INTERPRETED
+-------------------+-----------------------------+----------------+------+---------+----------------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) |
Page Cache Hits/Misses |
+-------------------+-----------------------------+----------------+------+---------+----------------
+------------------------+
| +ProduceResults | name | 1 | 1 | 0 | |
0/0 |
| | +-----------------------------+----------------+------+---------+----------------
+------------------------+
| +EagerAggregation | min(p.name) AS name | 1 | 1 | 172 | 0 |
0/0 |
| | +-----------------------------+----------------+------+---------+----------------
+------------------------+
| +Filter | p:Person | 172 | 172 | 172 | |
0/0 |
| | +-----------------------------+----------------+------+---------+----------------
+------------------------+
| +Expand(All) | (m)<-[anon_17:ACTED_IN]-(p) | 172 | 172 | 210 | |
0/0 |
| | +-----------------------------+----------------+------+---------+----------------
+------------------------+
| +NodeByLabelScan | m:Movie | 38 | 38 | 39 | |
0/0 |
+-------------------+-----------------------------+----------------+------+---------+----------------
+------------------------+
Index-backed order by can also be used for corresponding queries with the max function, but with slightly
lower performance.
Restrictions
The optimization can only work on native indexes. It does not work for predicates only querying for the
spatial type Point. Predicates that can be used to enable this optimization are:
520
• Equality (e.g. WHERE n.name = 'Tom Hanks')
• Range (e.g. WHERE n.uid > 1000 AND n.uid < 2000)
• Equality or range predicates querying for points (e.g. WHERE n.place > point({ x: 1, y: 2 }))
Forcing planner behavior is an advanced feature, and should be used with caution by
• Introduction
• Index hints
• Scan hints
• Join hints
6.6.1. Introduction
When executing a query, Neo4j needs to decide where in the query graph to start matching. This is done
by looking at the MATCH clause and the WHERE conditions and using that information to find useful indexes,
or other starting points.
However, the selected index might not always be the best choice. Sometimes multiple indexes are possible
candidates, and the query planner picks the suboptimal one from a performance point of view. Moreover,
521
in some circumstances (albeit rarely) it is better not to use an index at all.
Neo4j can be forced to use a specific starting point through the USING keyword. This is called giving a
planner hint. There are four types of planner hints: index hints, scan hints, join hints, and the PERIODIC
COMMIT query hint.
Graph
N0 [
label = "{Scientist|name = \'Liskov\'\lborn = 1939\l}"
]
N0 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N0 -> N1 [
color = "#4e9a06"
fontcolor = "#4e9a06"
label = "KNOWS\n"
]
N1 [
label = "{Scientist|name = \'Wing\'\lborn = 1956\l}"
]
N1 -> N4 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N1 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N2 [
label = "{Science|name = \'Computer Science\'\l}"
]
N3 [
label = "{Scientist|name = \'Conway\'\lborn = 1938\l}"
]
N3 -> N2 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N4 [
label = "{Science|name = \'Engineering\'\l}"
]
N5 [
label = "{Science|name = \'Chemistry\'\l}"
]
N6 [
label = "{Scientist|name = \'Curie\'\lborn = 1867\l}"
]
N6 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N7 [
label = "{Scientist|name = \'Arden\'\l}"
]
N7 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N8 [
label = "{Scientist|name = \'Franklin\'\l}"
]
522
N8 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
N9 [
label = "{Scientist|name = \'Harrison\'\l}"
]
N9 -> N5 [
color = "#2e3436"
fontcolor = "#2e3436"
label = "RESEARCHED\n"
]
Query
The following query will be used in some of the examples on this page. It has intentionally been
constructed in such a way that the statistical information will be inaccurate for the particular path that this
query matches. For this reason, it can be improved by supplying planner hints.
Query plan
Planner COST
Runtime PIPELINED
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.043 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | $autoint_3 AS column
| 0 | 1 | 0 | | 0/0 | 0.015 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | wing
| 0 | 1 | 0 | 640 | | 0.041 | In Pipeline 5
|
| |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | not anon_122 = anon_68
| 0 | 2 | 0 | | 0/0 | 0.050 | In Pipeline 4
|
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
523
| | +NodeHashJoin | cs
| 0 | 3 | 0 | 768 | 0/0 | 0.116 | In Pipeline 4
|
| | |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(Into) | (cs)<-[anon_122:RESEARCHED]-(conway)
| 0 | 1 | 5 | 984 | 0/0 | 0.248 | In Pipeline 3
|
| | | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_1, conway:Scientist(name) WHERE name
= $autostring_2 | 0 | 1 | 4 | 72 | 2/0 | 0.189 |
In Pipeline 2 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | wing:Scientist
| 0 | 3 | 3 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(All) | (cs)<-[anon_68:RESEARCHED]-(wing)
| 0 | 3 | 4 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeIndexSeek | cs:Science(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 1 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | wing:Scientist
| 0 | 1 | 1 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(All) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 3 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 0 |
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
524
It is possible to supply several index hints, but keep in mind that several starting points will require the use
of a potentially expensive join later in the query plan.
Query
Query plan
Planner COST
Runtime PIPELINED
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.054 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | liskov.born AS column
| 0 | 1 | 3 | | 1/0 | 0.041 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | wing
| 0 | 1 | 0 | 640 | | 0.040 | In Pipeline 5
|
| |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | not anon_122 = anon_68
| 0 | 2 | 0 | | 0/0 | 0.094 | In Pipeline 4
|
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeHashJoin | cs
| 0 | 3 | 0 | 768 | 0/0 | 0.070 | In Pipeline 4
|
525
| | |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(Into) | (cs)<-[anon_122:RESEARCHED]-(conway)
| 0 | 1 | 5 | 984 | 0/0 | 0.145 | In Pipeline 3
|
| | | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_1, conway:Scientist(name) WHERE name
= $autostring_2 | 0 | 1 | 4 | 72 | 2/0 | 0.141 |
In Pipeline 2 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | wing:Scientist
| 0 | 3 | 3 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(All) | (cs)<-[anon_68:RESEARCHED]-(wing)
| 0 | 3 | 4 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeIndexSeek | cs:Science(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 1 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | wing:Scientist
| 0 | 1 | 1 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(All) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 3 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 0 |
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
526
Query
Query plan
Planner COST
Runtime PIPELINED
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.139 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | liskov.born AS column
| 0 | 1 | 3 | | 1/0 | 0.025 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | wing
| 0 | 1 | 0 | 640 | | 0.031 | In Pipeline 5
|
| |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | not anon_122 = anon_68
| 0 | 2 | 0 | | 0/0 | 0.053 | In Pipeline 4
|
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeHashJoin | cs
| 0 | 3 | 0 | 768 | 0/0 | 0.105 | In Pipeline 4
|
| | |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(Into) | (cs)<-[anon_122:RESEARCHED]-(conway)
| 0 | 1 | 5 | 984 | 0/0 | 0.075 | In Pipeline 3
|
| | | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_1, conway:Scientist(name) WHERE name
= $autostring_2 | 0 | 1 | 4 | 72 | 2/0 | 0.226 |
In Pipeline 2 |
| | |
527
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | wing:Scientist
| 0 | 3 | 3 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(All) | (cs)<-[anon_68:RESEARCHED]-(wing)
| 0 | 3 | 4 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeIndexSeek | cs:Science(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 1 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | wing:Scientist
| 0 | 1 | 1 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(All) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 3 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 0 |
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
Query
Returns the year 'Barbara Liskov' was born, using a slightly better plan.
Query plan
Planner COST
Runtime PIPELINED
528
Runtime version 4.2
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.095 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | liskov.born AS column
| 0 | 1 | 3 | | 1/0 | 0.026 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | wing
| 0 | 1 | 0 | 640 | | 0.028 | In Pipeline 5
|
| |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | not anon_122 = anon_68
| 0 | 2 | 0 | | 0/0 | 0.044 | In Pipeline 4
|
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeHashJoin | cs
| 0 | 3 | 0 | 768 | 0/0 | 0.064 | In Pipeline 4
|
| | |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(Into) | (cs)<-[anon_122:RESEARCHED]-(conway)
| 0 | 1 | 5 | 984 | 0/0 | 0.095 | In Pipeline 3
|
| | | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_1, conway:Scientist(name) WHERE name
= $autostring_2 | 0 | 1 | 4 | 72 | 2/0 | 0.136 |
In Pipeline 2 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | wing:Scientist
| 0 | 3 | 3 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(All) | (cs)<-[anon_68:RESEARCHED]-(wing)
| 0 | 3 | 4 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeIndexSeek | cs:Science(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 1 |
529
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | wing:Scientist
| 0 | 1 | 1 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(All) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 3 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 0 |
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
Query
MATCH (s:Scientist)
USING SCAN s:Scientist
WHERE s.born < 1939
RETURN s.born AS column
530
Query plan
Planner COST
Runtime PIPELINED
+------------------+----------------------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other
|
+------------------+----------------------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | column | 3 | 2 | 0 | | Fused
in Pipeline 0 |
| | +----------------------------+----------------+------+---------+----------------
+---------------------+
| +Projection | cache[s.born] AS column | 3 | 2 | 0 | | Fused
in Pipeline 0 |
| | +----------------------------+----------------+------+---------+----------------
+---------------------+
| +Filter | cache[s.born] < $autoint_0 | 3 | 2 | 18 | | Fused
in Pipeline 0 |
| | +----------------------------+----------------+------+---------+----------------
+---------------------+
| +NodeByLabelScan | s:Scientist | 10 | 7 | 8 | 72 | Fused
in Pipeline 0 |
+------------------+----------------------------+----------------+------+---------+----------------
+---------------------+
Query
531
Returns the birth date of 'Jeanette Wing', using a slightly better plan.
Query plan
Planner COST
Runtime PIPELINED
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.046 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | wing.born AS column
| 0 | 1 | 3 | | 1/0 | 0.155 | In Pipeline 5
|
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | wing
| 0 | 1 | 0 | 640 | | 0.033 | In Pipeline 5
|
| |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | not anon_122 = anon_68
| 0 | 2 | 0 | | 0/0 | 0.048 | In Pipeline 4
|
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeHashJoin | cs
| 0 | 3 | 0 | 768 | 0/0 | 0.085 | In Pipeline 4
|
| | |\
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(Into) | (cs)<-[anon_122:RESEARCHED]-(conway)
| 0 | 1 | 5 | 984 | 0/0 | 0.143 | In Pipeline 3
|
| | | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_1, conway:Scientist(name) WHERE name
= $autostring_2 | 0 | 1 | 4 | 72 | 2/0 | 0.144 |
In Pipeline 2 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Filter | wing:Scientist
| 0 | 3 | 3 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
532
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(All) | (cs)<-[anon_68:RESEARCHED]-(wing)
| 0 | 3 | 4 | | | | Fused in
Pipeline 1 |
| | |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeIndexSeek | cs:Science(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 1 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | wing:Scientist
| 0 | 1 | 1 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(All) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 3 | | | | Fused in
Pipeline 0 |
| |
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 0 |
+-------------------------
+------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
Query
Query plan
Planner COST
Runtime PIPELINED
+-----------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| Operator | Details
533
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other
|
+-----------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +ProduceResults | column
| 0 | 1 | 0 | | 0/0 | 0.046 | In Pipeline 6
|
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Projection | wing.born AS column
| 0 | 1 | 3 | | 0/0 | 0.023 | In Pipeline 6
|
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Filter | not anon_136 = anon_82
| 0 | 1 | 0 | | 0/0 | 0.038 | In Pipeline 6
|
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +NodeHashJoin | cs, liskov
| 0 | 1 | 0 | 720 | 0/0 | 0.164 | In Pipeline 6
|
| |\
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +NodeHashJoin | wing
| 0 | 1 | 0 | 652 | | 0.055 | In Pipeline 5
|
| | |\
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Filter | cache[cs.name] = $autostring_2 AND cs:Science
| 0 | 1 | 5 | | | | Fused in
Pipeline 4 |
| | | |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +Expand(All) | (wing)-[anon_82:RESEARCHED]->(cs)
| 0 | 2 | 4 | | | | Fused in
Pipeline 4 |
| | | |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | | +NodeIndexSeek | wing:Scientist(name) WHERE name = $autostring_1
| 0 | 1 | 2 | 72 | | | Fused in
Pipeline 4 |
| | |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +Expand(Into) | (liskov)-[anon_41:KNOWS]->(wing)
| 0 | 1 | 4 | 984 | 1/0 | 0.062 | In Pipeline 3
|
| | |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| | +MultiNodeIndexSeek | liskov:Scientist(name) WHERE name = $autostring_0, wing:Scientist(name) WHERE
name = $autostring_1 | 0 | 1 | 4 | 72 | 2/0 |
0.149 | In Pipeline 2 |
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +Expand(Into) | (cs)<-[anon_136:RESEARCHED]-(liskov)
| 0 | 1 | 5 | 984 | 2/0 | 0.155 | In Pipeline 1
|
534
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
| +MultiNodeIndexSeek | cs:Science(name) WHERE name = $autostring_2, cache[cs.name],
| 0 | 1 | 4 | 72 | 2/0 | 1.273 | In Pipeline 0
|
| | liskov:Scientist(name) WHERE name = $autostring_0, cache[cs.name]
| | | | | | |
|
+-----------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+---------------------+
For this situation only, Cypher provides the global USING PERIODIC COMMIT query hint for updating queries
using LOAD CSV. If required, the limit for the number of rows per commit may be set as follows: USING
PERIODIC COMMIT 500.
PERIODIC COMMIT will process the rows until the number of rows reaches a limit. Then the current
transaction will be committed and replaced with a newly opened transaction. If no limit is set, a default
value will be used.
See Importing large amounts of data in LOAD CSV for examples of USING PERIODIC COMMIT with and
without setting the number of rows per commit.
Using PERIODIC COMMIT will prevent running out of memory when importing large
amounts of data. However, it will also break transactional isolation and thus it should
only be used where needed.
The USE clause can not be used together with the PERIODIC COMMIT clause.
535
Chapter 7. Execution plans
This section describes the characteristics of query execution plans and provides details
about each of the operators.
Introduction
The task of executing a query is decomposed into operators, each of which implements a specific piece of
work. The operators are combined into a tree-like structure called an execution plan. Each operator in the
execution plan is represented as a node in the tree. Each operator takes as input zero or more rows, and
produces as output zero or more rows. This means that the output from one operator becomes the input
for the next operator. Operators that join two branches in the tree combine input from two incoming
streams and produce a single output.
Evaluation model
Evaluation of the execution plan begins at the leaf nodes of the tree. Leaf nodes have no input rows and
generally comprise operators such as scans and seeks. These operators obtain the data directly from the
storage engine, thus incurring database hits. Any rows produced by leaf nodes are then piped into their
parent nodes, which in turn pipe their output rows to their parent nodes and so on, all the way up to the
root node. The root node produces the final results of the query.
However, some operators, such as those used for aggregation and sorting, need to aggregate all their
rows before they can produce output. Such operators need to complete execution in its entirety before any
rows are sent to their parents as input. These operators are called eager operators, and are denoted as
such in Execution plan operators at a glance. Eagerness can cause high memory usage and may therefore
be the cause of query performance issues.
Statistics
Each operator is annotated with statistics.
Rows
The number of rows that the operator produced. This is only available if the query was profiled.
EstimatedRows
This is the estimated number of rows that is expected to be produced by the operator. The estimate is
an approximate number based on the available statistical information. The compiler uses this estimate
to choose a suitable execution plan.
536
DbHits
Each operator will ask the Neo4j storage engine to do work such as retrieving or updating data. A
database hit is an abstract unit of this storage engine work. The actions triggering a database hit are
listed in Database hits (DbHits).
Page Cache Hits, Page Cache Misses, Page Cache Hit Ratio
These metrics are only shown for some queries when using Neo4j Enterprise Edition. The page cache is
used to cache data and avoid accessing disk, so having a high number of hits and a low number of
misses will typically make the query run faster. Whenever several operators are fused together for more
efficient execution we can no longer associate this metric with a given operator and then nothing will
appear here.
Time
Time is only shown for some operators when using the pipelined runtime. The number shown is the
time in milliseconds it took to execute the given operator. Whenever several operators are fused
together for more efficient execution we can no longer associate a duration with a given operator and
then nothing will appear here.
To produce an efficient plan for a query, the Cypher query planner requires information about the Neo4j
database. This information includes which indexes and constraints are available, as well as various
statistics maintained by the database. The Cypher query planner uses this information to determine which
access patterns will produce the best execution plan.
4. The number of relationships by type, ending with or starting from a node with a specific label.
Information about how the statistics are kept up to date, as well as configuration options for managing
query replanning and caching, can be found in the Operations Manual → Statistics and execution plans.
Query tuning describes how to tune Cypher queries. In particular, see Profiling a query for how to view the
execution plan for a query and Planner hints and the USING keyword for how to use hints to influence the
decisions of the planner when building an execution plan for a query.
For a deeper understanding of how each operator works, refer to Execution plan operators at a glance and
the linked sections per operator. Please remember that the statistics of the particular database where the
queries run will decide the plan used. There is no guarantee that a specific query will always be solved
with the same plan.
• Leaf operators, in most cases, locate the starting nodes and relationships required in order to execute
the query.
537
• Updating operators are used in queries that update the graph.
• Eager operators accumulate all their rows before piping them to the next operator.
538
Name Description Leaf? Updating? Considerations
539
Name Description Leaf? Updating? Considerations
540
Name Description Leaf? Updating? Considerations
541
Name Description Leaf? Updating? Considerations
542
Name Description Leaf? Updating? Considerations
543
Name Description Leaf? Updating? Considerations
544
Name Description Leaf? Updating? Considerations
We list below all the actions that trigger one or more database hits:
545
• Create actions
◦ Create a node
◦ Create a relationship
• Delete actions
◦ Delete a node
◦ Delete a relationship
• Update actions
◦ Set one or more labels on a node
• Node-specific actions
◦ Get a node by its ID
• Relationship-specific actions
◦ Get a relationship by its ID
• General actions
◦ Get the name of a property key by its ID, or its ID by the key name
• Schema actions
◦ Add an index
◦ Drop an index
546
◦ Get the reference of an index
◦ Create a constraint
◦ Drop a constraint
• Call a procedure
The presented value can vary slightly depending on the Cypher runtime that was used to
execute the query. In the pipelined runtime the number of database hits will typically be
higher since it uses a more accurate way of measuring.
Certain operators are only used by a subset of the runtimes that Cypher can choose from. If that is the
case, the example queries will be prefixed with an option to choose one of these runtimes.
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+---------+----------------+------+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other |
+-----------------+---------+----------------+------+---------+----------------+---------------------+
| +ProduceResults | n | 35 | 35 | 0 | | Fused in Pipeline 0 |
| | +---------+----------------+------+---------+----------------+---------------------+
| +AllNodesScan | n | 35 | 35 | 36 | 72 | Fused in Pipeline 0 |
+-----------------+---------+----------------+------+---------+----------------+---------------------+
547
Query
MATCH (n1)-[r]->()
WHERE id(r) = 0
RETURN r, n1
Query Plan
Planner COST
Runtime PIPELINED
+-------------------------------+----------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Other |
+-------------------------------+----------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | r, n1 | 1 | 1 |
0 | | Fused in Pipeline 0 |
| | +----------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +DirectedRelationshipByIdSeek | (n1)-[r]->(anon_17) WHERE id(r) = $autoint_0 | 1 | 1 |
1 | 72 | Fused in Pipeline 0 |
+-------------------------------+----------------------------------------------+----------------+------
+---------+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+----------------------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other
|
+-----------------+----------------------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | n | 1 | 1 | 0 | | Fused
in Pipeline 0 |
| | +----------------------------+----------------+------+---------+----------------
+---------------------+
| +NodeByIdSeek | n WHERE id(n) = $autoint_0 | 1 | 1 | 1 | 72 | Fused
in Pipeline 0 |
+-----------------+----------------------------+----------------+------+---------+----------------
+---------------------+
548
7.3.4. Node By Label Scan
The NodeByLabelScan operator fetches all nodes with a specific label from the node label index.
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------+---------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other
|
+------------------+---------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | person | 14 | 14 | 0 | | Fused in Pipeline
0 |
| | +---------------+----------------+------+---------+----------------
+---------------------+
| +NodeByLabelScan | person:Person | 14 | 14 | 15 | 72 | Fused in Pipeline
0 |
+------------------+---------------+----------------+------+---------+----------------
+---------------------+
Query
549
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+----------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Other |
+-----------------+----------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | location | 0 | 1 | 0 |
| Fused in Pipeline 0 |
| | +----------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeIndexSeek | location:Location(name) WHERE name = $autostring_0 | 0 | 1 | 2 |
72 | Fused in Pipeline 0 |
+-----------------+----------------------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime PIPELINED
+----------------------+------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits
| Memory (Bytes) | Other |
+----------------------+------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | t | 0 | 0 | 0
| | Fused in Pipeline 0 |
| | +------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeUniqueIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0 | 0 | 0 | 1
| 72 | Fused in Pipeline 0 |
+----------------------+------------------------------------------------+----------------+------+---------
+----------------+---------------------+
550
7.3.7. Multi Node Index Seek
The MultiNodeIndexSeek operator finds nodes using multiple index seeks. It supports using multiple
distinct indexes for different nodes in the query. The node variables and the indexes used are shown in the
arguments of the operator.
The operator yields a cartesian product of all index seeks. For example, if the operator does two seeks and
the first seek finds the nodes a1, a2 and the second b1, b2, b3, the MultiNodeIndexSeek will yield the
rows (a1, b1), (a1, b2), (a1, b3), (a2, b1), (a2, b2), (a2, b3).
Query
CYPHER runtime=pipelined
MATCH (location:Location {name: 'Malmo'}), (person:Person {name: 'Bob'}) RETURN location, person
Query Plan
Planner COST
Runtime PIPELINED
+---------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+---------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +ProduceResults | location, person
| 0 | 1 | 0 | | 2/0 | 0.253 | In Pipeline 0 |
| |
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +MultiNodeIndexSeek | location:Location(name) WHERE name = $autostring_0, person:Person(name) WHERE name
= $autostring_1 | 0 | 1 | 4 | 72 | 0/2 | 1.614 |
In Pipeline 0 |
+---------------------
+----------------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
Query
551
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+-------------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Other |
+-----------------------+-------------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | l | 2 | 1 |
0 | | Fused in Pipeline 0 |
| | +-------------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +NodeIndexSeekByRange | l:Location(name) WHERE name STARTS WITH $autostring_0 | 2 | 1 |
2 | 72 | Fused in Pipeline 0 |
+-----------------------+-------------------------------------------------------+----------------+------
+---------+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------------+----------------------------------------------------------+----------------
+------+---------+----------------+---------------------+
| Operator | Details | Estimated Rows
| Rows | DB Hits | Memory (Bytes) | Other |
+-----------------------------+----------------------------------------------------------+----------------
+------+---------+----------------+---------------------+
| +ProduceResults | t | 2
| 0 | 0 | | Fused in Pipeline 0 |
| | +----------------------------------------------------------+----------------
+------+---------+----------------+---------------------+
| +NodeUniqueIndexSeekByRange | UNIQUE t:Team(name) WHERE name STARTS WITH $autostring_0 | 2
| 0 | 1 | 72 | Fused in Pipeline 0 |
+-----------------------------+----------------------------------------------------------+----------------
+------+---------+----------------+---------------------+
552
7.3.10. Node Index Contains Scan
The NodeIndexContainsScan operator examines all values stored in an index, searching for entries
containing a specific string; for example, in queries including CONTAINS. Although this is slower than an
index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label
scan using NodeByLabelScan, and a property store filter.
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------------+----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Memory (Bytes) | Other |
+------------------------+----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | l | 0 | 2 |
0 | | Fused in Pipeline 0 |
| | +----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +NodeIndexContainsScan | l:Location(name) WHERE name CONTAINS $autostring_0 | 0 | 2 |
3 | 72 | Fused in Pipeline 0 |
+------------------------+----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
Query
553
Query Plan
Planner COST
Runtime PIPELINED
+------------------------+-----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Other |
+------------------------+-----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | l | 0 | 0 |
0 | | Fused in Pipeline 0 |
| | +-----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +NodeIndexEndsWithScan | l:Location(name) WHERE name ENDS WITH $autostring_0 | 0 | 0 |
1 | 72 | Fused in Pipeline 0 |
+------------------------+-----------------------------------------------------+----------------+------
+---------+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+-------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes)
| Other |
+-----------------+-------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | l | 10 | 10 | 0 |
| Fused in Pipeline 0 |
| | +-------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeIndexScan | l:Location(name) WHERE exists(name) | 10 | 10 | 11 | 72
| Fused in Pipeline 0 |
+-----------------+-------------------------------------+----------------+------+---------
+----------------+---------------------+
554
7.3.13. Undirected Relationship By Id Seek
The UndirectedRelationshipByIdSeek operator reads one or more relationships by id from the relationship
store. As the direction is unspecified, two rows are produced for each relationship as a result of alternating
the combination of the start and end node.
Query
MATCH (n1)-[r]-()
WHERE id(r) = 1
RETURN r, n1
Query Plan
Planner COST
Runtime PIPELINED
+---------------------------------+---------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Other |
+---------------------------------+---------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | r, n1 | 1 | 2 |
0 | | Fused in Pipeline 0 |
| | +---------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +UndirectedRelationshipByIdSeek | (n1)-[r]-(anon_16) WHERE id(r) = $autoint_0 | 1 | 2 |
1 | 72 | Fused in Pipeline 0 |
+---------------------------------+---------------------------------------------+----------------+------
+---------+----------------+---------------------+
7.3.14. Apply
All the different Apply operators (listed below) share the same basic functionality: they perform a nested
loop by taking a single row from the left-hand side, and using the Argument operator on the right-hand
side, execute the operator tree on the right-hand side. The versions of the Apply operators differ in how
the results are managed. The Apply operator (i.e. the standard version) takes the row produced by the
right-hand side — which at this point contains data from both the left-hand and right-hand sides — and
yields it..
Query
555
Query Plan
Planner COST
Runtime PIPELINED
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | p, q | 1 | 0 | 0 |
| | | Fused in Pipeline 1 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Apply | | 1 | 0 | 0 |
| 0/0 | | |
| |\ +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +NodeIndexSeek | q:Person(name) WHERE name = p.secondName | 1 | 0 | 0 |
80 | | | Fused in Pipeline 1 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | 0/1 | 0.217 | In Pipeline 0 |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
Query
CYPHER runtime=slotted
MATCH (p:Person)
WHERE (p)-[:FRIENDS_WITH]->(:Person)
RETURN p.name
556
Query Plan
Planner COST
Runtime SLOTTED
+------------------+---------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache
Hits/Misses |
+------------------+---------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `p.name` | 11 | 2 | 0 |
0/0 |
| | +---------------------------------------+----------------+------+---------
+------------------------+
| +Projection | p.name AS `p.name` | 11 | 2 | 2 |
1/0 |
| | +---------------------------------------+----------------+------+---------
+------------------------+
| +SemiApply | | 11 | 2 | 0 |
0/0 |
| |\ +---------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_45:Person | 2 | 0 | 2 |
1/0 |
| | | +---------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (p)-[anon_27:FRIENDS_WITH]->(anon_45) | 2 | 2 | 33 |
15/0 |
| | | +---------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | p | 14 | 14 | 0 |
0/0 |
| | +---------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 |
2/0 |
+------------------+---------------------------------------+----------------+------+---------
+------------------------+
Query
CYPHER runtime=slotted
MATCH (me:Person {name: "me"}), (other:Person)
WHERE NOT (me)-[:FRIENDS_WITH]->(other)
RETURN other.name
557
Query Plan
Planner COST
Runtime SLOTTED
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses |
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +ProduceResults | `other.name` | 4 | 13 | 0 |
| 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +Projection | other.name AS `other.name` | 4 | 13 | 13 |
| 2/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +AntiSemiApply | | 4 | 13 | 0 |
| 0/0 |
| |\ +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| | +Expand(Into) | (me)-[anon_62:FRIENDS_WITH]->(other) | 0 | 0 | 55 |
888 | 15/0 |
| | | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| | +Argument | me, other | 14 | 14 | 0 |
| 0/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +CartesianProduct | | 14 | 14 | 0 |
| 0/0 |
| |\ +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| | +NodeByLabelScan | other:Person | 14 | 14 | 15 |
| 2/0 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+
| +NodeIndexSeek | me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
| 0/1 |
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+
7.3.17. Anti
The Anti operator tests for the absence of a pattern. If there are incoming rows, the Anti operator will yield
no rows. If there are no incoming rows, the Anti operator will yield a single row.
Query
CYPHER runtime=pipelined
MATCH (me:Person {name: "me"}), (other:Person)
WHERE NOT (me)-[:FRIENDS_WITH]->(other)
RETURN other.name
558
Query Plan
Planner COST
Runtime PIPELINED
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `other.name` | 4 | 13 | 0 |
| 0/0 | 0.085 | In Pipeline 4 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | other.name AS `other.name` | 4 | 13 | 26 |
| 2/0 | 0.074 | In Pipeline 4 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Apply | | 4 | 13 | 0 |
| 0/0 | | |
| |\ +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Anti | | 14 | 13 | 0 |
| 0/0 | 0.080 | In Pipeline 4 |
| | | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Limit | 1 | 14 | 1 | 0 |
| | | Fused in Pipeline 3 |
| | | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(Into) | (me)-[anon_62:FRIENDS_WITH]->(other) | 0 | 1 | 55 |
2848 | | | Fused in Pipeline 3 |
| | | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Argument | me, other | 14 | 14 | 0 |
408 | | | Fused in Pipeline 3 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +CartesianProduct | | 14 | 14 | 0 |
296 | 0/0 | 0.078 | In Pipeline 2 |
| |\ +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +NodeByLabelScan | other:Person | 14 | 14 | 15 |
88 | 2/0 | 0.058 | In Pipeline 1 |
| | +--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeIndexSeek | me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | 0/1 | 0.273 | In Pipeline 0 |
+--------------------+--------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
559
Query
CYPHER runtime=slotted
MATCH (other:Person)
WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location)
RETURN other.name
Query Plan
Planner COST
Runtime SLOTTED
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page
Cache Hits/Misses |
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `other.name` | 13 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Projection | other.name AS `other.name` | 13 | 14 | 14 |
1/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +SelectOrSemiApply | anon_27 | 14 | 14 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_87:Location | 15 | 0 | 12 |
0/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_73:WORKS_IN]->(anon_87) | 15 | 12 | 26 |
12/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 12 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +LetSemiApply | | 14 | 14 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_53:Person | 2 | 0 | 2 |
1/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_35:FRIENDS_WITH]->(anon_53) | 2 | 2 | 33 |
15/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15 |
2/0 |
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
560
7.3.19. Let Anti Semi Apply
The LetAntiSemiApply operator tests for the absence of a pattern, and is a variation of the Apply operator.
When a query contains multiple negated pattern predicates — i.e. predicates separated with OR, where at
least one predicate contains NOT — LetAntiSemiApply will be used to evaluate the first of these. It will
record the result of evaluating the predicate but will leave any filtering to another operator. In the example,
LetAntiSemiApply will be used to check for the absence of the FRIENDS_WITH relationship from each person.
Query
CYPHER runtime=slotted
MATCH (other:Person)
WHERE NOT ((other)-[:FRIENDS_WITH]->(:Person)) OR (other)-[:WORKS_IN]->(:Location)
RETURN other.name
561
Query Plan
Planner COST
Runtime SLOTTED
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page
Cache Hits/Misses |
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `other.name` | 11 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Projection | other.name AS `other.name` | 11 | 14 | 14 |
1/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +SelectOrSemiApply | anon_32 | 14 | 14 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_93:Location | 15 | 0 | 2 |
0/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_79:WORKS_IN]->(anon_93) | 15 | 2 | 7 |
2/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 2 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +LetAntiSemiApply | | 14 | 14 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_58:Person | 2 | 0 | 2 |
1/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_40:FRIENDS_WITH]->(anon_58) | 2 | 2 | 33 |
15/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15 |
2/0 |
+--------------------+-------------------------------------------+----------------+------+---------
+------------------------+
562
Query
MATCH (other:Person)
WHERE other.age > 25 OR (other)-[:FRIENDS_WITH]->(:Person)
RETURN other.name
Query Plan
Planner COST
Runtime PIPELINED
+--------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+--------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `other.name` | 11 | 2 | 0 |
| | | Fused in Pipeline 2 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | other.name AS `other.name` | 11 | 2 | 4 |
| | | Fused in Pipeline 2 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +SelectOrSemiApply | other.age > $autoint_0 | 14 | 2 | 0 |
128 | | | Fused in Pipeline 2 |
| |\ +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Limit | 1 | 14 | 2 | 0 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Filter | anon_71:Person | 2 | 2 | 2 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(All) | (other)-[anon_53:FRIENDS_WITH]->(anon_71) | 2 | 2 | 32 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Argument | other | 14 | 14 | 0 |
296 | | | Fused in Pipeline 1 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15 |
72 | 2/0 | 0.122 | In Pipeline 0 |
+--------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
563
Query
MATCH (other:Person)
WHERE other.age > 25 OR NOT (other)-[:FRIENDS_WITH]->(:Person)
RETURN other.name
Query Plan
Planner COST
Runtime PIPELINED
+------------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `other.name` | 4 | 12 | 0 |
| | | Fused in Pipeline 3 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | other.name AS `other.name` | 4 | 12 | 24 |
| | | Fused in Pipeline 3 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +SelectOrAntiSemiApply | other.age > $autoint_0 | 14 | 12 | 0 |
448 | | | Fused in Pipeline 3 |
| |\ +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Anti | | 14 | 12 | 0 |
0 | 0/0 | 0.156 | In Pipeline 2 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Limit | 1 | 14 | 2 | 0 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Filter | anon_75:Person | 2 | 2 | 2 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(All) | (other)-[anon_57:FRIENDS_WITH]->(anon_75) | 2 | 2 | 32 |
| | | Fused in Pipeline 1 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Argument | other | 14 | 14 | 0 |
296 | | | Fused in Pipeline 1 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15 |
72 | 2/0 | 0.101 | In Pipeline 0 |
+------------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
564
Query
CYPHER runtime=slotted
MATCH (other:Person)
WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) OR other.age = 5
RETURN other.name
Query Plan
Planner COST
Runtime SLOTTED
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Page Cache Hits/Misses |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `other.name` | 13 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Projection | other.name AS `other.name` | 13 | 14 | 14 |
1/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +SelectOrSemiApply | anon_27 | 14 | 14 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_87:Location | 15 | 0 | 12 |
0/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_73:WORKS_IN]->(anon_87) | 15 | 12 | 26 |
12/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 12 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +LetSelectOrSemiApply | other.age = $autoint_0 | 14 | 14 | 14 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_53:Person | 2 | 0 | 2 |
1/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_35:FRIENDS_WITH]->(anon_53) | 2 | 2 | 33 |
15/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 14 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15 |
2/0 |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
565
7.3.23. Let Select Or Anti Semi Apply
The LetSelectOrAntiSemiApply operator is planned for negated pattern predicates — i.e. pattern
predicates preceded with NOT — that are combined with other predicates using OR. This operator is a
variation of the Apply operator.
Query
CYPHER runtime=slotted
MATCH (other:Person)
WHERE NOT (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) OR other.age = 5
RETURN other.name
566
Query Plan
Planner COST
Runtime SLOTTED
+---------------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits
| Page Cache Hits/Misses |
+---------------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `other.name` | 11 | 14 | 0
| 0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Projection | other.name AS `other.name` | 11 | 14 | 14
| 1/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +SelectOrSemiApply | anon_31 | 14 | 14 | 0
| 0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_91:Location | 15 | 0 | 2
| 0/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_77:WORKS_IN]->(anon_91) | 15 | 2 | 7
| 2/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 2 | 0
| 0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +LetSelectOrAntiSemiApply | other.age = $autoint_0 | 14 | 14 | 14
| 0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | anon_57:Person | 2 | 0 | 2
| 1/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (other)-[anon_39:FRIENDS_WITH]->(anon_57) | 2 | 2 | 33
| 15/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | other | 14 | 14 | 0
| 0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | other:Person | 14 | 14 | 15
| 2/0 |
+---------------------------+-------------------------------------------+----------------+------+---------
+------------------------+
567
Query
Query Plan
Planner COST
Runtime SLOTTED
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Page Cache Hits/Misses |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +EmptyResult | | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +AntiConditionalApply | | 1 | 1 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +MergeCreateNode | p | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +ConditionalApply | | 1 | 1 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +SetProperty | p.exists = true | 1 | 1 | 3 |
2/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | p | 1 | 1 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Optional | | 1 | 1 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
0/1 |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
568
Query
Query Plan
Planner COST
Runtime SLOTTED
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Page Cache Hits/Misses |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +EmptyResult | | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +AntiConditionalApply | | 1 | 1 | 0 |
0/0 |
| |\ +-------------------------------------------+----------------+------+---------
+------------------------+
| | +SetProperty | p.exists = true | 1 | 0 | 0 |
0/0 |
| | | +-------------------------------------------+----------------+------+---------
+------------------------+
| | +MergeCreateNode | p | 1 | 0 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +Optional | | 1 | 1 | 0 |
0/0 |
| | +-------------------------------------------+----------------+------+---------
+------------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
0/1 |
+-----------------------+-------------------------------------------+----------------+------+---------
+------------------------+
Query
CYPHER runtime=slotted
MATCH (p:Person)
RETURN p.name, [ (p)-[:WORKS_IN]->(location) | location.name ] AS cities
569
Query Plan
Planner COST
Runtime SLOTTED
+------------------+------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache
Hits/Misses |
+------------------+------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `p.name`, cities | 14 | 14 | 0 |
0/0 |
| | +------------------------------------+----------------+------+---------
+------------------------+
| +Projection | p.name AS `p.name` | 14 | 14 | 14 |
0/0 |
| | +------------------------------------+----------------+------+---------
+------------------------+
| +RollUpApply | cities, anon_32 | 14 | 14 | 0 |
0/0 |
| |\ +------------------------------------+----------------+------+---------
+------------------------+
| | +Projection | location.name AS anon_32 | 0 | 15 | 15 |
2/0 |
| | | +------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (p)-[anon_38:WORKS_IN]->(location) | 0 | 15 | 33 |
15/0 |
| | | +------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | p | 1 | 14 | 0 |
0/0 |
| | +------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 |
2/0 |
+------------------+------------------------------------+----------------+------+---------
+------------------------+
7.3.27. Argument
The Argument operator indicates the variable to be used as an argument to the right-hand side of an Apply
operator.
Query
Query Plan
Planner COST
Runtime SLOTTED
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
570
Hits | Memory (Bytes) | Page Cache Hits/Misses |
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +ProduceResults | | 1 | 0 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +EmptyResult | | 1 | 0 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +Apply | | 1 | 1 |
0 | | 0/0 |
| |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +AntiConditionalApply | | 1 | 1 |
0 | | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +MergeCreateRelationship | (s)-[anon_40:FRIENDS_WITH]->(s) | 1 | 1 |
2 | | 2/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +AntiConditionalApply | | 1 | 1 |
0 | | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Optional | s | 1 | 1 |
0 | | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Expand(Into) | (s)-[anon_40:FRIENDS_WITH]->(s) | 0 | 0 |
4 | 888 | 1/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +LockNodes | s | 1 | 1 |
0 | | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Optional | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Expand(Into) | (s)-[anon_40:FRIENDS_WITH]->(s) | 0 | 0 |
4 | 888 | 2/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +NodeIndexSeek | s:Person(name) WHERE name = $autostring_0 | 1 | 1 |
2 | | 0/1 |
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
571
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | fof | 0 | 1 | 0 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Expand(All) | (p)-[anon_30:FRIENDS_WITH]->(fof) | 0 | 1 | 3 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | Fused in Pipeline 0 |
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
572
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | fof | 0 | 0 | 0 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Filter | not anon_30 = anon_53 | 0 | 0 | 0 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Expand(Into) | (p)-[anon_30:FRIENDS_WITH]->(fof) | 0 | 0 | 0 |
0 | Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Expand(All) | (p)<-[anon_53]-(fof) | 0 | 0 | 3 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | Fused in Pipeline 0 |
+-----------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
MATCH (p:Person)
OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > 180
RETURN p, l
573
Query Plan
Planner COST
Runtime PIPELINED
+----------------------+-------------------------------------------------------------------
+----------------+------+---------+----------------+---------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Other |
+----------------------+-------------------------------------------------------------------
+----------------+------+---------+----------------+---------------------+
| +ProduceResults | p, l |
14 | 15 | 1 | | Fused in Pipeline 0 |
| | +-------------------------------------------------------------------
+----------------+------+---------+----------------+---------------------+
| +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 |
14 | 15 | 47 | | Fused in Pipeline 0 |
| | +-------------------------------------------------------------------
+----------------+------+---------+----------------+---------------------+
| +NodeByLabelScan | p:Person |
14 | 14 | 15 | 72 | Fused in Pipeline 0 |
+----------------------+-------------------------------------------------------------------
+----------------+------+---------+----------------+---------------------+
Query
574
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+------------------------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes)
| Other |
+-----------------------+------------------------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | p | 15 | 15 | 0 |
| Fused in Pipeline 0 |
| | +------------------------------+----------------+------+---------+----------------
+---------------------+
| +OptionalExpand(Into) | (l)-[anon_61]->(p) | 15 | 15 | 105 | 3352
| Fused in Pipeline 0 |
| | +------------------------------+----------------+------+---------+----------------
+---------------------+
| +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 33 |
| Fused in Pipeline 0 |
| | +------------------------------+----------------+------+---------+----------------
+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 72
| Fused in Pipeline 0 |
+-----------------------+------------------------------+----------------+------+---------+----------------
+---------------------+
Query
575
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+-----------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | p, q | 4 | 6 | 0 |
| Fused in Pipeline 0 |
| | +------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Filter | q:Person | 4 | 6 | 6 |
| Fused in Pipeline 0 |
| | +------------------------------------+----------------+------+---------
+----------------+---------------------+
| +VarLengthExpand(All) | (p)-[anon_17:FRIENDS_WITH*..2]-(q) | 4 | 6 | 47 |
128 | Fused in Pipeline 0 |
| | +------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 |
72 | Fused in Pipeline 0 |
+-----------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
576
Query Plan
Planner COST
Runtime PIPELINED
+------------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+------------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | p | 0 | 0 | 0 |
| Fused in Pipeline 0 |
| | +------------------------------------+----------------+------+---------
+----------------+---------------------+
| +VarLengthExpand(Into) | (p)-[anon_17:FRIENDS_WITH*..2]-(p) | 0 | 0 | 47 |
128 | Fused in Pipeline 0 |
| | +------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 |
72 | Fused in Pipeline 0 |
+------------------------+------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
577
Query Plan
Planner COST
Runtime PIPELINED
+---------------------------+------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+---------------------------+------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +ProduceResults | p, q | 0 | 0 | 0 |
| 0/0 | 0.015 | In Pipeline 1 |
| | +------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +Distinct | p, q | 0 | 0 | 0 |
192 | 0/0 | 1.615 | In Pipeline 1 |
| | +------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +Filter | q:Person | 0 | 0 | 0 |
| 0/0 | 0.063 | In Pipeline 1 |
| | +------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +VarLengthExpand(Pruning) | (p)-[:FRIENDS_WITH*3..4]-(q) | 0 | 0 | 32 |
896 | | | In Pipeline 1 |
| | +------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 |
72 | 2/0 | 0.081 | In Pipeline 0 |
+---------------------------+------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
Query
578
Query Plan
Planner COST
Runtime SLOTTED
+---------------------------------+------------------------------------------------+----------------
+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows
| DB Hits | Page Cache Hits/Misses |
+---------------------------------+------------------------------------------------+----------------
+------+---------+------------------------+
| +ProduceResults | | 1 | 0
| 0 | 0/0 |
| | +------------------------------------------------+----------------
+------+---------+------------------------+
| +EmptyResult | | 1 | 0
| 0 | 0/0 |
| | +------------------------------------------------+----------------
+------+---------+------------------------+
| +AntiConditionalApply | | 1 | 1
| 0 | 0/0 |
| |\ +------------------------------------------------+----------------
+------+---------+------------------------+
| | +MergeCreateNode | t | 1 | 0
| 0 | 0/0 |
| | +------------------------------------------------+----------------
+------+---------+------------------------+
| +Optional | | 1 | 1
| 0 | 0/0 |
| | +------------------------------------------------+----------------
+------+---------+------------------------+
| +AssertSameNode | t | 0 | 1
| 0 | 0/0 |
| |\ +------------------------------------------------+----------------
+------+---------+------------------------+
| | +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(id) WHERE id = $autoint_1 | 1 | 1
| 1 | 0/1 |
| | +------------------------------------------------+----------------
+------+---------+------------------------+
| +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(name) WHERE name = $autostring_0 | 0 | 1
| 1 | 0/1 |
+---------------------------------+------------------------------------------------+----------------
+------+---------+------------------------+
Query
CYPHER runtime=slotted
MATCH (p) WHERE false RETURN p
579
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+---------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+---------+----------------+------+---------+------------------------+
| +ProduceResults | p | 0 | 0 | 0 | 0/0 |
| | +---------+----------------+------+---------+------------------------+
| +DropResult | | 0 | 0 | 0 | 0/0 |
| | +---------+----------------+------+---------+------------------------+
| +AllNodesScan | p | 35 | 0 | 0 | 0/0 |
+-----------------+---------+----------------+------+---------+------------------------+
Query
CREATE (:Person)
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+-----------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+-----------------+----------------+------+---------+------------------------+
| +ProduceResults | | 1 | 0 | 0 | 0/0 |
| | +-----------------+----------------+------+---------+------------------------+
| +EmptyResult | | 1 | 0 | 0 | 0/0 |
| | +-----------------+----------------+------+---------+------------------------+
| +Create | (anon_8:Person) | 1 | 1 | 1 | 0/0 |
+-----------------+-----------------+----------------+------+---------+------------------------+
Query
580
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+---------+----------------+------+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other |
+-----------------+---------+----------------+------+---------+----------------+---------------------+
| +ProduceResults | n | 35 | 35 | 0 | | Fused in Pipeline 0 |
| | +---------+----------------+------+---------+----------------+---------------------+
| +AllNodesScan | n | 35 | 35 | 36 | 72 | Fused in Pipeline 0 |
+-----------------+---------+----------------+------+---------+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+---------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+---------+----------------+------+---------+------------------------+
| +ProduceResults | line | 10 | 4 | 0 | 0/0 |
| | +---------+----------------+------+---------+------------------------+
| +LoadCSV | line | 10 | 4 | 0 | 0/0 |
+-----------------+---------+----------------+------+---------+------------------------+
In query plans, the build input is always the left operator, and the probe input the right operator.
• NodeHashJoin
581
• ValueHashJoin
• NodeLeftOuterHashJoin
• NodeRightOuterHashJoin
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------+----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------+----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `loc.name` | 10 | 0 | 0 |
| 0/0 | 0.000 | In Pipeline 2 |
| | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | loc.name AS `loc.name` | 10 | 0 | 0 |
| 0/0 | 0.000 | In Pipeline 2 |
| | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Filter | not anon_32 = anon_51 | 10 | 0 | 0 |
| 0/0 | 0.000 | In Pipeline 2 |
| | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeHashJoin | loc | 10 | 0 | 0 |
496 | | 0.025 | In Pipeline 2 |
| |\ +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(All) | (matt)-[anon_51:WORKS_IN]->(loc) | 19 | 0 | 0 |
| | | Fused in Pipeline 1 |
| | | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +NodeIndexSeek | matt:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 |
72 | | | Fused in Pipeline 1 |
| | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Expand(All) | (bob)-[anon_32:WORKS_IN]->(loc) | 19 | 1 | 3 |
| | | Fused in Pipeline 0 |
| | +----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeIndexSeek | bob:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | | | Fused in Pipeline 0 |
+------------------+----------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
582
7.3.42. Value Hash Join
The ValueHashJoin operator is a variation of the hash join. This operator allows for arbitrary values to be
used as the join key. It is most frequently used to solve predicates of the form: n.prop1 = m.prop2 (i.e.
equality predicates between two property columns).
Query
MATCH (p:Person),(q:Person)
WHERE p.age = q.age
RETURN p,q
Query Plan
Planner COST
Runtime PIPELINED
+--------------------+---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses | Time (ms) | Other |
+--------------------+---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +ProduceResults | p, q | 0 | 0 | 0 | |
0/0 | 0.011 | In Pipeline 2 |
| | +---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +ValueHashJoin | p.age = q.age | 0 | 0 | 0 | 432 |
0/0 | 0.163 | In Pipeline 2 |
| |\ +---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| | +NodeByLabelScan | q:Person | 14 | 14 | 15 | 72 |
2/0 | 0.071 | In Pipeline 1 |
| | +---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 72 |
2/0 | 0.106 | In Pipeline 0 |
+--------------------+---------------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
583
Query
MATCH (a:Person)
OPTIONAL MATCH (a)-->(b:Person)
USING JOIN ON a
RETURN a.name, b.name
Query Plan
Planner COST
Runtime PIPELINED
+-------------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+-------------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `a.name`, `b.name` | 14 | 14 |
0 | | 0/0 | 0.180 | In Pipeline 2 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +Projection | cache[a.name] AS `a.name`, cache[b.name] AS `b.name` | 14 | 14 |
24 | | 0/0 | 0.089 | In Pipeline 2 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +NodeRightOuterHashJoin | a | 14 | 14 |
0 | 1072 | 0/0 | 0.357 | In Pipeline 2 |
| |\ +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| | +NodeByLabelScan | a:Person | 14 | 14 |
15 | 72 | 2/0 | 0.153 | In Pipeline 1 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +CacheProperties | cache[a.name], cache[b.name] | 2 | 2 |
6 | | | | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +Expand(All) | (b)<-[anon_36]-(a) | 2 | 2 |
33 | | | | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | b:Person | 14 | 14 |
15 | 72 | | | Fused in Pipeline 0 |
+-------------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
Query
584
Query Plan
Planner COST
Runtime SLOTTED
+-------------------+------------------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page
Cache Hits/Misses |
+-------------------+------------------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | `other.name` | 0 | 2 | 0 |
0/0 |
| | +------------------------------------------+----------------+------+---------
+------------------------+
| +Projection | other.name AS `other.name` | 0 | 2 | 2 |
2/0 |
| | +------------------------------------------+----------------+------+---------
+------------------------+
| +TriadicSelection | WHERE NOT (me)--(other) | 0 | 2 | 0 |
0/0 |
| |\ +------------------------------------------+----------------+------+---------
+------------------------+
| | +Filter | not anon_18 = anon_37 | 0 | 2 | 0 |
0/0 |
| | | +------------------------------------------+----------------+------+---------
+------------------------+
| | +Expand(All) | (anon_35)-[anon_37:FRIENDS_WITH]-(other) | 0 | 6 | 14 |
4/0 |
| | | +------------------------------------------+----------------+------+---------
+------------------------+
| | +Argument | me, anon_18, anon_35 | 4 | 4 | 0 |
0/0 |
| | +------------------------------------------+----------------+------+---------
+------------------------+
| +Expand(All) | (me)-[anon_18:FRIENDS_WITH]-(anon_35) | 4 | 4 | 33 |
15/0 |
| | +------------------------------------------+----------------+------+---------
+------------------------+
| +NodeByLabelScan | me:Person | 14 | 14 | 15 |
2/0 |
+-------------------+------------------------------------------+----------------+------+---------
+------------------------+
Query
585
Query Plan
Planner COST
Runtime PIPELINED
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `other.name` | 0 | 2 | 0 |
| 0/0 | 0.054 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | other.name AS `other.name` | 0 | 2 | 4 |
| 2/0 | 0.108 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +TriadicFilter | WHERE NOT (me)--(other) | 0 | 2 | 0 |
808 | 0/0 | 0.082 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Apply | | 0 | 2 | 0 |
| 0/0 | | |
| |\ +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Filter | not anon_18 = anon_37 | 0 | 2 | 0 |
| | | Fused in Pipeline 2 |
| | | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(All) | (anon_35)-[anon_37:FRIENDS_WITH]-(other) | 0 | 6 | 14 |
| | | Fused in Pipeline 2 |
| | | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Argument | me, anon_18, anon_35 | 4 | 4 | 0 |
192 | | | Fused in Pipeline 2 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +TriadicBuild | (me)--(anon_35) | 4 | 4 | 0 |
328 | 0/0 | 0.190 | In Pipeline 1 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Expand(All) | (me)-[anon_18:FRIENDS_WITH]-(anon_35) | 4 | 4 | 33 |
| | | Fused in Pipeline 0 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | me:Person | 14 | 14 | 15 |
72 | | | Fused in Pipeline 0 |
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
586
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +ProduceResults | `other.name` | 0 | 2 | 0 |
| 0/0 | 0.135 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Projection | other.name AS `other.name` | 0 | 2 | 4 |
| 2/0 | 0.148 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +TriadicFilter | WHERE NOT (me)--(other) | 0 | 2 | 0 |
808 | 0/0 | 0.517 | In Pipeline 3 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Apply | | 0 | 2 | 0 |
| 0/0 | | |
| |\ +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Filter | not anon_18 = anon_37 | 0 | 2 | 0 |
| | | Fused in Pipeline 2 |
| | | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Expand(All) | (anon_35)-[anon_37:FRIENDS_WITH]-(other) | 0 | 6 | 14 |
| | | Fused in Pipeline 2 |
| | | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| | +Argument | me, anon_18, anon_35 | 4 | 4 | 0 |
192 | | | Fused in Pipeline 2 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +TriadicBuild | (me)--(anon_35) | 4 | 4 | 0 |
328 | 0/0 | 4.973 | In Pipeline 1 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +Expand(All) | (me)-[anon_18:FRIENDS_WITH]-(anon_35) | 4 | 4 | 33 |
| | | Fused in Pipeline 0 |
| | +------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | me:Person | 14 | 14 | 15 |
72 | | | Fused in Pipeline 0 |
+------------------+------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------------+
587
Query
Query Plan
Planner COST
Runtime PIPELINED
+--------------------+----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses | Time (ms) | Other |
+--------------------+----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +ProduceResults | p, t | 140 | 140 | 0 | |
2/0 | 2.041 | In Pipeline 2 |
| | +----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +CartesianProduct | | 140 | 140 | 0 | 288 |
0/0 | 0.311 | In Pipeline 2 |
| |\ +----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| | +NodeByLabelScan | p:Person | 14 | 14 | 15 | 88 |
2/0 | 0.033 | In Pipeline 1 |
| | +----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
| +NodeByLabelScan | t:Team | 10 | 10 | 11 | 72 |
2/0 | 0.075 | In Pipeline 0 |
+--------------------+----------+----------------+------+---------+----------------
+------------------------+-----------+---------------+
7.3.48. Foreach
The Foreach operator executes a nested loop between the left child operator and the right child operator.
In an analogous manner to the Apply operator, it takes a row from the left-hand side and, using the
Argument operator, provides it to the operator tree on the right-hand side. Foreach will yield all the rows
coming in from the left-hand side; all results from the right-hand side are pulled in and discarded.
Query
588
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+--------------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+--------------------+----------------+------+---------+------------------------+
| +ProduceResults | | 1 | 0 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +EmptyResult | | 1 | 0 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 |
| |\ +--------------------+----------------+------+---------+------------------------+
| | +Create | (anon_36:Person) | 1 | 3 | 9 | 0/0 |
| | | +--------------------+----------------+------+---------+------------------------+
| | +Argument | value | 1 | 3 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +EmptyRow | | 1 | 1 | 0 | 0/0 |
+-----------------+--------------------+----------------+------+---------+------------------------+
7.3.49. Eager
For isolation purposes, the Eager operator ensures that operations affecting subsequent operations are
executed fully for the whole dataset before continuing execution. Information from the stores is fetched in
a lazy manner; i.e. the pattern matching might not be fully exhausted before updates are applied. To
guarantee reasonable semantics, the query planner will insert Eager operators into the query plan to
prevent updates from influencing pattern matching; this scenario is exemplified by the query below, where
the DELETE clause influences the MATCH clause. The Eager operator can cause high memory usage when
importing data or migrating graph structures. In such cases, the operations should be split into simpler
steps; e.g. importing nodes and relationships separately. Alternatively, the records to be updated can be
returned, followed by an update statement.
Query
589
Query Plan
Planner COST
Runtime SLOTTED
+-------------------------+-------------+----------------+------+---------+----------------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses |
+-------------------------+-------------+----------------+------+---------+----------------
+------------------------+
| +ProduceResults | | 1 | 0 | 0 | |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +EmptyResult | | 1 | 0 | 0 | |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Apply | | 1 | 504 | 0 | |
0/0 |
| |\ +-------------+----------------+------+---------+----------------
+------------------------+
| | +AntiConditionalApply | | 1 | 504 | 0 | |
0/0 |
| | |\ +-------------+----------------+------+---------+----------------
+------------------------+
| | | +MergeCreateNode | anon_38 | 1 | 0 | 0 | |
0/0 |
| | | +-------------+----------------+------+---------+----------------
+------------------------+
| | +Optional | | 35 | 504 | 0 | |
0/0 |
| | | +-------------+----------------+------+---------+----------------
+------------------------+
| | +AllNodesScan | anon_38 | 35 | 504 | 540 | |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Eager | | 36 | 36 | 0 | 7456 |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Delete | b | 36 | 36 | 9 | |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Delete | a | 36 | 36 | 12 | |
1/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Delete | r | 36 | 36 | 18 | |
19/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Eager | | 36 | 36 | 0 | 7456 |
0/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +Expand(All) | (a)-[r]-(b) | 36 | 36 | 71 | |
22/0 |
| | +-------------+----------------+------+---------+----------------
+------------------------+
| +AllNodesScan | a | 35 | 35 | 36 | |
1/0 |
+-------------------------+-------------+----------------+------+---------+----------------
+------------------------+
590
7.3.50. Eager Aggregation
The EagerAggregation operator evaluates a grouping expression and uses the result to group rows into
different groupings. For each of these groupings, EagerAggregation will then evaluate all aggregation
functions and return the result. To do this, EagerAggregation, as the name implies, needs to pull in all data
eagerly from its source and build up state, which leads to increased memory pressure in the system.
Query
Query Plan
Planner COST
Runtime PIPELINED
+-------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+-------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | location, people | 4 | 6 |
0 | | 0/0 | 0.271 | In Pipeline 1 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +EagerAggregation | cache[l.name] AS location, collect(p.name) AS people | 4 | 6 |
30 | 2404 | | 0.112 | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +Filter | p:Person | 15 | 15 |
15 | | | | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +Expand(All) | (l)<-[anon_19:WORKS_IN]-(p) | 15 | 15 |
26 | | | | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +CacheProperties | cache[l.name] | 10 | 10 |
20 | | | | Fused in Pipeline 0 |
| | +------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
| +NodeByLabelScan | l:Location | 10 | 10 |
11 | 72 | | | Fused in Pipeline 0 |
+-------------------+------------------------------------------------------+----------------+------
+---------+----------------+------------------------+-----------+---------------------+
Query
MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p.name, count(*) AS count
591
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Other |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +ProduceResults | `p.name`, count |
0 | 2 | 0 | | 0/0 | 0.066 | p.name ASC | In Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +OrderedAggregation | cache[p.name] AS `p.name`, count(*) AS count |
0 | 2 | 0 | 208 | 0/0 | 0.163 | p.name ASC | In Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
0 | 2 | 3 | 72 | 0/1 | 0.388 | p.name ASC | In Pipeline 0 |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
Query
592
Query Plan
Planner COST
Runtime PIPELINED
+--------------------------+------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+--------------------------+------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | people | 1 | 1 | 0 |
| Fused in Pipeline 0 |
| | +------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeCountFromCountStore | count( (:Person) ) AS people | 1 | 1 | 1 |
72 | Fused in Pipeline 0 |
+--------------------------+------------------------------+----------------+------+---------
+----------------+---------------------+
Query
Query Plan
Planner COST
Runtime PIPELINED
+----------------------------------+--------------------------------------------+----------------+------
+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Memory (Bytes) | Other |
+----------------------------------+--------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +ProduceResults | jobs | 1 | 1 |
0 | | Fused in Pipeline 0 |
| | +--------------------------------------------+----------------+------
+---------+----------------+---------------------+
| +RelationshipCountFromCountStore | count( (:Person)-[:WORKS_IN]->() ) AS jobs | 1 | 1 |
1 | 72 | Fused in Pipeline 0 |
+----------------------------------+--------------------------------------------+----------------+------
+---------+----------------+---------------------+
593
7.3.54. Distinct
The Distinct operator removes duplicate rows from the incoming stream of rows. To ensure only distinct
elements are returned, Distinct will pull in data lazily from its source and build up state. This may lead to
increased memory pressure in the system.
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------+-----------------------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) |
Other |
+------------------+-----------------------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | l | 14 | 6 | 0 | |
Fused in Pipeline 0 |
| | +-----------------------------+----------------+------+---------+----------------
+---------------------+
| +Distinct | l | 14 | 6 | 0 | 192 |
Fused in Pipeline 0 |
| | +-----------------------------+----------------+------+---------+----------------
+---------------------+
| +Filter | p:Person | 15 | 15 | 15 | |
Fused in Pipeline 0 |
| | +-----------------------------+----------------+------+---------+----------------
+---------------------+
| +Expand(All) | (l)<-[anon_19:WORKS_IN]-(p) | 15 | 15 | 26 | |
Fused in Pipeline 0 |
| | +-----------------------------+----------------+------+---------+----------------
+---------------------+
| +NodeByLabelScan | l:Location | 10 | 10 | 11 | 72 |
Fused in Pipeline 0 |
+------------------+-----------------------------+----------------+------+---------+----------------
+---------------------+
Query
MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN DISTINCT p.name
594
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Other |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +ProduceResults | `p.name` |
0 | 2 | 0 | | 0/0 | 0.055 | p.name ASC | In Pipeline 0 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +OrderedDistinct | cache[p.name] AS `p.name` |
0 | 2 | 0 | | 0/0 | 0.821 | p.name ASC | In Pipeline 0 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
0 | 2 | 3 | 72 | 0/1 | 0.218 | p.name ASC | In Pipeline 0 |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+------------
+---------------+
7.3.56. Filter
The Filter operator filters each row coming from the child operator, only passing through rows that
evaluate the predicates to true.
Query
595
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+--------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits |
Memory (Bytes) | Other |
+-----------------+--------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | p | 14 | 0 | 0 |
| Fused in Pipeline 0 |
| | +--------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Filter | cache[p.name] =~ $autostring_0 | 14 | 0 | 0 |
| Fused in Pipeline 0 |
| | +--------------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeIndexScan | p:Person(name) WHERE exists(name), cache[p.name] | 14 | 14 | 15 |
72 | Fused in Pipeline 0 |
+-----------------+--------------------------------------------------+----------------+------+---------
+----------------+---------------------+
7.3.57. Limit
The Limit operator returns the first 'n' rows from the incoming input.
Query
Query Plan
Planner COST
Runtime PIPELINED
+------------------+----------+----------------+------+---------+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other |
+------------------+----------+----------------+------+---------+----------------+---------------------+
| +ProduceResults | p | 3 | 3 | 0 | | Fused in Pipeline 0 |
| | +----------+----------------+------+---------+----------------+---------------------+
| +Limit | 3 | 3 | 3 | 0 | | Fused in Pipeline 0 |
| | +----------+----------------+------+---------+----------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 4 | 5 | 72 | Fused in Pipeline 0 |
+------------------+----------+----------------+------+---------+----------------+---------------------+
7.3.58. Skip
The Skip operator skips 'n' rows from the incoming rows.
596
Query
MATCH (p:Person)
RETURN p
ORDER BY p.id
SKIP 1
Query Plan
Planner COST
Runtime PIPELINED
+------------------+----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses | Time (ms) | Ordered by | Other |
+------------------+----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +ProduceResults | p | 13 | 13 | 0 | |
2/0 | 0.279 | p.id ASC | In Pipeline 1 |
| | +----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Skip | $autoint_0 | 13 | 13 | 0 | |
0/0 | 0.020 | p.id ASC | In Pipeline 1 |
| | +----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Sort | `p.id` ASC | 14 | 14 | 0 | 360 |
0/0 | 0.148 | p.id ASC | In Pipeline 1 |
| | +----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Projection | p.id AS `p.id` | 14 | 14 | 28 | |
| | | Fused in Pipeline 0 |
| | +----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 72 |
| | | Fused in Pipeline 0 |
+------------------+----------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
7.3.59. Sort
The Sort operator sorts rows by a provided key. In order to sort the data, all data from the source operator
needs to be pulled in eagerly and kept in the query state, which will lead to increased memory pressure in
the system.
Query
597
Query Plan
Planner COST
Runtime PIPELINED
+------------------+--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses | Time (ms) | Ordered by | Other |
+------------------+--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +ProduceResults | p | 14 | 14 | 0 | |
2/0 | 0.337 | p.name ASC | In Pipeline 1 |
| | +--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Sort | `p.name` ASC | 14 | 14 | 0 | 1152 |
0/0 | 0.151 | p.name ASC | In Pipeline 1 |
| | +--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Projection | p.name AS `p.name` | 14 | 14 | 28 | |
| | | Fused in Pipeline 0 |
| | +--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 72 |
| | | Fused in Pipeline 0 |
+------------------+--------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
Query
MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p ORDER BY p.name, p.age
598
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by |
Other |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +ProduceResults | p |
0 | 2 | 0 | | 2/0 | 0.110 | p.name ASC, p.age ASC | In
Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +PartialSort | `p.name` ASC, `p.age` ASC |
0 | 2 | 0 | 488 | 0/0 | 0.097 | p.name ASC, p.age ASC | In
Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` |
0 | 2 | 0 | | | | p.name ASC | Fused
in Pipeline 0 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
0 | 2 | 3 | 72 | | | p.name ASC | Fused
in Pipeline 0 |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
7.3.61. Top
The Top operator returns the first 'n' rows sorted by a provided key. Instead of sorting the entire input, only
the top 'n' rows are retained.
Query
599
Query Plan
Planner COST
Runtime PIPELINED
+------------------+----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache
Hits/Misses | Time (ms) | Ordered by | Other |
+------------------+----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +ProduceResults | p | 2 | 2 | 0 | |
2/0 | 0.142 | p.name ASC | In Pipeline 1 |
| | +----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Top | `p.name` ASC LIMIT 2 | 2 | 2 | 0 | 1104 |
0/0 | 0.076 | p.name ASC | In Pipeline 1 |
| | +----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Projection | p.name AS `p.name` | 14 | 14 | 28 | |
| | | Fused in Pipeline 0 |
| | +----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 72 |
| | | Fused in Pipeline 0 |
+------------------+----------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
Query
MATCH (p:Person) WHERE p.name STARTS WITH 'P' RETURN p ORDER BY p.name, p.age LIMIT 2
600
Query Plan
Planner COST
Runtime PIPELINED
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| Operator | Details | Estimated
Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by |
Other |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +ProduceResults | p |
0 | 2 | 0 | | 2/0 | 0.113 | p.name ASC, p.age ASC | In
Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +PartialTop | `p.name` ASC, `p.age` ASC LIMIT 2 |
0 | 2 | 0 | 352 | 0/0 | 0.676 | p.name ASC, p.age ASC | In
Pipeline 1 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` |
0 | 2 | 0 | | | | p.name ASC | Fused
in Pipeline 0 |
| | +--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
| +NodeIndexSeekByRange | p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] |
0 | 2 | 3 | 72 | | | p.name ASC | Fused
in Pipeline 0 |
+-----------------------+--------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------
+-----------------------+---------------------+
7.3.63. Union
The Union operator concatenates the results from the right child operator with the results from the left
child operator.
Query
MATCH (p:Location)
RETURN p.name
UNION ALL
MATCH (p:Country)
RETURN p.name
601
Query Plan
Planner COST
Runtime PIPELINED
+--------------------+--------------------+----------------+------+---------+----------------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Other
|
+--------------------+--------------------+----------------+------+---------+----------------
+---------------------+
| +ProduceResults | `p.name` | 20 | 11 | 0 | | Fused in
Pipeline 2 |
| | +--------------------+----------------+------+---------+----------------
+---------------------+
| +Union | | 20 | 11 | 0 | 768 | Fused in
Pipeline 2 |
| |\ +--------------------+----------------+------+---------+----------------
+---------------------+
| | +Projection | `p.name` | 10 | 1 | 0 | | Fused in
Pipeline 1 |
| | | +--------------------+----------------+------+---------+----------------
+---------------------+
| | +Projection | p.name AS `p.name` | 10 | 1 | 2 | | Fused in
Pipeline 1 |
| | | +--------------------+----------------+------+---------+----------------
+---------------------+
| | +NodeByLabelScan | p:Country | 10 | 1 | 2 | 72 | Fused in
Pipeline 1 |
| | +--------------------+----------------+------+---------+----------------
+---------------------+
| +Projection | `p.name` | 10 | 10 | 0 | | Fused in
Pipeline 0 |
| | +--------------------+----------------+------+---------+----------------
+---------------------+
| +Projection | p.name AS `p.name` | 10 | 10 | 20 | | Fused in
Pipeline 0 |
| | +--------------------+----------------+------+---------+----------------
+---------------------+
| +NodeByLabelScan | p:Location | 10 | 10 | 11 | 72 | Fused in
Pipeline 0 |
+--------------------+--------------------+----------------+------+---------+----------------
+---------------------+
7.3.64. Unwind
The Unwind operator returns one row per item in a list.
Query
602
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+----------------------------------------+----------------+------+---------
+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Other
|
+-----------------+----------------------------------------+----------------+------+---------
+---------------------+
| +ProduceResults | value | 10 | 5 | 0 | Fused in
Pipeline 0 |
| | +----------------------------------------+----------------+------+---------
+---------------------+
| +Unwind | range($autoint_0, $autoint_1) AS value | 10 | 5 | 0 | Fused in
Pipeline 0 |
+-----------------+----------------------------------------+----------------+------+---------
+---------------------+
Query
Query Plan
Planner COST
Runtime SLOTTED
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Memory (Bytes) | Page Cache Hits/Misses |
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +ProduceResults | | 1 | 0 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +EmptyResult | | 1 | 0 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +Apply | | 1 | 1 |
0 | | 0/0 |
| |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +AntiConditionalApply | | 1 | 1 |
0 | | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +MergeCreateRelationship | (s)-[anon_40:FRIENDS_WITH]->(s) | 1 | 1 |
2 | | 2/0 |
| | | | +-------------------------------------------+----------------+------
603
+---------+----------------+------------------------+
| | | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +AntiConditionalApply | | 1 | 1 |
0 | | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Optional | s | 1 | 1 |
0 | | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Expand(Into) | (s)-[anon_40:FRIENDS_WITH]->(s) | 0 | 0 |
4 | 888 | 1/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +LockNodes | s | 1 | 1 |
0 | | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Optional | s | 1 | 1 |
0 | | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Expand(Into) | (s)-[anon_40:FRIENDS_WITH]->(s) | 0 | 0 |
4 | 888 | 2/0 |
| | | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| | +Argument | s | 1 | 1 |
0 | | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+----------------+------------------------+
| +NodeIndexSeek | s:Person(name) WHERE name = $autostring_0 | 1 | 1 |
2 | | 0/1 |
+------------------------------+-------------------------------------------+----------------+------
+---------+----------------+------------------------+
7.3.66. Optional
The Optional operator is used to solve some OPTIONAL MATCH queries. It will pull data from its source,
simply passing it through if any data exists. However, if no data is returned by its source, Optional will
yield a single row with all columns set to null.
Query
604
Query Plan
Planner COST
Runtime PIPELINED
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +ProduceResults | p, q | 1 | 1 | 0 |
| 2/0 | 0.110 | In Pipeline 2 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +Apply | | 1 | 1 | 0 |
| 0/0 | 0.010 | |
| |\ +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| | +Optional | p | 1 | 1 | 0 |
0 | 0/0 | 0.456 | In Pipeline 2 |
| | | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| | +NodeIndexSeek | q:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 |
80 | 1/0 | 0.068 | In Pipeline 1 |
| | +-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
| +NodeIndexSeek | p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 |
72 | 0/1 | 0.201 | In Pipeline 0 |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+------------------------+-----------+---------------+
Query
605
Query Plan
Planner COST
Runtime SLOTTED
+---------------------+------------------------------+----------------+------+---------
+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache
Hits/Misses |
+---------------------+------------------------------+----------------+------+---------
+------------------------+
| +ProduceResults | u, v | 18 | 1 | 0 |
0/0 |
| | +------------------------------+----------------+------+---------
+------------------------+
| +Apply | | 18 | 1 | 0 |
0/0 |
| |\ +------------------------------+----------------+------+---------
+------------------------+
| | +ProjectEndpoints | (u)-[r*]->(v) | 18 | 1 | 0 |
0/0 |
| | | +------------------------------+----------------+------+---------
+------------------------+
| | +Argument | r | 1 | 1 | 0 |
0/0 |
| | +------------------------------+----------------+------+---------
+------------------------+
| +Projection | p AS r | 1 | 1 | 0 |
0/0 |
| | +------------------------------+----------------+------+---------
+------------------------+
| +Create | (n), (m), (n)-[p:KNOWS]->(m) | 1 | 1 | 4 |
0/0 |
+---------------------+------------------------------+----------------+------+---------
+------------------------+
7.3.68. Projection
For each incoming row, the Projection operator evaluates a set of expressions and produces a row with
the results of the expressions.
Query
606
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+---------------------------+----------------+------+---------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Other |
+-----------------+---------------------------+----------------+------+---------+---------------------+
| +ProduceResults | greeting | 1 | 1 | 0 | Fused in Pipeline 0 |
| | +---------------------------+----------------+------+---------+---------------------+
| +Projection | $autostring_0 AS greeting | 1 | 1 | 0 | Fused in Pipeline 0 |
+-----------------+---------------------------+----------------+------+---------+---------------------+
Query
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+--------------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+--------------------+----------------+------+---------+------------------------+
| +ProduceResults | | 1 | 0 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +EmptyResult | | 1 | 0 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 |
| |\ +--------------------+----------------+------+---------+------------------------+
| | +Create | (anon_36:Person) | 1 | 3 | 9 | 0/0 |
| | | +--------------------+----------------+------+---------+------------------------+
| | +Argument | value | 1 | 3 | 0 | 0/0 |
| | +--------------------+----------------+------+---------+------------------------+
| +EmptyRow | | 1 | 1 | 0 | 0/0 |
+-----------------+--------------------+----------------+------+---------+------------------------+
607
Query
Query Plan
Planner COST
Runtime PIPELINED
+-----------------+-----------------------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) |
Page Cache Hits/Misses | Time (ms) | Ordered by | Other |
+-----------------+-----------------------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +ProduceResults | label | 10 | 4 | 0 | |
0/0 | 0.069 | label ASC | In Pipeline 1 |
| | +-----------------------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +Sort | label ASC | 10 | 4 | 0 | 496 |
0/0 | 0.183 | label ASC | In Pipeline 1 |
| | +-----------------------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
| +ProcedureCall | db.labels() :: (label :: STRING?) | 10 | 4 | | |
| | | Fused in Pipeline 0 |
+-----------------+-----------------------------------+----------------+------+---------+----------------
+------------------------+-----------+------------+---------------------+
Query
608
Query Plan
Planner COST
Runtime PIPELINED
+------------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Memory
(Bytes) | Other |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +ProduceResults | location, name | 15 | 15 | 0 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Projection | cache[l.name] AS location, p.name AS name | 15 | 15 | 30 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Filter | p:Person | 15 | 15 | 15 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +Expand(All) | (l)<-[anon_19:WORKS_IN]-(p) | 15 | 15 | 26 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +CacheProperties | cache[l.name] | 10 | 10 | 20 |
| Fused in Pipeline 0 |
| | +-------------------------------------------+----------------+------+---------
+----------------+---------------------+
| +NodeByLabelScan | l:Location | 10 | 10 | 11 |
72 | Fused in Pipeline 0 |
+------------------+-------------------------------------------+----------------+------+---------
+----------------+---------------------+
Query
609
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+---------------------------------------------------------------------+----------------
+------+---------+------------------------+
| Operator | Details | Estimated Rows |
Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+---------------------------------------------------------------------+----------------
+------+---------+------------------------+
| +ProduceResults | | 1 |
0 | 0 | 0/0 |
| | +---------------------------------------------------------------------+----------------
+------+---------+------------------------+
| +EmptyResult | | 1 |
0 | 0 | 0/0 |
| | +---------------------------------------------------------------------+----------------
+------+---------+------------------------+
| +Create | (max:Person), (chris:Person), (max)-[anon_79:FRIENDS_WITH]->(chris) | 1 |
1 | 8 | 0/0 |
+-----------------+---------------------------------------------------------------------+----------------
+------+---------+------------------------+
7.3.73. Delete
The Delete operator is used to delete a node or a relationship.
Query
610
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| Operator | Details | Estimated Rows |
Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses |
+-----------------+-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +ProduceResults | | 0 |
0 | 0 | | 0/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +EmptyResult | | 0 |
0 | 0 | | 0/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +Delete | w | 0 |
1 | 1 | | 2/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +Eager | | 0 |
1 | 0 | 4376 | 0/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +Filter | w.duration = $autoint_1 AND me.name = $autostring_0 AND me:Person | 0 |
1 | 9 | | 3/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +Expand(All) | (london)<-[w:WORKS_IN]-(me) | 0 |
7 | 9 | | 2/0 |
| | +-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +NodeIndexSeek | london:Location(name) WHERE name = $autostring_2 | 0 |
1 | 2 | | 0/1 |
+-----------------+-------------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
Query
MATCH (p:Person)
DETACH DELETE p
611
Query Plan
Planner COST
Runtime SLOTTED
+------------------+----------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+------------------+----------+----------------+------+---------+------------------------+
| +ProduceResults | | 14 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +EmptyResult | | 14 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +DetachDelete | p | 14 | 14 | 0 | 48/0 |
| | +----------+----------------+------+---------+------------------------+
| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 2/0 |
+------------------+----------+----------------+------+---------+------------------------+
Query
612
Query Plan
Planner COST
Runtime SLOTTED
+-----------------------+------------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits
| Page Cache Hits/Misses |
+-----------------------+------------------------------------------------+----------------+------
+---------+------------------------+
| +ProduceResults | | 1 | 0 | 0
| 0/0 |
| | +------------------------------------------------+----------------+------
+---------+------------------------+
| +EmptyResult | | 1 | 0 | 0
| 0/0 |
| | +------------------------------------------------+----------------+------
+---------+------------------------+
| +AntiConditionalApply | | 1 | 1 | 0
| 0/0 |
| |\ +------------------------------------------------+----------------+------
+---------+------------------------+
| | +MergeCreateNode | anon_7 | 1 | 1 | 3
| 0/0 |
| | +------------------------------------------------+----------------+------
+---------+------------------------+
| +Optional | | 1 | 1 | 0
| 0/0 |
| | +------------------------------------------------+----------------+------
+---------+------------------------+
| +NodeIndexSeek | anon_7:Person(name) WHERE name = $autostring_0 | 1 | 0 | 1
| 0/1 |
+-----------------------+------------------------------------------------+----------------+------
+---------+------------------------+
Query
Query Plan
Planner COST
Runtime SLOTTED
+------------------------------+-------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB
Hits | Page Cache Hits/Misses |
+------------------------------+-------------------------------------------+----------------+------
+---------+------------------------+
613
| +ProduceResults | | 1 | 0 |
0 | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+------------------------+
| +EmptyResult | | 1 | 0 |
0 | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+------------------------+
| +Apply | | 1 | 0 |
0 | 0/0 |
| |\ +-------------------------------------------+----------------+------
+---------+------------------------+
| | +AntiConditionalApply | | 1 | 0 |
0 | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +MergeCreateRelationship | (s)-[anon_43:FRIENDS_WITH]->(s) | 1 | 0 |
0 | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +Argument | s | 1 | 0 |
0 | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | +AntiConditionalApply | | 1 | 0 |
0 | 0/0 |
| | |\ +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +Optional | s | 1 | 0 |
0 | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +Expand(Into) | (s)-[anon_43:FRIENDS_WITH]->(s) | 0 | 0 |
0 | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +LockNodes | s | 1 | 0 |
0 | 0/0 |
| | | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | | +Argument | s | 1 | 0 |
0 | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | +Optional | s | 1 | 0 |
0 | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | +Expand(Into) | (s)-[anon_43:FRIENDS_WITH]->(s) | 0 | 0 |
0 | 0/0 |
| | | +-------------------------------------------+----------------+------
+---------+------------------------+
| | +Argument | s | 1 | 0 |
0 | 0/0 |
| | +-------------------------------------------+----------------+------
+---------+------------------------+
| +NodeIndexSeek | s:Person(name) WHERE name = $autostring_0 | 1 | 0 |
1 | 0/1 |
+------------------------------+-------------------------------------------+----------------+------
+---------+------------------------+
Query
MATCH (n)
SET n:Person
614
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+----------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+----------+----------------+------+---------+------------------------+
| +ProduceResults | | 35 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +EmptyResult | | 35 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +SetLabels | n:Person | 35 | 35 | 35 | 2/0 |
| | +----------+----------------+------+---------+------------------------+
| +AllNodesScan | n | 35 | 35 | 36 | 1/0 |
+-----------------+----------+----------------+------+---------+------------------------+
Query
MATCH (n)
REMOVE n:Person
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+----------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+----------+----------------+------+---------+------------------------+
| +ProduceResults | | 35 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +EmptyResult | | 35 | 0 | 0 | 0/0 |
| | +----------+----------------+------+---------+------------------------+
| +RemoveLabels | n:Person | 35 | 35 | 35 | 2/0 |
| | +----------+----------------+------+---------+------------------------+
| +AllNodesScan | n | 35 | 35 | 36 | 1/0 |
+-----------------+----------+----------------+------+---------+------------------------+
615
Query
MATCH (n)
SET n = {weekday: 'Monday', meal: 'Lunch'}
Query Plan
Planner COST
Runtime SLOTTED
+---------------------------+---------------------------------------------------+----------------+------
+---------+------------------------+
| Operator | Details | Estimated Rows | Rows |
DB Hits | Page Cache Hits/Misses |
+---------------------------+---------------------------------------------------+----------------+------
+---------+------------------------+
| +ProduceResults | | 35 | 0 |
0 | 0/0 |
| | +---------------------------------------------------+----------------+------
+---------+------------------------+
| +EmptyResult | | 35 | 0 |
0 | 0/0 |
| | +---------------------------------------------------+----------------+------
+---------+------------------------+
| +SetNodePropertiesFromMap | n = {weekday: $autostring_0, meal: $autostring_1} | 35 | 35 |
141 | 4/0 |
| | +---------------------------------------------------+----------------+------
+---------+------------------------+
| +AllNodesScan | n | 35 | 35 |
36 | 1/0 |
+---------------------------+---------------------------------------------------+----------------+------
+---------+------------------------+
Query
MATCH (n)-[r]->(m)
SET r = {weight: 5, unit: 'kg'}
616
Query Plan
Planner COST
Runtime SLOTTED
+-----------------------------------+-----------------------------------------------+----------------
+------+---------+------------------------+
| Operator | Details | Estimated Rows |
Rows | DB Hits | Page Cache Hits/Misses |
+-----------------------------------+-----------------------------------------------+----------------
+------+---------+------------------------+
| +ProduceResults | | 18 |
0 | 0 | 0/0 |
| | +-----------------------------------------------+----------------
+------+---------+------------------------+
| +EmptyResult | | 18 |
0 | 0 | 0/0 |
| | +-----------------------------------------------+----------------
+------+---------+------------------------+
| +SetRelationshipPropertiesFromMap | r = {weight: $autoint_0, unit: $autostring_1} | 18 |
18 | 69 | 4/0 |
| | +-----------------------------------------------+----------------
+------+---------+------------------------+
| +Expand(All) | (m)<-[r]-(n) | 18 |
18 | 71 | 22/0 |
| | +-----------------------------------------------+----------------
+------+---------+------------------------+
| +AllNodesScan | m | 35 |
35 | 36 | 1/0 |
+-----------------------------------+-----------------------------------------------+----------------
+------+---------+------------------------+
Query
MATCH (n)
SET n.checked = true
617
Query Plan
Planner COST
Runtime SLOTTED
+-----------------+------------------+----------------+------+---------+------------------------+
| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses |
+-----------------+------------------+----------------+------+---------+------------------------+
| +ProduceResults | | 35 | 0 | 0 | 0/0 |
| | +------------------+----------------+------+---------+------------------------+
| +EmptyResult | | 35 | 0 | 0 | 0/0 |
| | +------------------+----------------+------+---------+------------------------+
| +SetProperty | n.checked = true | 35 | 35 | 38 | 2/0 |
| | +------------------+----------------+------+---------+------------------------+
| +AllNodesScan | n | 35 | 35 | 36 | 1/0 |
+-----------------+------------------+----------------+------+---------+------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-------------------+----------------------------------------------------------------+
| Operator | Details |
+-------------------+----------------------------------------------------------------+
| +CreateConstraint | CONSTRAINT uniqueness ON (c:Country) ASSERT (c.name) IS UNIQUE |
+-------------------+----------------------------------------------------------------+
Query
618
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-----------------+-----------------------------------------------------+
| Operator | Details |
+-----------------+-----------------------------------------------------+
| +DropConstraint | CONSTRAINT ON (c:Country) ASSERT (c.name) IS UNIQUE |
+-----------------+-----------------------------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+--------------------------------+----------------------------------------------------------------+
| Operator | Details |
+--------------------------------+----------------------------------------------------------------+
| +CreateConstraint | CONSTRAINT uniqueness ON (c:Country) ASSERT (c.name) IS UNIQUE |
| | +----------------------------------------------------------------+
| +DoNothingIfExists(CONSTRAINT) | CONSTRAINT uniqueness ON (c:Country) ASSERT (c.name) IS UNIQUE |
+--------------------------------+----------------------------------------------------------------+
619
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-------------------+----------------------------------------------------------+
| Operator | Details |
+-------------------+----------------------------------------------------------+
| +CreateConstraint | CONSTRAINT existence ON (p:Person) ASSERT exists(p.name) |
+-------------------+----------------------------------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-----------------+------------------------------------------------+
| Operator | Details |
+-----------------+------------------------------------------------+
| +DropConstraint | CONSTRAINT ON (p:Person) ASSERT exists(p.name) |
+-----------------+------------------------------------------------+
Query
620
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-------------------+---------------------------------------------------------------------------------+
| Operator | Details |
+-------------------+---------------------------------------------------------------------------------+
| +CreateConstraint | CONSTRAINT node_key ON (e:Employee) ASSERT (e.firstname, e.surname) IS NODE KEY |
+-------------------+---------------------------------------------------------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-----------------+------------------------------------------------------------------------+
| Operator | Details |
+-----------------+------------------------------------------------------------------------+
| +DropConstraint | CONSTRAINT ON (e:Employee) ASSERT (e.firstname, e.surname) IS NODE KEY |
+-----------------+------------------------------------------------------------------------+
Query
621
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-------------------+---------------------------------------------------------------+
| Operator | Details |
+-------------------+---------------------------------------------------------------+
| +CreateConstraint | CONSTRAINT existence ON ()-[l:LIKED]-() ASSERT exists(l.when) |
+-------------------+---------------------------------------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-----------------+-----------------------------------------------------+
| Operator | Details |
+-----------------+-----------------------------------------------------+
| +DropConstraint | CONSTRAINT ON ()-[l:LIKED]-() ASSERT exists(l.when) |
+-----------------+-----------------------------------------------------+
Query
622
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+-----------------+-----------------+
| Operator | Details |
+-----------------+-----------------+
| +DropConstraint | CONSTRAINT name |
+-----------------+-----------------+
Query
SHOW CONSTRAINTS
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+------------------+
| Operator |
+------------------+
| +ShowConstraints |
+------------------+
Query
623
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+--------------+-----------------------------------------+
| Operator | Details |
+--------------+-----------------------------------------+
| +CreateIndex | INDEX my_index FOR (:Country) ON (name) |
+--------------+-----------------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+---------------------------+-----------------------------------------+
| Operator | Details |
+---------------------------+-----------------------------------------+
| +CreateIndex | INDEX my_index FOR (:Country) ON (name) |
| | +-----------------------------------------+
| +DoNothingIfExists(INDEX) | INDEX my_index FOR (:Country) ON (name) |
+---------------------------+-----------------------------------------+
Query
624
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+------------+--------------------------------+
| Operator | Details |
+------------+--------------------------------+
| +DropIndex | INDEX FOR (:Country) ON (name) |
+------------+--------------------------------+
Query
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+------------+------------+
| Operator | Details |
+------------+------------+
| +DropIndex | INDEX name |
+------------+------------+
Query
625
Query Plan
Planner ADMINISTRATION
Runtime SCHEMA
+--------------+
| Operator |
+--------------+
| +ShowIndexes |
+--------------+
Planning shortest paths in Cypher can lead to different query plans depending on the predicates that need
to be evaluated. Internally, Neo4j will use a fast bidirectional breadth-first search algorithm if the
predicates can be evaluated whilst searching for the path. Therefore, this fast algorithm will always be
certain to return the right answer when there are universal predicates on the path; for example, when
searching for the shortest path where all nodes have the Person label, or where there are no nodes with a
name property.
If the predicates need to inspect the whole path before deciding on whether it is valid or not, this fast
algorithm cannot be relied on to find the shortest path, and Neo4j may have to resort to using a slower
exhaustive depth-first search algorithm to find the path. This means that query plans for shortest path
queries with non-universal predicates will include a fallback to running the exhaustive search to find the
path should the fast algorithm not succeed. For example, depending on the data, an answer to a shortest
path query with existential predicates — such as the requirement that at least one node contains the
property name='Kevin Bacon' — may not be able to be found by the fast algorithm. In this case, Neo4j will
fall back to using the exhaustive search to enumerate all paths and potentially return an answer.
The running times of these two algorithms may differ by orders of magnitude, so it is important to ensure
that the fast approach is used for time-critical queries.
When the exhaustive search is planned, it is still only executed when the fast algorithm fails to find any
matching paths. The fast algorithm is always executed first, since it is possible that it can find a valid path
even though that could not be guaranteed at planning time.
Please note that falling back to the exhaustive search may prove to be a very time consuming strategy in
some cases; such as when there is no shortest path between two nodes. Therefore, in these cases, it is
recommended to set cypher.forbid_exhaustive_shortestpath to true, as explained in Operations Manual
→ Configuration settings
626
7.4.1. Shortest path with fast algorithm
Query
This query can be evaluated with the fast algorithm — there are no predicates that need to see the whole
path before being evaluated.
Query plan
Planner COST
Runtime PIPELINED
+---------------------
+----------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+---------------------
+----------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +ProduceResults | p
| 0 | 1 | 0 | | 0/0 | 0.705 | In Pipeline 1 |
| |
+----------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +ShortestPath | p = (KevinB)-[anon_117:ACTED_IN*]-(Al) WHERE all(r IN relationships(p) WHERE
exists(r.role)) | 0 | 1 | 13 | 1704 | | |
In Pipeline 1 |
| |
+----------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +MultiNodeIndexSeek | KevinB:Person(name) WHERE name = $autostring_0, Al:Person(name) WHERE name =
$autostring_1 | 0 | 1 | 4 | 72 | 1/1 | 0.323 |
In Pipeline 0 |
+---------------------
+----------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
627
Query
This query, in contrast with the one above, needs to check that the whole path follows the predicate
before we know if it is valid or not, and so the query plan will also include the fallback to the slower
exhaustive search algorithm.
Query plan
Planner COST
Runtime SLOTTED
+--------------------------+------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| Operator | Details | Estimated Rows |
Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses |
+--------------------------+------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +ProduceResults | p | 0 |
1 | 0 | | 0/0 |
| | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +AntiConditionalApply | | 0 |
1 | 0 | | 0/0 |
| |\ +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Top | anon_94 ASC LIMIT 1 | 0 |
0 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Projection | length(p) AS anon_94 | 0 |
0 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Filter | length(p) > $autoint_2 | 0 |
0 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Projection | (KevinB)-[anon_116*]-(Al) AS p | 0 |
0 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +VarLengthExpand(Into) | (KevinB)-[anon_116*]-(Al) | 0 |
0 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Argument | KevinB, Al, p, anon_116 | 0 |
0 | 0 | | 0/0 |
| | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +Apply | | 0 |
1 | 0 | | 0/0 |
| |\ +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Optional | KevinB, Al | 1 |
1 | 0 | | 0/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +ShortestPath | p = (KevinB)-[anon_116*]-(Al) WHERE length(p) > $autoint_2 | 0 |
1 | 1 | 1776 | 21/0 |
| | | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +Argument | KevinB, Al | 0 |
628
1 | 0 | | 0/0 |
| | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +CartesianProduct | | 0 |
1 | 0 | | 0/0 |
| |\ +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| | +NodeIndexSeek | Al:Person(name) WHERE name = $autostring_1 | 0 |
1 | 2 | | 1/0 |
| | +------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
| +NodeIndexSeek | KevinB:Person(name) WHERE name = $autostring_0 | 0 |
1 | 2 | | 1/0 |
+--------------------------+------------------------------------------------------------+----------------
+------+---------+----------------+------------------------+
The way the bigger exhaustive query plan works is by using Apply/Optional to ensure that when the fast
algorithm does not find any results, a null result is generated instead of simply stopping the result stream.
On top of this, the planner will issue an AntiConditionalApply, which will run the exhaustive search if the
path variable is pointing to null instead of a path.
An ErrorPlan operator will appear in the execution plan in cases where (i)
cypher.forbid_exhaustive_shortestpath is set to true, and (ii) the fast algorithm is not able to find the
shortest path.
This query, just like the one above, needs to check that the whole path follows the predicate before we
know if it is valid or not. However, the inclusion of the WITH clause means that the query plan will not
include the fallback to the slower exhaustive search algorithm. Instead, any paths found by the fast
algorithm will subsequently be filtered, which may result in no answers being returned.
629
Query plan
Planner COST
Runtime PIPELINED
+---------------------
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| Operator | Details
| Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Other |
+---------------------
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +ProduceResults | p
| 1 | 1 | 0 | | 0/0 | 0.106 | In Pipeline 1 |
| |
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +Filter | length(p) > $autoint_2
| 0 | 1 | 0 | | 0/0 | 0.077 | In Pipeline 1 |
| |
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +ShortestPath | p = (KevinB)-[anon_116*]-(Al)
| 0 | 1 | 1 | 1776 | | | In Pipeline 1 |
| |
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
| +MultiNodeIndexSeek | KevinB:Person(name) WHERE name = $autostring_0, Al:Person(name) WHERE name =
$autostring_1 | 0 | 1 | 4 | 72 | 2/0 | 0.231 | In
Pipeline 0 |
+---------------------
+--------------------------------------------------------------------------------------------
+----------------+------+---------+----------------+------------------------+-----------+---------------+
630
Chapter 8. Deprecations, additions and
compatibility
Cypher is a language that is constantly evolving. New features get added to the language
continuously, and occasionally, some features become deprecated and are subsequently
removed.
◦ Version 4.1.3
◦ Version 4.1
◦ Version 4.0
◦ Version 3.5
◦ Version 3.4
◦ Version 3.3
◦ Version 3.2
◦ Version 3.1
◦ Version 3.0
• Compatibility
SHOW ROLE name PRIVILEGES Functionality Updated Can now handle multiple
roles, SHOW ROLES n1, n2, …
PRIVILEGES
SHOW USER name PRIVILEGES Functionality Updated Can now handle multiple
users, SHOW USERS n1, n2, …
PRIVILEGES
631
Feature Type Change Details
632
Feature Type Change Details
SHOW [ALL | UNIQUE | NODE Functionality Added New Cypher commands for
EXIST[S] | RELATIONSHIP listing constraints.
EXIST[S] | EXIST[S] | NODE
KEY] CONSTRAINT[S] [BRIEF
| VERBOSE [OUTPUT]]
633
Feature Type Change Details
634
Feature Type Change Details
635
Feature Type Change Details
636
Feature Type Change Details
CREATE INDEX [name] FOR Syntax Added New syntax for creating
(n:Label) ON (n.prop) indexes, which can include a
name.
637
Feature Type Change Details
638
Feature Type Change Details
639
8.1.8. Version 3.2
Feature Type Change Details
CYPHER planner=rule (Rule Functionality Removed All queries now use the cost
planner) planner. Any query
prepended thus will fall back
to using Cypher 3.1.
640
Feature Type Change Details
8.2. Compatibility
The ability of Neo4j to support multiple older versions of the Cypher language has been
changing. In versions of Neo4j before 3.5 the backwards compatibility layer included the
Cypher language parser, planner and runtime. All supported versions of Cypher would
run on the same Neo4j kernel. In Neo4j 3.4, however, this was changed such that the
compatibility layer no longer included the runtime. This meant that running, for example,
a CYPHER 3.1 query inside Neo4j 3.5 would plan the query using the 3.1 planner, and run
it using the 3.5 runtime and kernel. In Neo4j 4.0 this was changed again, such that the
compatibility layer includes only the parser. For example, running a CYPHER 3.5 query
inside Neo4j will parse older language features, but plan using the 4.2 planner, and run
using the 4.2 runtime and kernel. The primary reason for this change has been
optimizations in the Cypher runtime to allow Cypher query to perform better.
Older versions of the language can still be accessed if required. There are two ways to select which
version to use in queries.
641
1. Setting a version for all queries: You can configure your database with the configuration parameter
cypher.default_language_version, and enter which version you’d like to use (see Supported language
versions). Every Cypher query will use this version, provided the query hasn’t explicitly been configured
as described in the next item below.
2. Setting a version on a query by query basis: The other method is to set the version for a particular
query. Prepending a query with CYPHER 3.5 will execute the query with the version of Cypher included
in Neo4j 3.5.
CYPHER 3.5
MATCH (n:Person)
WHERE n.age > {agelimit}
RETURN n.name, n.age
Without the CYPHER 3.5 prefix this query would fail with a syntax error. With CYPHER 3.5 however, it will
only generate a warning and still work.
In Neo4j 4.2 some older language features are understood by the Cypher parser even if
they are no longer supported by the Neo4j kernel. These features will result in runtime
errors. See the table at Cypher Version 4.0 for the list of affected features.
Each release of Neo4j supports a limited number of old Cypher Language Versions.
When you upgrade to a new release of Neo4j, please make sure that it supports the
Cypher language version you need. If not, you may need to modify your queries to work
with a newer Cypher language version.
642
Chapter 9. Glossary of keywords
This section comprises a glossary of all the keywords — grouped by category and thence
ordered lexicographically — in the Cypher query language.
• Clauses
• Operators
• Functions
• Expressions
• Administrative commands
• Privilege Actions
9.1. Clauses
Clause Category Description
CREATE CONSTRAINT [existence] [IF Schema Create a constraint ensuring that all
NOT EXISTS] ON (n:Label) ASSERT nodes with a particular label have a
exists(n.property) certain property.
CREATE CONSTRAINT [node_key] [IF Schema Create a constraint ensuring all nodes
NOT EXISTS] ON (n:Label) ASSERT with a particular label have all the
(n.prop1, …, n.propN) IS NODE KEY specified properties and that the
[OPTIONS {optionKey: optionValue[, … combination of property values is
]}] unique; i.e. ensures existence and
uniqueness.
CREATE CONSTRAINT [existence] [IF Schema Create a constraint ensuring that all
NOT EXISTS] ON ()-[r:REL_TYPE]-() relationships with a particular type have
ASSERT exists(r.property) a certain property.
CREATE INDEX [single] [IF NOT Schema Create an index on all nodes with a
EXISTS] FOR (n:Label) ON (n.property) particular label and a single property; i.e.
[OPTIONS {optionKey: optionValue[, … create a single-property index.
]}]
643
Clause Category Description
CREATE INDEX [composite] [IF NOT Schema Create an index on all nodes with a
EXISTS] FOR (n:Label) ON (n.prop1, …, particular label and multiple properties;
n.propN) [OPTIONS {optionKey: i.e. create a composite index.
optionValue[, …]}]
DROP CONSTRAINT name [IF EXISTS] Schema Drop a constraint using the name.
DROP INDEX name [IF EXISTS] Schema Drop an index using the name.
LOAD CSV Importing data Use when importing data from CSV
files.
644
Clause Category Description
SHOW [ALL|BTREE] INDEX[ES] Schema List indexes in the database, either all or
[BRIEF|VERBOSE [OUTPUT]] B-tree only.
USING INDEX variable:Label(property) Hint Index hints are used to specify which
index, if any, the planner should use as a
starting point.
USING INDEX SEEK Hint Index seek hint instructs the planner to
variable:Label(property) use an index seek for this clause.
USING JOIN ON variable Hint Join hints are used to enforce a join
operation at specified points.
USING PERIODIC COMMIT Hint This query hint may be used to prevent
an out-of-memory error from occurring
when importing large amounts of data
using LOAD CSV.
USING SCAN variable:Label Hint Scan hints are used to force the planner
to do a label scan (followed by a filtering
operation) instead of using an index.
645
9.2. Operators
Operator Category Description
* Mathematical Multiplication
+ Mathematical Addition
+ String Concatenation
+ List Concatenation
/ Mathematical Division
= Comparison Equality
646
Operator Category Description
OR Boolean Disjunction
^ Mathematical Exponentiation
9.3. Functions
Function Category Description
647
Function Category Description
648
Function Category Description
649
Function Category Description
localtime({hour [, minute, second, …]}) Temporal Returns a LocalTime with the specified
component values.
650
Function Category Description
651
Function Category Description
round(), with precision Numeric Returns the value of the given number
rounded with the specified precision,
with half-values always being rounded
up.
round(), with precision and rounding Numeric Returns the value of the given number
mode rounded with the specified precision
and the specified rounding mode.
652
Function Category Description
size() applied to pattern expression Scalar Returns the number of paths matching
the pattern expression.
653
Function Category Description
9.4. Expressions
Name Description
CYPHER $version query Version This will force 'query' to use Neo4j
Cypher $version. The default is 4.0.
CYPHER runtime=interpreted query Runtime This will force the query planner to use
the interpreted runtime. This is the only
option in Neo4j Community Edition.
CYPHER runtime=slotted query Runtime This will cause the query planner to use
the slotted runtime. This is only available
in Neo4j Enterprise Edition.
654
Name Type Description
CYPHER runtime=pipelined query Runtime This will cause the query planner to use
the pipelined runtime if it supports
'query'. This is only available in Neo4j
Enterprise Edition.
ALTER CURRENT USER SET User and role Change the password of the user that is
PASSWORD FROM … TO currently logged in.
ALTER USER … [SET [PLAINTEXT | User and role Changes a user account. Changes can
ENCRYPTED] PASSWORD {password include setting a new password, setting
[CHANGE [NOT] REQUIRED] | the account status and enabling that the
CHANGE [NOT] REQUIRED}] [SET user should change the password upon
STATUS {ACTIVE | SUSPENDED}] next login.
CREATE [OR REPLACE] ROLE … [IF User and role Creates new roles.
NOT EXISTS] [AS COPY OF]
CREATE [OR REPLACE] USER … [IF User and role Creates a new user and sets the
NOT EXISTS] SET [PLAINTEXT | password for the new account.
ENCRYPTED] PASSWORD … [[SET Optionally the account status can also
PASSWORD] CHANGE [NOT] be set and if the user should change the
REQUIRED] [SET STATUS {ACTIVE | password upon first login.
SUSPENDED}]
DROP ROLE … [IF EXISTS] User and role Deletes a specified role.
DROP USER … [IF EXISTS] User and role Deletes a specified user.
655
Command Admin category Description
GRANT ROLE[S] … TO User and role Assigns one or multiple roles to one or
multiple users.
REVOKE [GRANT | DENY] … ON DBMS Privilege Removes a DBMS privilege from one or
FROM multiple roles.
REVOKE ROLE[S] … FROM User and role Removes one or multiple roles from one
or multiple users.
SHOW [ALL | POPULATED] ROLES User and role Returns information about all or
[WITH USERS] populated roles, optionally including the
assigned users.
SHOW [ROLE … | USER … | ALL ] Privilege Returns information about role, user or
PRIVILEGES all privileges.
SHOW USERS User and role Returns information about all users.
ALL DATABASE PRIVILEGES Database and schema Determines whether a user is allowed to
access, create, drop, and list indexes
and constraints, create new labels,
types and property names on a specific
database.
656
Name Category Description
657
Name Category Description
658
Name Category Description
659
Name Category Description
660
Appendix A: Cypher styleguide
This appendix contains the recommended style when writing Cypher queries.
• General recommendations
• Casing
• Spacing
• Patterns
• Meta characters
The purpose of the styleguide is to make the code as easy to read as possible, and thereby contributing to
lower cost of maintenance.
For rules and recommendations for naming of labels, relationship types and properties, please see the
Naming rules and recommendations.
• When referring to labels and relationship types, the colon should be included as follows: :Label,
:REL_TYPE.
• When referring to functions, use lower camel case and parentheses should be used as follows:
shortestPath(). Arguments should normally not be included.
• If you are storing Cypher statements in a separate file, use the file extension .cypher.
Bad
Good
MATCH (n)
WHERE n.name CONTAINS 's'
RETURN n.name
• Indent ON CREATE and ON MATCH with two spaces. Put ON CREATE before ON MATCH if both are present.
661
Bad
Good
MERGE (n)
ON CREATE SET n.prop = 0
MERGE (a:A)-[:T]-(b:B)
ON CREATE SET a.name = 'me'
ON MATCH SET b.name = 'you'
RETURN a.prop
• Start a subquery on a new line after the opening brace, indented with two (additional) spaces. Leave
the closing brace on its own line.
Bad
MATCH (a:A)
WHERE
EXISTS { MATCH (a)-->(b:B) WHERE b.prop = $param }
RETURN a.foo
Also bad
MATCH (a:A)
WHERE EXISTS
{MATCH (a)-->(b:B)
WHERE b.prop = $param}
RETURN a.foo
Good
MATCH (a:A)
WHERE EXISTS {
MATCH (a)-->(b:B)
WHERE b.prop = $param
}
RETURN a.foo
Bad
MATCH (a:A)
WHERE EXISTS {
(a)-->(b:B)
}
RETURN a.prop
Good
MATCH (a:A)
WHERE EXISTS { (a)-->(b:B) }
RETURN a.prop
662
A.3. Casing
• Write keywords in upper case.
Bad
match (p:Person)
where p.name starts with 'Ma'
return p.name
Good
MATCH (p:Person)
WHERE p.name STARTS WITH 'Ma'
RETURN p.name
Bad
Good
Bad
Good
◦ properties
◦ variables
◦ parameters
Bad
663
Good
A.4. Spacing
• For literal maps:
◦ No space between the opening brace and the first key
Bad
Good
Bad
Good
• No space in patterns.
Bad
Good
MATCH (:Person)-->(:Vehicle)
RETURN count(*)
664
• Use a wrapping space around operators.
Bad
MATCH p=(s)-->(e)
WHERE s.name<>e.name
RETURN length(p)
Good
MATCH p = (s)-->(e)
WHERE s.name <> e.name
RETURN length(p)
Bad
Good
MATCH (person:Person:Owner)
RETURN person.name
Bad
MATCH (),()
WITH ['a','b',3.14] AS list
RETURN list,2,3,4
Good
MATCH (), ()
WITH ['a', 'b', 3.14] AS list
RETURN list, 2, 3, 4
Bad
Good
665
Bad
MATCH (a:A)
WHERE EXISTS {(a)-->(b:B)}
RETURN a.prop
Good
MATCH (a:A)
WHERE EXISTS { (a)-->(b:B) }
RETURN a.prop
A.5. Patterns
• When patterns wrap lines, break after arrows, not before.
Bad
MATCH (:Person)-->(vehicle:Car)-->(:Company)
<--(:Country)
RETURN count(vehicle)
Good
MATCH (:Person)-->(vehicle:Car)-->(:Company)<--
(:Country)
RETURN count(vehicle)
• Use anonymous nodes and relationships when the variable would not be used.
Bad
Good
Bad
Good
MATCH (:Person)-->(vehicle:Car)-->(:Company)
RETURN count(vehicle)
666
Bad
MATCH ()-->(vehicle:Car)-->(manufacturer:Company)
WHERE manufacturer.foundedYear < 2000
RETURN vehicle.mileage
Good
MATCH (manufacturer:Company)<--(vehicle:Car)<--()
WHERE manufacturer.foundedYear < 2000
RETURN vehicle.mileage
Bad
MATCH (:Person)-->(vehicle:Car)-->(manufacturer:Company)
WHERE manufacturer.foundedYear < 2000
RETURN vehicle.mileage
Good
MATCH (manufacturer:Company)<--(vehicle:Car)<--(:Person)
WHERE manufacturer.foundedYear < 2000
RETURN vehicle.mileage
Bad
MATCH (:Country)-->(:Company)<--(vehicle:Car)<--(:Person)
RETURN vehicle.mileage
Good
MATCH (:Person)-->(vehicle:Car)-->(:Company)<--(:Country)
RETURN vehicle.mileage
A.6. Meta-characters
• Use single quotes, ', for literal string values.
Bad
RETURN "Cypher"
Good
RETURN 'Cypher'
◦ Disregard this rule for literal strings that contain a single quote character. If the string has both, use
the form that creates the fewest escapes. In the case of a tie, prefer single quotes.
667
Bad
Good
Bad
Good
Bad
RETURN 1;
Good
RETURN 1
License
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
Adapt
remix, transform, and build upon the material
The licensor cannot revoke these freedoms as long as you follow the license terms.
668
NonCommercial
You may not use the material for commercial purposes.
ShareAlike
If you remix, transform, or build upon the material, you must distribute your contributions under the
same license as the original.
No additional restrictions
You may not apply legal terms or technological measures that legally restrict others from doing
anything the license permits.
Notices
You do not have to comply with the license for elements of the material in the public domain or where your
use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary for your intended
use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the
material.
See https://creativecommons.org/licenses/by-nc-sa/4.0/ for further details. The full license text is available
at https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.
669