子句构成
本节描述了 Cypher® 在组合不同读写子句时的语义。
一个查询由多个子句链式组成。这些内容在子句一章中有更详细的讨论。
整个查询的语义由其子句的语义定义。每个子句的输入是图的状态和一个由当前变量组成的中间结果表。子句的输出是图的新状态和一个新的中间结果表,作为下一个子句的输入。第一个子句的输入是查询前的图状态和一个空的中间结果表。最后一个子句的输出是查询结果。
除非使用 |
本节将使用以下示例图。
现在,以下是该查询在每个子句执行后的中间结果表和图的状态
MATCH (john:Person {name: 'John'})
MATCH (john)-[:FRIEND]->(friend)
RETURN friend.name AS friendName
该查询只有读子句,因此图的状态保持不变,故在下方省略。
子句 | 子句执行后的中间结果表 | ||||||
---|---|---|---|---|---|---|---|
MATCH (john:Person {name: 'John'}) |
|
||||||
MATCH (john)-[:FRIEND]->(friend) |
|
||||||
RETURN friend.name AS friendName |
|
上述示例只关注了允许线性组合的子句,并省略了写子句。下一节将探讨这些非线性组合和写子句。
读写查询
在 Cypher 查询中,读子句和写子句可以交替出现。读写查询最重要的方面是图的状态也会在子句之间发生变化。
一个子句永远无法观察到后续子句所做的写入。 |
使用与上述相同的示例图,本示例展示了以下查询在每个子句执行后的中间结果表和图的状态
MATCH (j:Person) WHERE j.name STARTS WITH "J"
CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"})
该查询查找所有 name
属性以 "J" 开头的节点,并为每个此类节点创建另一个 name
属性设置为 "Jay-jay" 的节点。
子句 | 子句执行后的中间结果表 | 子句执行后的图状态,红色表示更改 | ||||||
---|---|---|---|---|---|---|---|---|
MATCH (j:Person) WHERE j.name STARTS WITH "J" |
|
|||||||
CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"}) |
|
需要注意的是,MATCH
子句没有找到由 CREATE
子句创建的 Person
节点,尽管名称 "Jay-jay" 以 "J" 开头。这是因为 CREATE
子句在 MATCH
子句之后,因此 MATCH
无法观察到 CREATE
对图所做的任何更改。
带有 UNION
的查询
UNION
查询略有不同,因为两个或多个查询的结果被组合在一起,但每个查询都从一个空的中间结果表开始。
在一个带有 UNION
子句的查询中,任何 在 UNION
之前的子句都无法观察到 在 UNION
之后的子句所做的写入。任何 在 UNION
之后的子句都可以观察到 在 UNION
之前的子句所做的所有写入。这意味着,一个子句永远无法观察到后续子句所做的写入的规则在使用 UNION
的查询中仍然适用。
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
子句 | 子句执行后的中间结果表 | 子句执行后的图状态,红色表示更改 | ||||
---|---|---|---|---|---|---|
CREATE (jj:Person {name: "Jay-jay"}) |
|
|||||
RETURN count(*) AS count |
|
|||||
MATCH (j:Person) WHERE j.name STARTS WITH "J" |
|
|||||
RETURN count(*) AS count |
|
需要注意的是,MATCH
子句找到了由 CREATE
子句创建的 Person
节点。这是因为 CREATE
子句在 MATCH
子句之前,因此 MATCH
可以观察到 CREATE
对图所做的任何更改。
带有 CALL {}
子查询的查询
在 CALL {}
子句内的子查询会针对每个传入的输入行进行评估。这意味着子查询内的写子句可能会被执行多次。子查询的不同调用会依次执行,按照传入输入行的顺序。
子查询的后续调用可以观察到早期调用所做的写入。
CALL {}
的查询中的中间结果表和图状态使用与上述相同的示例图,本示例展示了以下查询在每个子句执行后的中间结果表和图的状态
下面的查询使用一个变量作用域子句(在 Neo4j 5.23 中引入)将变量导入到 CALL 子查询中。如果您使用的是旧版 Neo4j,请改用导入 WITH 子句。 |
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
}
子句 | 子句执行后的中间结果表 | 子句执行后的图状态,红色表示更改 | ||||||
---|---|---|---|---|---|---|---|---|
MATCH (john:Person {name: 'John'}) |
|
|||||||
SET john.friends = [] |
|
|||||||
MATCH (john)-[:FRIEND]->(friend) |
|
|||||||
第一次调用 WITH john.friends AS friends |
|
|||||||
第一次调用 SET john.friends = friends + friend.name |
|
|||||||
第二次调用 WITH john.friends AS friends |
|
|||||||
第二次调用 SET john.friends = friends + friend.name |
|
需要注意的是,在子查询中,WITH
子句的第二次调用可以观察到 SET
子句的第一次调用所做的写入。