GraphGists

首先让我们获取一些数据来检索结果

CREATE (matrix:Movie {title:"The Matrix",released:1997})
CREATE (cloudAtlas:Movie {title:"Cloud Atlas",released:2012})
CREATE (forrestGump:Movie {title:"Forrest Gump",released:1994})
CREATE (keanu:Person {name:"Keanu Reeves", born:1964})
CREATE (robert:Person {name:"Robert Zemeckis", born:1951})
CREATE (tom:Person {name:"Tom Hanks", born:1956})
CREATE (tom)-[:ACTED_IN {roles:["Forrest"]}]->(forrestGump)
CREATE (tom)-[:ACTED_IN {roles:['Zachry']} ]->(cloudAtlas)
CREATE (robert)-[:DIRECTED]->(forrestGump)

这是我们将开始使用的数据

Nodes created: 6
Relationships created: 3
Properties set: 14
Labels added: 6

过滤结果

到目前为止,我们已经在图中匹配了模式,并始终返回我们找到的所有结果。通常情况下,我们会有一些条件来决定我们想要查看什么。类似于SQL,这些过滤条件是在WHERE子句中表达的。该子句允许使用任意数量的布尔表达式(谓词),并结合使用ANDORXORNOT。最简单的谓词是比较,尤其是相等性。

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

对于一个或多个属性的相等性,也可以使用更紧凑的语法

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

一个可能有点令人惊讶的方面是,您甚至可以使用模式作为谓词。其中MATCH扩展了匹配的模式的数量和形状,模式谓词限制了当前的结果集。它只允许通过也满足附加模式(或NOT)的路径。

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

在这里,我们找到演员,因为他们具有ACTED_IN关系,但随后跳过那些曾经DIRECTED任何电影的演员。

还有更高级的过滤方式,例如集合谓词,我们将在后面讨论。

返回结果

到目前为止,我们仅通过其标识符直接返回节点、关系或路径。但是,RETURN子句实际上可以返回任意数量的表达式。但是,Cypher中的表达式到底是什么?

最简单的表达式是文字值,例如数字、字符串和数组,如[1,2,3],以及映射,如{name:"Tom Hanks", born:1964, movies:["Forrest Gump", …​], count:13}。您可以使用点语法(如n.name)访问任何节点、关系或映射的单个属性。可以使用下标(如names[0]movies[1..-1])检索数组的单个元素或切片。每个函数评估(如length(array)toInt("12")substring("2014-07-01",0,4)coalesce(p.nickname,"n/a"))也是一个表达式。

您在WHERE中使用的谓词被视为布尔表达式。

当然,可以组合和连接更简单的表达式以形成更复杂的表达式。

默认情况下,表达式本身将用作列的标签,在许多情况下,您希望使用expression AS alias将其别名为更易于理解的名称。稍后您可以使用其别名引用该列。

MATCH (p:Person)
RETURN p, p.name AS name, upper(p.name), coalesce(p.nickname,"n/a") AS nickname, {name: p.name, label:head(labels(p))} AS person

如果您对唯一结果感兴趣,则可以在RETURN之后使用DISTINCT关键字来指示。

聚合信息

在许多情况下,您希望在遍历图中的模式时聚合或分组遇到的数据。在 Cypher 中,聚合发生在RETURN子句中,同时计算最终结果。支持许多常见的聚合函数,例如countsumavgminmax,但还有更多其他函数。

可以通过以下方式计算数据库中的人数

MATCH (:Person)
RETURN count(*) as people

请注意,在聚合过程中会跳过NULL值。要仅聚合唯一值,请使用DISTINCT,如count(DISTINCT role)

Cypher 中的聚合可以正常工作。您指定要聚合的结果列,Cypher 将使用所有非聚合列作为分组键

聚合会影响哪些数据在排序或后续查询部分中仍然可见。

要找出演员和导演合作的频率,您可以运行以下语句

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

通常情况下,您希望在聚合count(x)之后进行排序和分页。

排序和分页

排序与其他查询语言类似,使用ORDER BY expression [ASC|DESC]子句。只要该表达式可以从返回的信息中计算出来,它就可以是前面讨论过的任何表达式。

例如,如果您返回person.name,您仍然可以ORDER BY person.age,因为两者都可以从person引用中访问。您不能按无法从您返回的信息中推断出的内容进行排序。这对于聚合和DISTINCT返回值尤其重要,因为两者都会删除聚合数据的可见性。

分页是SKIP {offset} LIMIT {count}的直接使用。

一个常见的模式是聚合计数(分数或频率),按其排序,并仅返回前 n 个条目。

例如,要查找最多产的演员,您可以执行以下操作

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

收集聚合

最有用的聚合函数是collect,顾名思义,它会将所有聚合值收集到一个真实的数组或列表中。这在许多情况下非常方便,因为您在聚合时不会丢失详细信息。

Collect 非常适合检索典型的父子结构,其中每行返回一个核心实体(父级、根或头部),以及使用collect创建的所有相关信息。这意味着无需为每个子行重复父级信息,甚至无需运行 1+n 个语句来分别检索父级及其子级。

要检索数据库中每部电影的演员阵容,您可以使用以下语句

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

由 collect 创建的列表既可以由使用 Cypher 结果的客户端使用,也可以直接在语句中使用任何集合函数或谓词。