neo4j Clauses语法条款

查询由多个子句链接在一起组成。这些子句将在“子句”一章中详细讨论。

整个查询的语义由其子句的语义定义。每个子句的输入都是图的状态和由当前变量组成的中间结果表。一个子句的输出是图的新状态和新的中间结果表,作为下一个子句的输入。第一个子句将查询前的图的状态和一个空的中间结果表作为输入。最后一个子句的输出是查询的结果。

除非使用ORDER BY,否则 Neo4j 不保证查询结果的行顺序。

示例 1. read 子句之间的中间结果表

本节使用以下示例图。

现在遵循以下查询的中间结果表和每个子句后的图形状态:

MATCH (john:Person {name: 'John'})
MATCH (john)-[:FRIEND]->(friend)
RETURN friend.name AS friendName

该查询仅具有读取子句,因此图的状态保持不变,因此下面将其省略。

表 1. 各子句后的中间结果表
条款该条款后的中间结果表
匹配(约翰:人 {名称:'约翰'})
约翰

(:Person {name: 'John'})

匹配 (约翰)-[:朋友]->(朋友)
约翰朋友

(:Person {name: 'John'})

(:Person {name: 'Sara'})

(:Person {name: 'John'})

(:Person {name: 'Joe'})

返回 friend.name 作为 friendName
朋友姓名

'Sara'

'Joe'

上面的例子只研究了允许线性组合和省略 write 子句的子句。下一节将探讨这些非线性组合和 write 子句。

读写查询

在 Cypher 查询中,读取和写入子句可以轮流执行。读写查询最重要的方面是图的状态也会在子句之间发生变化。

一个子句永远不能观察到后面子句的写入。

示例 2. 读取和写入子句之间的中间结果和图的状态表

使用与上面相同的示例图,此示例显示以下查询的每个子句之后的中间结果表和图的状态:

MATCH (j:Person) WHERE j.name STARTS WITH "J"
CREATE (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"})

该查询查找所有属性name以“J”开头的节点,并为每个这样的节点创建另一个属性name设置为“Jay-jay”的节点。

表 2. 中间结果表和各子句后的图状态
条款该条款后的中间结果表该条款之后的图表状态,红色表示变化
匹配 (j:Person) 其中 j.name 以“J”开头

(:Person {name: 'John'})

(:Person {name: 'Joe'})

创建 (j)-[:FRIEND]->(jj:Person {name: "Jay-jay"})
杰杰

(:Person {name: 'John'})

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

(:Person {name: 'Joe'})

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

需要注意的是,即使名称“Jay-jay”以“J”开头,该MATCH子句也无法找到Person由该子句创建的节点。这是因为该子句位于该子句之后,因此无法观察到该子句对图所做的任何更改。CREATECREATEMATCHMATCHCREATE

查询UNION

UNION查询略有不同,因为两个或多个查询的结果放在一起,但每个查询都以一个包含中间结果的空表开始。

在带有 子句的查询中UNION之前的任何子句都无法观察到之后的子句UNION所做的写入。之后的任何子句都可以观察到之前子句所做的所有写入。 这意味着 子句永远无法观察到 之后子句所做的写入的规则在使用 的查询中仍然适用。UNION UNIONUNIONUNION

示例 3. 查询中的中间结果和图形状态表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

表 3. 中间结果表和各子句后的图状态

条款该条款后的中间结果表该条款之后的图表状态,红色表示变化
创建(jj:Person {名称:“Jay-jay”})
杰杰

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

返回计数(*)作为计数
数数

1

匹配 (j:Person) 其中 j.name 以“J”开头

(:Person {name: 'John'})

(:Person {name: 'Joe'})

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

返回计数(*)作为计数
数数

3

需要注意的是,MATCH子句会查找Person由子句创建的节点CREATE。这是因为CREATE子句位于MATCH子句之前,因此MATCH可以观察到对图所做的任何更改CREATE

CALL {}带有子查询的查询

子句中的子查询CALL {}针对每个传入的输入行进行评估。这意味着子查询中的写入子句可以执行多次。子查询的不同调用按传入输入行的顺序依次执行。

子查询的后续调用可以观察到子查询的早期调用所做的写入。

示例 4. 查询中的中间结果和图形状态表CALL {}

使用与上面相同的示例图,此示例显示以下查询的每个子句之后的中间结果表和图的状态:

MATCH (john:Person {name: 'John'})
SET john.friends = []
WITH john
MATCH (john)-[:FRIEND]->(friend)
WITH john, friend
CALL {
  WITH john, friend
  WITH *, john.friends AS friends
  SET john.friends = friends + friend.name
}
表 4. 中间结果表和各子句后的图状态
条款该条款后的中间结果表该条款之后的图表状态,红色表示变化
匹配(约翰:人 {名称:'约翰'})
约翰

(:Person {name: 'John'})

设置约翰.朋友=[]
约翰

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

匹配 (约翰)-[:朋友]->(朋友)
约翰朋友

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

(:Person {name: 'Sara'})

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

(:Person {name: 'Joe'})

第一次调用

与 *, john.friends AS friends
约翰朋友朋友们

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

(:Person {name: 'Sara'})

[]

第一次调用

设置约翰.朋友 = 朋友 + 朋友.姓名
约翰朋友朋友们

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

(:Person {name: 'Sara'})

[]

第二次调用

与 *, john.friends AS friends
约翰朋友朋友们

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

(:Person {name: 'Joe'})

['Sara']

第二次调用

设置约翰.朋友 = 朋友 + 朋友.姓名
约翰朋友朋友们

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

(:Person {name: 'Joe'})

['Sara']

值得注意的是,在子查询中,该WITH子句的第二次调用可以观察到该子句第一次调用所做的写入SET

实施注意事项

实现上述语义的一种简单方法是完全执行每个子句并在执行下一个子句之前实现中间结果表。这种方法会消耗大量内存来实现中间结果表,并且通常性能不佳。

相反,Cypher 通常会尝试交错执行子句。这称为惰性求值。它仅在需要时实现中间结果。在许多读写查询中,交错执行子句是没有问题的,但如果没有,Cypher 必须确保中间结果表在正确的时间实现。这是通过Eager在执行计划中插

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北京橙溪 www.enwing.com

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值