Cypher 基本概念

Cypher 是 Neo4j 数据库的查询语言,就如同 SQL 之于其他关系型数据库一样。Neo4j 作为一种图数据库,其数据均以节点、关系来存储。所以 Cypher 应该能够有某种语法来描述节点和关系,并能表征他们之间的关系。

比如下面 cypher 用来查找所有居住在北京的人。

(p:Person) -[:LIVES_IN]-> (:City {name: 'Beijing'}) return p

节点语法

() # 匿名的节点
(matrix) # 使用一个变量 matrix 与这个节点关联
(:Movie) # 类型为 Movie 的节点
(matrix:Movie)
(matrix:Movie {title: "The Matrix"}) # 指含有特定属性的某类节点
(matrix:Movie {title: "The Matrix", released: 1997})

关系语法

--> # 非直接相连的关系
-[role]-> # 使用变量关联此关系
-[:ACTED_IN]-> # 类型为 ACTED_IN 的关系
-[role:ACTED_IN]->
-[role:ACTED_IN {roles: ["Neo"]}]-> # 含有特定属性的关系

模式语法

将节点和关系组合起来,得出一个模式,而后使用此模式进行匹配:

(keanu:Person:Actor {name:  "Keanu Reeves"} )
-[role:ACTED_IN     {roles: ["Neo"] } ]->
(matrix:Movie       {title: "The Matrix"} )

模式变量

模式由多个节点和关系组成,通常较长,可以将其保存为一个变量。在一条 cypher 中的其他位置,便可以使用此模式。

acted_in = (:Person)-[:ACTED_IN]->(:Movie)

子句

如同 SQL 中的 SELECTWHERE 等子句,在 cypher 中也有这类子句,用来进行查找、过滤、排序等操作。

创建

使用 CREATE 关键字能够创建节点、关系、模式,如下面语句,创建了一个类型为 Movie 的节点,且含有两个属性:

CREATE (:Movie { title:"The Matrix",released:1997 })

还可以同时使用多个 CREATE 创建更为复杂的模式:

CREATE (a:Person { name:"Tom Hanks",
  born:1956 })-[r:ACTED_IN { roles: ["Forrest"]}]->(m:Movie { title:"Forrest Gump",released:1994 })
CREATE (d:Person { name:"Robert Zemeckis", born:1951 })-[:DIRECTED]->(m)
RETURN a,d,r,m

为现有的节点添加关系:

MATCH (p:Person { name:"Tom Hanks" })
CREATE (m:Movie { title:"Cloud Atlas",released:2012 })
CREATE (p)-[r:ACTED_IN { roles: ['Zachry']}]->(m)
RETURN p,r,m

匹配

MATCH (m:Movie)
RETURN m


MATCH (p:Person { name:"Keanu Reeves" })
RETURN p


MATCH (p:Person { name:"Tom Hanks" })-[r:ACTED_IN]->(m:Movie)
RETURN m.title, r.roles

添加 label

match (n {id:desired-id})
set n :newLabel
return n

Merge

有时候希望给某个节点添加属性,但又不能保证其存在于库中,此时可以使用 MERGE

首先查找某个模式,如果存在便得出这个模式,不存在则创建。在 ON CREATE 中指定的操作,会在创建的时候进行。

MERGE (m:Movie { title:"Cloud Atlas" })
ON CREATE SET m.released = 2012
RETURN m

如果不存在 pm 之间的 ACTED_IN 关系,则创建,并在创建时添加属性。

MATCH (m:Movie { title:"Cloud Atlas" })
MATCH (p:Person { name:"Tom Hanks" })
MERGE (p)-[r:ACTED_IN]->(m)
ON CREATE SET r.roles =['Zachry']
RETURN p,r,m

得到正确的结果

过滤结果

可以在 WHERE 中对 MATCH 的结果进行过滤:

MATCH (m:Movie)
WHERE m.title = "The Matrix"
RETURN m

更好的方式是,在 MATCH 中指定更细致的条件:

MATCH (m:Movie { title: "The Matrix" })
RETURN m

WHERE 子句中可以使用正则表达式:

MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name =~ "K.+" OR m.released > 2000 OR "Neo" IN r.roles
RETURN p,r,m

p.name =~ "K.+" 表示 name 以 K 开头。

WHERE 中还可以指定一个模式,可以过滤掉符合或者不符合这个模式的结果。

MATCH (p:Person)-[:ACTED_IN]->(m)
WHERE NOT (p)-[:DIRECTED]->()
RETURN p,m

返回结果

可以对结果整体来处理:

MATCH (:Person)
RETURN count(*) AS people
+# Cypher 基础教程-+
| people |
+# Cypher 基础教程-+
| 3      |
+# Cypher 基础教程-+
1 row

使用 DISTINCT 滤除重复: count(DISTINCT role)

count 会将其他列来进行分组,下面例子中会使用 actor,director 作为分组的键值:

MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie)<-[:DIRECTED]-(director:Person)
RETURN actor,director,count(*) AS collaborations

对结果排序

ORDER BY 可以基于任何可以访问到的变量、属性进行排序,默认是正序,如需倒序,使用关键词 DESC

MATCH (a:Person)-[:ACTED_IN]->(m:Movie)
RETURN a,count(*) AS appearances
ORDER BY appearances DESC;

Collect

使用 collect 可以将多个匹配收集到一个数组中。

MATCH (m:Movie)<-[:ACTED_IN]-(a:Person)
RETURN m.title AS movie, collect(a.name) AS cast, count(*) AS actors

组合多条查询语句

UNION

UNION 可以将两个查询结果合并起来:

MATCH (actor:Person)-[r:ACTED_IN]->(movie:Movie)
RETURN actor.name AS name, type(r) AS acted_in, movie.title AS title
UNION
MATCH (director:Person)-[r:DIRECTED]->(movie:Movie)
RETURN director.name AS name, type(r) AS acted_in, movie.title AS title
+-------------------------------------------------+
| name              | acted_in   | title          |
+-------------------------------------------------+
| "Tom Hanks"       | "ACTED_IN" | "Cloud Atlas"  |
| "Tom Hanks"       | "ACTED_IN" | "Forrest Gump" |
| "Robert Zemeckis" | "DIRECTED" | "Forrest Gump" |
+-------------------------------------------------+
3 rows

WITH

WITH 能将多个语句连接起来,就像管道一样,前一个语句的输出作为下一个语句的输入:

MATCH (person:Person)-[:ACTED_IN]->(m:Movie)
WITH person, count(*) AS appearances, collect(m.title) AS movies
WHERE appearances > 1
RETURN person.name, appearances, movies

索引

对节点的某个属性建立索引,之后使用该属性来进行查询时,能够加快查询速度。

CREATE INDEX ON :Actor(name)

MATCH (actor:Actor { name: "Tom Hanks" })
RETURN actor;          

当需要通过某个属性查询所有满足条件的节点时,分别在各不同 label 的节点上建立索引,就不起作用了。

MATCH (n:{ name:"xxx" }) RETURN n

上面这条语句会很慢,他需要遍历所有的节点。为此,可以给所有节点增加一个共有的 label,然后建立索引。

// 给所有节点增加一个 label 叫做 Node
match (n) set n :Node

// 在 Node 的 name 属性上建立索引
create index on :Node(name)