ZhangZhihui's Blog  

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.

ClauseTable of intermediate results after the clause
MATCH (john:Person {name: 'John'})
john

(:Person {name: 'John'})

MATCH (john)-[:FRIEND]->(friend)
johnfriend

(:Person {name: 'John'})

(:Person {name: 'Sara'})

(:Person {name: 'John'})

(:Person {name: 'Joe'})

RETURN friend.name AS friendName
friendName

'Sara'

'Joe'

 

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"})

 

Table 2. The table of intermediate results and the state of the graph after each clause
ClauseTable of intermediate results after the clauseState of the graph after the clause, changes in red
MATCH (j:Person) WHERE j.name STARTS WITH "J"
j

(:Person {name: 'John'})

(:Person {name: 'Joe'})

Diagram
CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"})
jjj

(:Person {name: 'John'})

(:Person {name: 'Jay-jay'})

(:Person {name: 'Joe'})

(:Person {name: 'Jay-jay'})

Diagram

 

 

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

 

Table 3. The table of intermediate results and the state of the graph after each clause
ClauseTable of intermediate results after the clauseState of the graph after the clause, changes in red
CREATE (jj:Person {name: "Jay-jay"})
jj

(:Person {name: 'Jay-jay'})

Diagram
RETURN count(*) AS count
count

1

Diagram
MATCH (j:Person) WHERE j.name STARTS WITH "J"
j

(:Person {name: 'John'})

(:Person {name: 'Joe'})

(:Person {name: 'Jay-jay'})

Diagram
RETURN count(*) AS count
count

3

Diagram

 

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
}
复制代码

 

Table 4. The table of intermediate results and the state of the graph after each clause
ClauseTable of intermediate results after the clauseState of the graph after the clause, changes in red
MATCH (john:Person {name: 'John'})
john

(:Person {name: 'John'})

Diagram
SET john.friends = []
john

(:Person {name: 'John', friends: []})

Diagram
MATCH (john)-[:FRIEND]->(friend)
johnfriend

(:Person {name: 'John', friends: []})

(:Person {name: 'Sara'})

(:Person {name: 'John', friends: []})

(:Person {name: 'Joe'})

Diagram

First invocation of

WITH john.friends AS friends
johnfriendfriends

(:Person {name: 'John', friends: []})

(:Person {name: 'Sara'})

[]

Diagram

First invocation of

SET john.friends = friends + friend.name
johnfriendfriends

(:Person {name: 'John', friends: ['Sara']})

(:Person {name: 'Sara'})

[]

Diagram

Second invocation of

WITH john.friends AS friends
johnfriendfriends

(:Person {name: 'John', friends: ['Sara']})

(:Person {name: 'Joe'})

['Sara']

Diagram

Second invocation of

SET john.friends = friends + friend.name
johnfriendfriends

(:Person {name: 'John', friends: ['Sara', 'Joe']})

(:Person {name: 'Joe'})

['Sara']

Diagram

 

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:

Not allowed:
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"    │[]                   │
└───────────┴─────────────────────┘
复制代码

 

posted on   ZhangZhihuiAAA  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
 
点击右上角即可分享
微信分享提示