Clause composition
The semantics of a whole query is defined by the semantics of its clauses. Each clause has as input the state of the graph and a table of intermediate results consisting of the current variables. The output of a clause is a new state of the graph and a new table of intermediate results, serving as input to the next clause. The first clause takes as input the state of the graph before the query and an empty table of intermediate results. The output of the last clause is the result of the query.
Example 1. Table of intermediate results between read clauses
Now follows the table of intermediate results and the state of the graph after each clause for the following query:
MATCH (john:Person {name: 'John'}) MATCH (john)-[:FRIEND]->(friend) RETURN friend.name AS friendName
The query only has read clauses, so the state of the graph remains unchanged and is therefore omitted below.
Clause | Table of intermediate results after the clause | ||||||
---|---|---|---|---|---|---|---|
MATCH (john:Person {name: 'John'}) |
|
||||||
MATCH (john)-[:FRIEND]->(friend) |
|
||||||
RETURN friend.name AS friendName |
|
Example 2. Table of intermediate results and state of the graph between read and write clauses
MATCH (j:Person) WHERE j.name STARTS WITH "J" CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"})
Clause | Table of intermediate results after the clause | State of the graph after the clause, changes in red | ||||||
---|---|---|---|---|---|---|---|---|
MATCH (j:Person) WHERE j.name STARTS WITH "J" |
|
|
||||||
CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"}) |
|
|
Queries with UNION
UNION
queries are slightly different because the results of two or more queries are put together, but each query starts with an empty table of intermediate results.
In a query with a UNION
clause, any clause before the UNION
cannot observe writes made by a clause after the UNION
. Any clause after UNION
can observe all writes made by a clause before the UNION
. This means that the rule that a clause can never observe writes made by a later clause still applies in queries using UNION
.
Example 3. Table of intermediate results and state of the graph in a query with UNION
CREATE (jj:Person {name: "Jay-jay"}) RETURN count(*) AS count UNION MATCH (j:Person) WHERE j.name STARTS WITH "J" RETURN count(*) AS count
Clause | Table of intermediate results after the clause | State of the graph after the clause, changes in red | ||||
---|---|---|---|---|---|---|
CREATE (jj:Person {name: "Jay-jay"}) |
|
|
||||
RETURN count(*) AS count |
|
|
||||
MATCH (j:Person) WHERE j.name STARTS WITH "J" |
|
|
||||
RETURN count(*) AS count |
|
|
Queries with CALL {}
subqueries
Subqueries inside a CALL {}
clause are evaluated for each incoming input row. This means that write clauses inside a subquery can get executed more than once. The different invocations of the subquery are executed in turn, in the order of the incoming input rows.
Later invocations of the subquery can observe writes made by earlier invocations of the subquery.
Example 4. Table of intermediate results and state of the graph in a query with CALL {}
MATCH (john:Person {name: 'John'}) SET john.friends = [] WITH john MATCH (john)-[:FRIEND]->(friend) WITH john, friend CALL (john, friend) { WITH john.friends AS friends SET john.friends = friends + friend.name }
Clause | Table of intermediate results after the clause | State of the graph after the clause, changes in red | ||||||
---|---|---|---|---|---|---|---|---|
MATCH (john:Person {name: 'John'}) |
|
|
||||||
SET john.friends = [] |
|
|
||||||
MATCH (john)-[:FRIEND]->(friend) |
|
|
||||||
First invocation of WITH john.friends AS friends |
|
|
||||||
First invocation of SET john.friends = friends + friend.name |
|
|
||||||
Second invocation of WITH john.friends AS friends |
|
|
||||||
Second invocation of SET john.friends = friends + friend.name |
|
|
CALL procedure
The CALL
clause is used to call a procedure deployed in the database.
CREATE (andy:Developer {name: 'Andy', born: 1991}), (beatrice:Developer {name: 'Beatrice', born: 1985}), (charlotte:Administrator {name: 'Charlotte', born: 1990}), (david:Administrator {name: 'David', born: 1994, nationality: 'Swedish'}), (andy)-[:KNOWS]->(beatrice), (beatrice)-[:KNOWS]->(charlotte), (andy)-[:KNOWS]->(david)
Example 1. CALL
a procedure without arguments
CALL db.labels()
╒═══════════════╕ │label │ ╞═══════════════╡ │"Developer" │ ├───────────────┤ │"Administrator"│ └───────────────┘
Example 2. CALL
a procedure without arguments
dbms.checkConfigValue()
checks the validity of a configuration setting value, using literal arguments.
CALL dbms.checkConfigValue('server.bolt.enabled', 'true')
╒═════╤══════════════════╕ │valid│message │ ╞═════╪══════════════════╡ │true │"requires restart"│ └─────┴──────────────────┘
Example 3. CALL
a procedure using parameters
:param setting => 'server.bolt.enabled'; :param value => 'true'; CALL dbms.checkConfigValue($setting, $value);
Example 4. CALL
a procedure using both literal and parameter arguments
:param setting => 'server.bolt.enabled'; CALL dbms.checkConfigValue($setting, "true");
Example 5. YIELD *
CALL db.labels() YIELD *
Note that YIELD *
is only valid in standalone procedure calls. Variables must be explicitly named in a YIELD
clause if other clauses than a single procedure CALL
are present. This restriction simplifies query logic and protects against output variables from the procedure accidentally clashing with other query variables. For example, the following is not valid:
CALL db.labels() YIELD * RETURN count(*) AS results
Example 6. YIELD
specific procedure results and filter on them
YIELD
can be used to filter for specific results. This requires knowing the names of the arguments within a procedure’s signature, which can either be found in the Operations Manual → Procedures or in the signature
column returned by a SHOW PROCEDURES
command (see example below).
Find the argument names of db.propertyKeys:
SHOW PROCEDURES YIELD name, signature WHERE name = 'db.propertyKeys' RETURN signature
╒══════════════════════════════════════════════╕ │signature │ ╞══════════════════════════════════════════════╡ │"db.propertyKeys() :: (propertyKey :: STRING)"│ └──────────────────────────────────────────────┘
It is then possible to use these argument names for further query filtering. Note that if the procedure call is part of a larger query, its output must be named explicitly. In the below example, propertyKey
is aliased as prop
and then used later in the query to count the occurrence of each property in the graph.
CALL db.propertyKeys() YIELD propertyKey AS prop MATCH (n) WHERE n[prop] IS NOT NULL RETURN prop, count(n) AS numNodes
╒═════════════╤════════╕ │prop │numNodes│ ╞═════════════╪════════╡ │"born" │4 │ ├─────────────┼────────┤ │"name" │4 │ ├─────────────┼────────┤ │"nationality"│1 │ └─────────────┴────────┘
Note on VOID procedures
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. VOID
procedure only produces side-effects and does not allow for 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).
Optional procedure calls
OPTIONAL CALL
allows for an optional procedure call. Similar to OPTIONAL MATCH
any empty rows produced by the OPTIONAL CALL
will return null
.
Example 7. Difference between using CALL
and OPTIONAL CALL
This query uses the apoc.neighbors.tohop()
procedure (part of Neo4j’s APOC Core library), which returns all nodes connected by the given relationship type within the specified distance (1 hop, in this case) and direction.
Regular procedure CALL
MATCH (n) CALL apoc.neighbors.tohop(n, "KNOWS>", 1) YIELD node RETURN n.name AS name, collect(node.name) AS connections
╒══════════╤═════════════════════╕ │name │connections │ ╞══════════╪═════════════════════╡ │"Andy" │["Beatrice", "David"]│ ├──────────┼─────────────────────┤ │"Beatrice"│["Charlotte"] │ └──────────┴─────────────────────┘
Note that the result does not include the nodes in the graph without any outgoing KNOWS
relationships connected to them.
MATCH (n) CALL apoc.neighbors.tohop(n, "KNOWS>", 1) YIELD node RETURN n.name AS name, node.name AS connections
╒══════════╤═══════════╕ │name │connections│ ╞══════════╪═══════════╡ │"Andy" │"Beatrice" │ ├──────────┼───────────┤ │"Andy" │"David" │ ├──────────┼───────────┤ │"Beatrice"│"Charlotte"│ └──────────┴───────────┘
The same query is used below, but CALL
is replaced with OPTIONAL CALL
.
Optional procedure CALL
MATCH (n) OPTIONAL CALL apoc.neighbors.tohop(n, "KNOWS>", 1) YIELD node RETURN n.name AS name, collect(node.name) AS connections
The result now includes the two nodes without any outgoing KNOWS
relationships connected to them.
╒═══════════╤═════════════════════╕ │name │connections │ ╞═══════════╪═════════════════════╡ │"Andy" │["Beatrice", "David"]│ ├───────────┼─────────────────────┤ │"Beatrice" │["Charlotte"] │ ├───────────┼─────────────────────┤ │"Charlotte"│[] │ ├───────────┼─────────────────────┤ │"David" │[] │ └───────────┴─────────────────────┘
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了