Neo4j权威指南 (图数据库技术丛书)
上QQ阅读APP看书,第一时间看更新

3.2 基本语法

本节主要描述Cypher查询语言的基本语法。

3.2.1 类型

Cypher处理的所有值都有一个特定的类型,它支持如下类型:

● 数值型

● 字符串

● 布尔型

● 节点

● 关系

● 路径

● 映射(Map)

● 列表(List)

在Cypher语句中,大多数类型的值都可以使用字面值表达式(参见“3.2.2表达式”小节)。在使用null的时候要特别注意,因为null是任何类型的值(参见“3.2.9空值”小节)。节点、关系和路径可以作为模式匹配的返回结果。

提示

标签(label)不是值,它只是模式匹配的一种语法形式。

3.2.2 表达式

3.2.2.1 概述

Cypher中的表达式如下:

● 十进制(整型和双精度型)的字面值:13、-4000、3.14、6.022E23。

● 十六进制整型字面值(以0x开头):0x13zf、0xFC3A9、-0x66eff。

● 八进制整型字面值(以0开头):01372、02127、 -05671。

● 字符串字面值:'Hello'、"World"。

● 布尔字面值:true、false、TRUE、FALSE。

● 变量:n、x、rel、myFancyVariable、`A name with weird stuff in it[]! `。

● 属性:n.prop、x.prop、rel.thisProperty 、myFancyVariable. `(weird property name)`。

● 动态属性:n["prop"]、rel[n.city + n.zip]、map[coll[0]]。

● 参数:$param、$0

● 表达式列表:['a', 'b']、[1, 2, 3]、['a', 2, n.property, $param] 、[ ]。

● 函数调用:length(p)、nodes(p)。

● 聚合函数:avg(x.prop)、count(*)。

● 路径-模式:(a)-->()<--(b)。

● 计算式:1 + 2 and 3 < 4。

● 返回true或者false的断言表达式:a.prop = 'Hello'、length(p) >10、exists(a.name)。

● 正则表达式:a.name =~ 'Tob.*'。

● 大小写敏感的字符串匹配表达式:a.surname STARTS WITH 'Sven'、a.surname ENDS WITH 'son' or a.surname CONTAINS 'son'。

● CASE表达式。

3.2.2.2 转义字符

Cypher中的字符串可以包含的转义字符如表3-1所示。

表3-1 Cypher中的字符串可以包含的转义字符

3.2.2.3 Case表达式

Cypher支持Case条件表达式,它类似于其他语言中的if/else语句。

1.简单的Case表达式

计算表达式的值,然后依次与WHEN语句中的表达式进行比较,直到匹配上为止。如果未匹配上,则ELSE中的表达式将作为结果。如果ELSE语句不存在,那么将返回null。

语法

    CASE test
    WHEN value THEN result
    [WHEN ...]
    [ELSE default]
    END

参数

● test:一个有效的表达式。

● value:一个表达式,它的结果将与test表达式的结果进行比较。

● result:如果value表达式能够与test表达式匹配,它将作为结果表达式。

● default:没有匹配的情况下的默认返回表达式。

查询

    MATCH (n)
    RETURN
    CASE n.eyes
    WHEN 'blue'
    THEN 1
    WHEN 'brown'
    THEN 2
    ELSE 3 END AS result

结果

2.一般的Case表达式

按顺序判断断言,直到找到一个为true,然后对应的结果被返回。如果没有找到,就返回ELSE的值。如果没有ELSE语句,则返回null。

语法

    CASE
    WHEN predicate THEN result
    [WHEN ...]
    [ELSE default]
    END

参数

● predicate:判断的断言,以找到一个有效的可选项。

● result:如果predicate匹配到,result就作为结果表达式。

● default:没有匹配到情况下,默认返回表达式。

查询

    MATCH (n)
    RETURN
    CASE
    WHEN n.eyes = 'blue'
    THEN 1
    WHEN n.age < 40
    THEN 2
    ELSE 3 END AS result

结果

3.2.3 变量

当需要引用模式(Pattern)或者查询某一部分的时候,可以对其进行命名。针对不同部分的这些命名被称为变量。

例如:

    MATCH (n)-->(b)
    RETURN b

这里的n和b就是变量。

变量名是区分大小写的。它可以包含下划线、字母(a~z、A~Z)和数字(0~9),但必须以字母开头。如果变量名中需要用到其他字符,可以用反向单引号(`)将变量名括起来。比如,如果变量名中包含特殊字符μ,则可以按如下方式使用:

    match(`µg`:food) return `µg`

变量的命名规则同样也适用于属性的命名。

提示

变量仅在同一个查询内可见。它不能被用于后续的查询。如果有WITH连接起来的多个查询部分,变量必须列在WITH语句中才能应用到后续部分。详细参见“3.3.18 WITH”小节。

3.2.4 参数

Cypher支持带参数的查询,这意味着开发人员不是必须用字符串来构建查询。此外,这也让执行计划的缓存更容易。

参数能够用于WHERE语句中的字面值和表达式,START语句中的索引值、索引查询以及节点和关系的id。参数不能用于属性名、关系类型和标签,因为这些模式(Pattern)将作为查询结构的一部分被编译进查询计划。

合法的参数名是字母、数字以及两者的组合。下面是一个使用参数的完整例子。参数以JSON格式提供,具体如何提交它们取决于所使用的驱动程序。

3.2.4.1 字符串

参数

    {
    "name" : "Johan"
    }

可以有两种查询方式来使用参数,如下:

查询方式1

    MATCH (n)
    WHERE n.name = $name
    RETURN n

查询方式2

    MATCH (n { name: $name })
    RETURN n

3.2.4.2 正则表达式

参数

    {
    "regex" : ".*h.*"
    }

查询

    MATCH (n)
    WHERE n.name =~ $regex
    RETURN n.name

3.2.4.3 大小写敏感的字符串模式匹配

参数

    {
    "name" : "Michael"
    }

查询

    MATCH (n)
    WHERE n.name STARTS WITH $name
    RETURN n.name

3.2.4.4 创建带有属性的节点

参数

    {
    "props" : {
    "name" : "Andres",
    "position" : "Developer"
    }
    }

查询

    CREATE ($props)

3.2.4.5 创建带有多个属性的多个节点

参数

    {
    "props" : [ {
    "awesome" : true,
    "name" : "Andres",
    "position" : "Developer"
    }, {
    "children" : 3,
    "name" : "Michael",
    "position" : "Developer"
    } ]
    }

查询

    UNWIND $props AS properties
    CREATE (n:Person)
    SET n = properties
    RETURN n

3.2.4.6 设置节点的所有属性

注意:这将替换当前的所有属性。

参数

    {
    "props" : {
    "name" : "Andres",
    "position" : "Developer"
    }
    }

查询

    MATCH (n)
    WHERE n.name='Michaela'
    SET n = $props

3.2.4.7 SKIP和LIMIT

参数

    {
    "s" : 1,
    "l" : 1
    }

查询

    MATCH (n)
    RETURN n.name
    SKIP  $s
    LIMIT $l

3.2.4.8 节点id

参数

    {
    "id" : 0
    }

查询

    MATCH (n)
    WHERE id(n)= $id
    RETURN n.name

3.2.4.9 多个节点id

参数

    {
    "ids"  :  [  0,  1,  2  ]
    }

查询

    MATCH (n)
    WHERE id(n) IN $ids
    RETURN n.name

3.2.5 运算符

3.2.5.1 数学运算符

数学运算符包括+、-、*、/、%和^。

3.2.5.2 比较运算符

比较运算符包括=、<>、<、>、<=、>=、IS NULL和IS NOT NULL。

3.2.5.3 布尔运算符

布尔运算符包括AND、OR、XOR和NOT。

3.2.5.4 字符串运算符

连接字符串的运算符为+,正则表达式的匹配运算符为=~。

3.2.5.5 列表运算符

列表的连接也可以通过+运算符,可以用IN来检查列表中是否存在某个元素。

3.2.5.6 属性运算符

提示

Cypher 2.0版本以后,之前存在的属性运算符“? ”和“! ”已经被移除了。这个语法不再支持。对于不存在的属性将返回null。如果真的还需要?运算符的功能,可以使用(NOT(has(<ident>.prop)) OR <ident>.prop=<value>)。使用“? ”表达可选关系也被移除了,取而代之的是OPTINAL MATCH。

3.2.5.7 值的相等与比较

Cypher支持使用=和<>来比较两个值的相等/不相等关系,同类型的值只有它们是同一个值的时候才相等,如3 = 3和"x" <> "xy"。

只有两个Map的键相同且指向的值也相等的时候它们才相等。对于列表来说,只有它们包含相等值的相同序列的时候才相等,如[3, 4] = [1+2, 8/2]。

不同类型的值在比较相等的时候遵循以下规则:

● 路径可看作是一些节点和关系的列表,它等于所有包含相同序列节点和关系的所有列表。

● 对任何值测试是否 = 和 <> null都将返回null,这包括null = null和null <> null。唯一可靠地测试一个值是否为null的方法是使用IS NULL或者IS NOT NULL。

不同类型之间不能相互比较。特别地,节点、关系和映射之间不能相互比较。

3.2.5.8 值的排序与比较

比较运算符<=、<(升序)和>=、>(降序)可以用于值排序的比较,如下所示。

● 数字型值的排序比较采用数字顺序。

● java.lang.Double.NaN大于所有值。

● 字符串排序的比较采用字典顺序。如"x" < "xy"。

● 布尔值的排序遵循false < true。

● 当有个参数为null的时候,比较结果为null,如null < 3的结果为null。

● 将其他类型的值相互比较进行排序将报错。

3.2.5.9 链式比较运算

比较运算可以被任意地联结在一起,如x < y <= z等价于x < y AND y <= z。

正式地,如果a, b, c, …, y, z是表达式,op1, op2, …, opN是比较运算符,这时,a op1 b op2 c … y opN z等价于a op1 b and b op2 c and … y opN z。

例如:

    MATCH (n) WHERE 21 < n.age <= 30 RETURN n

等价于:

    MATCH (n) WHERE 21 < n.age AND n.age <= 30 RETURN n

该查询将匹配年龄介于21和30之间的所有节点。

3.2.6 注释

Cypher语言的注释类似其他语言,用双斜线//来注释行,例如:

    MATCH (n) RETURN n //这是行末尾注释
    MATCH (n)
    //这是整行注释
    RETURN n
    MATCH (n) WHERE n.property = '//这不是注释’ RETURN n

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一样,即属性将被设置到新创建的节点和关系中。

提示

模式在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的一步和两步的节点。这是一个查询一度和二度人脉的典型例子。

提示

变长关系不能用于CREATE和MERGE语句。

3.2.7.6 赋值给路径变量

如上所述,连接在一起的一系列节点和关系被称为路径。Cypher允许使用标识符给路径命名,例如:

    p= (a)-[*3..5]->(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]

结果

提示

如果返回一个范围的索引值越界了,那么返回直接从越界的地方进行截断。如果是单个元素的索引值越界,则返回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: <expression>。

● 变量选择器——投射一个变量,变量名作为键,变量的值作为投射的值。它的语法只有变量。

● 全属性选择器——投射来自map_variable中的所有键值对。

提示

如果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 { .title, .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的逻辑值表如表3-2所示。

表3-2 空值与AND、OR和XOR的逻辑值表

3.2.9.3 空值与IN

IN运算符遵循类似的逻辑。如果列表中存在某个值,结果就返回true;如果列表包含null值并且没有匹配到值,结果返回null;否则结果为false。表3-3给出一些例子。

表3-3 空值与IN运算符的例子

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)。