3.2 Cypher 语法(2)
3.2.7 模式(Patterns)
模式和模式匹配是 Cypher 非常核心的部分。要高效地使用 Cypher 必须深入理解模式。
使用模式可以描述你期望看到的数据的形状。例如,在 MATCH 语句中,当用模式描述一个形状的时候, Cypher 将按照模式来获取相应的数据。
模式描述数据的形式很类似在白板上画出图的形状。通常用圆圈来表达节点,使用箭头来表达关系。
模式在 MATCH 、 CREATE 和 MERGE 等语句中都会出现,后续章节会详细描述。
3.2.7.1 节点模式
模式能表达的最简单的形状就是节点。节点使用一对圆括号表示,然后中间含一个名字。例如:
(a)
这个模式描述了一个节点,其名称使用变量 a 表示。
3.2.7.2 关联节点的模式
模式可以描述多个节点及其之间的关系。 Cypher 使用箭头来表达两个节点之间的关系。例如:
(a)-->(b)
这个模式描述了一个非常简单的数据形状,即两个节点和从其中一个节点到另外一个节点的关系。两个节点分别命名为 a 和 b ,关系是有方向的,从 a 指向 b 。
这种描述节点和关系的方式可以扩展到任意数量的节点和它们之间的关系,例如:
(a)-->(b)<--(c)
这一系列相互关联的节点和关系被称为路径(Path)。
注意:节点的命名仅仅当后续的模式或者 Cypher 查询中需要引用时才需要。如果不需要引用,则命名可以省略。例如:
(a)-->()<--(c)
3.2.7.3 标签
模式除了可以描述节点之外,还可以用来描述标签。比如:
(a:User)-->(b)
也可以描述一一个节点的多个标签,如:
(a:User: Admin)-->(b)
3.2.7.4 指定属性
节点和关系是图的基础结构。Neo4j 的节点和关系都可以有属性,这样可以建立更丰富的模型。属性在模式中使用键值对的映射结构来表达,然后用大括号包起来。例如,一个有两个属性的节点如下所示。
(a {name: 'Andres', sport: 'Brazilian Ju-Jitsu'})
关系中的属性如下所示。
(a)-[{blocked: false}]->(b)
当模式中有属性时,它实际上为数据增加了额外的约束。在 CREATE 语句中,属性会被增加到新创建的节点和关系中。在 MERGE 语句中,属性将作为一个约束去匹配数据库中的数据是否存在该属性。如果没有匹配到,这时 MERGE 的行为将与 CREATE 一样,即属性将被设置到新创建的节点和关系中。
tips: 模式在 CREATE 语句中支持使用单个参数来指定属性。例如: CREATE (node $paramName)。但这在其他语句中是不行的,因为 Cypher 在编译查询的时候需要知道属性的名称,以便能够高效地匹配。
3.2.7.5 描述关系
如前面的例子所示,可以用箭头简单地描述两个节点之间的关系。它描述了关系的存在性和方向性。但如果不关心关系的方向,则箭头的头部可以省略。例如:
(a)--(b)
与节点类似,如果后续需要引用到该关系,则可以给关系赋一个变量名。变量名需要用方括号括起来,放在箭头的短横线中间,如下所示。
(a)-[r]->(b)
就像节点有标签一样,关系可以有类型(Type) 。给关系指定类型,如下所示。
(a)-[r:REL_TYPE]->(b)
不像节点可以有多个标签,关系只能有一个类型。但如果所描述的关系可以是一个类型集中的任意一种类型,可以将这些类型都列入模式中,它们之间以竖线“|”分割,例如:
(a)-[r:TYPE1|TYPE2]->(b)
注意:这种模式仅适用于描述已经存在的数据(如在 MATCH 语句中),而在 CREATE 或者 MERGE 语句中是不允许的,因为一个关系不能创建多个类型。
与节点类似,关系的命名也是可以省略的,例如:
(a)-[:REL_TYPE]->(b)
与使用一串节点和关系来描述一个长路径的模式不同,很多关系(以及中间的节点)可以采用指定关系的长度的模式来描述,例如:
(a)-[*2]->(b)
它描述了一张三个节点和两个关系的图。这些节点和关系都在同一条路径中(路径的长度为 2 )。它等同于:
(a)-->()-->(b)
关系的长度也可以指定一个范围,这被称为可变长度的关系,例如:
(a)-[*3..5]->(b)
关系的长度最小值为 3 ,最大值为 5 。它描述了一个或者有 4 个节点和 3 个关系,或者 5 个节点 4 个关系,或者 6 个节点和 5 个关系连在一起的图组成的一 条路径。
长度的边界也是可以省略的,如描述一个路径长度大于等于 3 的路径:
(a)-[*3..]->(b)
路径长度小于等于 5的路径,例如:
(a)-[*..5]->(b)
两个边界都可以省略,这允许任意长度的路径,例如:
(a)-[*]->(b)
来看一个简单的查询例子:
查询
MATCH (me)-[:KNOWS*1..2]-(remote_friend) WHERE me.name = 'Filipa' RETURN remote_friend.name
结果
这个查询用于找到符合这个模式的数据:即指定一个节点(name 属性值为’Filipa’)和与它关系为 KNOWS 的一步和两步的节点。这是一个查询一度和二度人脉的典型例子。
tips: 变长关系不能用于 CREATE 和 MERGE 语句。
3.2.7.6 赋值给路径变量
如上所述,连接在一起的一系列节点和关系被称为路径。 Cypher 允许使用标识符给路径命名,例如:
p= (a)-[*3..51->(b)
在 MATCH 中, CREAT 和 MERGE 语句中可以这样做,但当模式作为表达式的时候不能这样。
3.2.8 列表
Cypher 对列表(List)有很好的支持。
3.2.8.1概述
可以使用方括号和一组以逗号分割的元素来创建一个列表。
查询
RETURN [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] AS list
结果
在下面的例子中使用了 range 函数。它指定了列表包含元素的开始数字和结束数字。范围的两端也是被包含在内的,例如:
查询
RETURN range(0, 10)
结果
可以使用方括号[]访问列表中的元素。
查询
RETURN range(0, 10)[3]
结果
索引也可以为负数,这时访问的方向将从列表的末尾作为开始点。
查询
RETURN range(0, 10)[-3]
结果
也可以在[]中指定列表返回指定范围的元素。它将提取开始索引到结束索引的值,但不包含结束索引所对应的值。如下面例子中,开始索引为 0 ,结束索引为 3 ,结果 0,1,2 索引对应的值被返回,但结束索引 3 对应的值未返回。
查询
RETURN range(0, 10)[0..3]
结果
查询
RETURN range(0, 10)[0..-5]
结果
查询
RETURN range(0, 10)[-5..]
结果
查询
RETURN range(0, 10)[..4]
结果
tips: 如果返回一个范围的索引值越界了,那么返回直接从越界的地方进行截断。如果是单个元素的索引值越界,则返回 null 。
查询
RETURN range(0, 10)[5..15]
结果
查询
RETURN range(0, 10)[15]
结果
可以用 size 函数获取列表的长度,比如:
查询
RETURN size(range(0, 10)[0..3])
结果
3.2.8.2 List 推导式
List 推导式(Comprehension)是 Cypher 中基于已经存在的列表创建一个列表的语法构造。它遵循数学上的集合,代替使用映射和过滤函数。
查询
RETURN [x IN range(0, 10) WHERE x%2=0 | x^3] AS result
结果
如果希望分别地过滤或者映射, WHERE 部分或者表达式部分都是可以省略的。
查询
RETURN [x IN range(0, 10) WHERE x%2=0] AS result
结果
查询
RETURN [x IN range(0, 10) | x^3] AS result
结果
3.2.8.3 模式推导式
模式推导式是 Cypher 基于模式匹配的结果创建列表的一一种语法构造。模式推导式将像一般的 MATCH 语句那样去匹配模式,断言部分与一般的 WHERE 语句一样,但它将产生一个指定的定制投射。
查询
MATCH (a:Person {name: 'Charlie Sheen'}) RETURN [(a)-->(b) WHERE b:Movie | b.year] AS years
结果
整个断言,包括 WHERE 关键字都是可选的,可以被省略。
3.2.8.4 字面值映射
Cypher 也可以构造映射。通过 REST 接口可以获取 JSON 对象。在 Java 中对应的是java.util.Map<String, Object>。
查询
RETURN {key: 'value', listKey:[{inner: 'Map1'}, {inner: 'Map2'}]}
结果
3.2.8.5 Map 投射
Cypher 支持一个名为 “map projections” 的概念。它使得基于已有的节点、关系和其他 map 值来构建 map 变得容易。
Map 投射以指向图实体的且用逗号分隔的变量簇开头,并包含以{}包括起来的映射元素,语法如下:
map_variable {map_element, [, ...n]}
一个 map 元素投射一个或多个键值对到 map 投射。这里有 4 种类型的 map 投射元素:
- 属性选择器——投射属性名作为键, map_variable 中对应键的值作为键值。
- 字面值项——来自任意表达式的键值对,如key: 。
- 变量选择器——投射一个变量, 变量名作为键,变量的值作为投射的值。它的语法只有变量。
- 全属性选择器——投射来自 map_variable 中的所有键值对。
tips: 如果 map_ variable 的值指向一个 null ,那么整个 map 投射将返回 null 。
投射举例
找到 Charlie Sheen 和返回关于他和他参演过的电影。这个例子展示了字面值项类型的 map 投射,反过来它还在聚合函数 collect() 中使用了 map 投射。
查询
MATCH (actor:Person {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie:Movie) RETURN actor { .name, .realName, movies: collect(movie { .ttitle, .year})}
结果
找到演过电影的所有演员,并显示他们所参演电影的数量。这个例子用一个变量来代表数量,使用变量选择器来投射值,如下所示。
查询
MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie) WITH actor, count(movie) AS nrOfMovies RETURN actor { .name, nrOfMovies}
结果
还是以 “Charlie Sheen” 为例,此时返回该节点的所有属性。这里使用了全属性选择器来投射所有的节点属性和一个额外的显式投射的 age 属性。因为此属性在该节点不存在,所以投射的值为 null 。
查询
MATCH (actor:Person {name: 'Charlie Sheen'}) RETURN actor { .*, .age}
结果
3.2.9 空值
3.2.9.1 空值介绍
空值 null 在 Cypher 中表示未找到或者未定义。从概念上讲, null 意味着“一个未找到的未知值”。对待 null 会与其他值有些不同,例如从节点中获取一个并不存在的属性将返回 null 。大多数以 null 作为输入的表达式将返回 null 。这包括 WHERE 语句中用于断言的布尔表达式。
null 不等于 null ,两个未知的值并不意味着它们是同一个值。因此,null = null 返回 null 而不是 true 。
3.2.9.2空值的逻辑运算
逻辑运算符包括 AND 、 OR 、 XOR 、 IN 、 NOT ,把 null 当作未知的三值逻辑值, AND 、 OR 和 XOR 的逻辑值表如下表所示。
a | b | a AND b | a OR b | a XOR b |
---|---|---|---|---|
false | false | false | false | false |
false | null | false | null | null |
false | true | false | true | true |
true | false | false | true | true |
true | null | null | true | null |
true | true | true | true | false |
null | false | false | null | null |
null | null | null | null | null |
null | true | null | true | null |
3.2.9.3空值与 IN
IN 运算符遵循类似的逻辑。如果列表中存在某个值,结果就返回 true ;如果列表包含 null 值并且没有匹配到值,结果返回 null ;否则结果为false。下表给出了一些例子。
表达式 | 结果 |
---|---|
2 IN [1, 2, 3] | true |
2 IN [1, null, 3] | null |
2 IN [1, 2, null] | trtue |
2 IN [1] | false |
2 IN [] | false |
null IN [1, 2, 3] | null |
null IN [1, null, 3] | null |
null IN [] | false |
All 、 any 、 none 和 single 与 IN 类似,如果可以确切地计算结果,将返回 true 或者 false ;否则将返回 null 。
3.2.9.4返回空值的表达式
- 从列表中 获取不存在的元素:[][0], head([])。
- 试图访问节点或者关系的不存在的属性:n.missingProperty。
- 与null做比较:1<null。
- 包含 null的算术运算:1 + null。
- 包含任何 null参数的函数调用:sin(null)。