教程目录 引自:https://neo4j.com/docs/getting-started/cypher-intro/results/
获取正确的结果
示例图
在本节中,使用了两个示例数据集。第一个图基于电影数据库。以下代码块可帮助您创建用于探索 Cypher® 查询的数据:
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)
这是生成的图:
第二个数据集是一个由人员、他们工作的公司以及他们喜欢的技术组成的小型网络。您可以在以下章节中找到它的图像。
过滤结果
到目前为止,您已经匹配了图中的模式,并且总是返回您找到的所有结果。现在让我们看看过滤结果并仅返回您感兴趣的数据子集的选项。这些过滤器条件使用 WHERE
子句表示。此子句允许使用任意数量的布尔表达式、谓词,并与 AND
、OR
、XOR
和 NOT
组合。最简单的谓词是比较;尤其是相等。
MATCH (m:Movie)
WHERE m.title = 'The Matrix'
RETURN m
Rows: 1
+------------------------------------------------+
| m |
+------------------------------------------------+
| (:Movie {title: 'The Matrix', released: 1997}) |
+------------------------------------------------+
上面的查询使用 WHERE
子句,等效于此查询,该查询在模式匹配中包含条件:
MATCH (m:Movie {title: 'The Matrix'})
RETURN m
Cypher 旨在具有灵活性,因此通常有多种编写查询的方法。
其他选项包括数值比较、匹配正则表达式以及检查列表中是否存在值。
以下示例中的 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
基于给定的逻辑运算符 OR
和图模式 (p:Person)-[r:ACTED_IN]->(m:Movie)
,查询结果必须满足以下要求之一:
- 该人的名字以字母“K”开头。
- 这部电影是在 2000 年之后发行的。
- 角色是“Neo”。
在我们的例子中,只有第二个条件匹配图模式 (p:Person)-[r:ACTED_IN]->(m:Movie)
,因此输出如下:
Rows: 1
+-------------------------------------------------------------------------------------------------------------------------------+
| p | r | m |
+-------------------------------------------------------------------------------------------------------------------------------+
| (:Person {name: 'Tom Hanks', born: 1956}) | [:ACTED_IN {roles: ['Zachry']}] | (:Movie {title: 'Cloud Atlas', released: 2012}) |
+-------------------------------------------------------------------------------------------------------------------------------+
一个高级方面是模式可以用作谓词。MATCH
扩展了匹配的模式的数量和形状,而模式谓词限制了当前结果集。它只允许满足指定模式的路径通过。正如您可以预期的那样,NOT
的使用只允许不满足指定模式的路径通过。
MATCH (p:Person)-[:ACTED_IN]->(m)
WHERE NOT (p)-[:DIRECTED]->()
RETURN p, m
Rows: 2
+----------------------------------------------------------------------------------------------+
| p | m |
+----------------------------------------------------------------------------------------------+
| (:Person {name: 'Tom Hanks', born: 1956}) | (:Movie {title: 'Cloud Atlas', released: 2012}) |
| (:Person {name: 'Tom Hanks', born: 1956}) | (:Movie {title: 'Forrest Gump', released: 1994}) |
+----------------------------------------------------------------------------------------------+
在这里,您可以找到演员,因为他们支持 ACTED_IN
关系,然后跳过那些曾经 DIRECTED
任何电影的演员。
还有更高级的过滤方法,例如列表谓词,这将在本节后面讨论。
查询值范围
经常有查询想要查找特定范围内的数据。日期或数字范围可用于检查特定时间线、年龄值或其他用途中的事件。
此标准的语法与 SQL 和其他编程语言逻辑结构非常相似,用于检查值的范围。
以下数据集用于演示这些情况的 Cypher 查询。
要重现数据集,请运行以下 Cypher 查询:
CREATE (diana:Person {name: "Diana"})
CREATE (melissa:Person {name: "Melissa", twitter: "@melissa"})
CREATE (dan:Person {name: "Dan", twitter: "@dan", yearsExperience: 6})
CREATE (sally:Person {name: "Sally", yearsExperience: 4})
CREATE (john:Person {name: "John", yearsExperience: 5})
CREATE (jennifer:Person {name: "Jennifer", twitter: "@jennifer", yearsExperience: 5})
CREATE (joe:Person {name: "Joe"})
CREATE (mark:Person {name: "Mark", twitter: "@mark"})
CREATE (ann:Person {name: "Ann"})
CREATE (xyz:Company {name: "XYZ"})
CREATE (x:Company {name: "Company X"})
CREATE (a:Company {name: "Company A"})
CREATE (Neo4j:Company {name: "Neo4j"})
CREATE (abc:Company {name: "ABC"})
CREATE (query:Technology {type: "Query Languages"})
CREATE (etl:Technology {type: "Data ETL"})
CREATE (integrations:Technology {type: "Integrations"})
CREATE (graphs:Technology {type: "Graphs"})
CREATE (dev:Technology {type: "Application Development"})
CREATE (java:Technology {type: "Java"})
CREATE (diana)-[:LIKES]->(query)
CREATE (melissa)-[:LIKES]->(query)
CREATE (dan)-[:LIKES]->(etl)<-[:LIKES]-(melissa)
CREATE (xyz)<-[:WORKS_FOR]-(sally)-[:LIKES]->(integrations)<-[:LIKES]-(dan)
CREATE (sally)<-[:IS_FRIENDS_WITH]-(john)-[:LIKES]->(java)
CREATE (john)<-[:IS_FRIENDS_WITH]-(jennifer)-[:LIKES]->(java)
CREATE (john)-[:WORKS_FOR]->(xyz)
CREATE (sally)<-[:IS_FRIENDS_WITH]-(jennifer)-[:IS_FRIENDS_WITH]->(melissa)
CREATE (joe)-[:LIKES]->(query)
CREATE (x)<-[:WORKS_FOR]-(diana)<-[:IS_FRIENDS_WITH]-(joe)-[:IS_FRIENDS_WITH]->(mark)-[:LIKES]->(graphs)<-[:LIKES]-(jennifer)-[:WORKS_FOR {startYear: 2017}]->(Neo4j)
CREATE (ann)<-[:IS_FRIENDS_WITH]-(jennifer)-[:IS_FRIENDS_WITH]->(mark)
CREATE (john)-[:LIKES]->(dev)<-[:LIKES]-(ann)-[:IS_FRIENDS_WITH]->(dan)-[:WORKS_FOR]->(abc)
CREATE (ann)-[:WORKS_FOR]->(abc)
CREATE (a)<-[:WORKS_FOR]-(melissa)-[:LIKES]->(graphs)<-[:LIKES]-(diana)
假设您想知道谁拥有三到七年的经验。以下代码块显示了这种情况的 Cypher 查询。
MATCH (p:Person)
WHERE 3 <= p.yearsExperience <= 7
RETURN p
测试属性是否存在
您可能只对节点或关系上是否存在属性感兴趣。例如,您可能想检查系统中的哪些客户拥有 Twitter 句柄,以便您可以显示相关内容。或者,您可以检查您的所有员工是否都具有开始日期属性,以验证哪些实体可能需要更新。
请记住:在 Neo4j 中,只有当属性具有值时,该属性才存在(存储)。不会存储空属性。这确保仅为您的节点和关系保留有价值的必要信息。
要在 Neo4j v5 中编写这种类型的存在检查,您需要使用 IS NOT NULL
谓词来仅包含属性存在的节点或关系。
Cypher 代码写在下面的块中。
//Query1: find all users who have a twitter property
MATCH (p:Person)
WHERE p.twitter IS NOT NULL
RETURN p.name;
//Query2: find all WORKS_FOR relationships that have a startYear property
MATCH (p:Person)-[rel:WORKS_FOR]->(c:Company)
WHERE rel.startYear IS NOT NULL
RETURN p, rel, c;
Query1 结果:
Rows: 4
+------------------------+
| p.name |
+------------------------+
| 'Melissa' |
| 'Dan' |
| 'Jennifer' |
| 'Mark' |
+------------------------+
Query2 结果:
检查字符串 - 部分值、模糊搜索
某些场景需要查询语法来匹配字符串中的部分值或广泛类别。要执行这种类型的查询,您需要一些灵活性以及用于字符串匹配和搜索的选项。无论您是寻找以某个值开头、结尾还是包含某个值的字符串,Cypher 都可以轻松且高效地处理它。
Cypher 中有一些关键字与 WHERE
子句一起使用来测试字符串属性值。STARTS WITH
关键字允许您检查属性的值是否以您指定的字符串开头。使用 CONTAINS
关键字,您可以检查指定的字符串是否是属性值的一部分。ENDS_WITH
关键字检查属性字符串的末尾是否包含您指定的值。
以下 Cypher 块中包含每个示例。
//check if a property starts with 'M'
MATCH (p:Person)
WHERE p.name STARTS WITH 'M'
RETURN p.name;
//check if a property contains 'a'
MATCH (p:Person)
WHERE p.name CONTAINS 'a'
RETURN p.name;
//check if a property ends with 'n'
MATCH (p:Person)
WHERE p.name ENDS WITH 'n'
RETURN p.name;
您还可以使用正则表达式来测试字符串的值。例如,您可以查找共享名字的所有 Person
节点,或者您可以找到所有具有特定部门代码的类。
让我们看一个例子。
MATCH (p:Person)
WHERE p.name =~ 'Jo.*'
RETURN p.name
Rows: 2
+--------------------------------+
| p.name |
+--------------------------------+
| 'John' |
| 'Joe' |
+--------------------------------+
就像在 SQL 和其他语言中一样,您可以检查属性值是否是列表中的值。IN
关键字允许您指定一个值数组,并根据列表中的每个值验证属性的内容。
这是一个例子:
MATCH (p:Person)
WHERE p.yearsExperience IN [1, 5, 6]
RETURN p.name, p.yearsExperience
Rows: 3
+--------------+-----------------+
| p.name | p.yearsExp |
+--------------+-----------------+
| 'Jennifer' | 5 |
| 'Dan' | 6 |
| 'John' | 5 |
+--------------+-----------------+
过滤模式
使图独一无二的一件事是它对关系的关注。就像您可以根据节点标签或属性过滤查询一样,您也可以根据关系或模式过滤结果。这允许您测试模式是否也具有某种关系或没有,或者是否存在另一种模式。
以下 Cypher 代码显示了如何完成此操作。
//Query1: find which people are friends of someone who works for Neo4j
MATCH (p:Person)-[r:IS_FRIENDS_WITH]->(friend:Person)
WHERE exists((p)-[:WORKS_FOR]->(:Company {name: 'Neo4j'}))
RETURN p, r, friend;
//Query2: find Jennifer's friends who do not work for a company
MATCH (p:Person)-[r:IS_FRIENDS_WITH]->(friend:Person)
WHERE p.name = 'Jennifer'
AND NOT exists((friend)-[:WORKS_FOR]->(:Company))
RETURN friend.name;
Query1 结果:
Query2 结果:
Rows: 1
+--------------------------------+
| friend.name |
+--------------------------------+
| 'Mark' |
+--------------------------------+
可选模式
在某些情况下,您可能想要从模式中检索结果,即使它们不匹配整个模式或所有条件。这就是 SQL 中外部联接的工作方式。在 Cypher 中,您可以使用 OPTIONAL MATCH
模式尝试匹配它,但如果它没有找到结果,则这些行将为这些值返回 null。
您可以通过查询名称以特定字母开头并且可能为公司工作的人员来查看这在 Cypher 中的外观。
//Find all people whose name starts with J and who may work for a company.
MATCH (p:Person)
WHERE p.name STARTS WITH 'J'
OPTIONAL MATCH (p)-[:WORKS_FOR]-(other:Company)
RETURN p.name, other.name;
Rows: 3
+--------------------------------+
| p.name | other.name |
+--------------------------------+
| 'Jennifer' | 'Neo4j' |
| 'John' | 'XYZ' |
| 'Joe' | null |
+--------------------------------+
请注意,Joe 会被返回,因为他的名字以字母“J”开头,但他的公司名称为 null。那是因为他没有与 COMPANY
节点的 WORKS_FOR
关系。由于您使用了 OPTIONAL MATCH
,因此他的 Person
节点仍然从第一个匹配项返回,但未找到第二个匹配项,因此返回 null。
要查看差异,请尝试在第二个匹配项的前面运行没有 OPTIONAL
的查询。您可以看到不再返回 Joe 的行。那是因为 Cypher 使用 AND
匹配读取语句,因此该人必须匹配第一个条件(名称以“J”开头)和第二个条件(该人为公司工作)。
更复杂的模式
即使在这一点上,您也能够处理许多简单的图查询。但是,当您想要将模式扩展到单个关系之外时会发生什么?如果您想知道除了 Jennifer 之外还有谁喜欢图,该怎么办?
我们通过简单地添加到我们的第一个模式或匹配其他模式来处理此功能和许多其他功能。让我们看几个例子。
//Query1: find who likes graphs besides Jennifer
MATCH (j:Person {name: 'Jennifer'})-[r:LIKES]-(graph:Technology {type: 'Graphs'})-[r2:LIKES]-(p:Person)
RETURN p.name;
//Query2: find who likes graphs besides Jennifer that she is also friends with
MATCH (j:Person {name: 'Jennifer'})-[:LIKES]->(:Technology {type: 'Graphs'})<-[:LIKES]-(p:Person),
(j)-[:IS_FRIENDS_WITH]-(p)
RETURN p.name;
Query1 结果:
Rows: 3
+-----------------------+
| p.name |
+-----------------------+
| 'Diana' |
| 'Mark' |
| 'Melissa' |
+-----------------------+
Query2 结果:
Rows: 2
+-----------------------+
| p.name |
+-----------------------+
| 'Mark' |
| 'Melissa' |
+-----------------------+
请注意,在第二个查询中,在第一个 MATCH
行之后使用逗号,并在下一行添加另一个模式以进行匹配。这允许您将模式链接在一起,类似于您上面使用的 WHERE exists(<pattern>)
语法。使用此结构,您可以添加多个不同的模式并将它们链接在一起,从而允许您使用某些模式遍历图的各个部分。
返回结果
到目前为止,您已经通过它们的变量直接返回了节点、关系和路径。但是,RETURN
子句可以返回任意数量的表达式。但是 Cypher 中的表达式是什么?
最简单的表达式是文字值。文字值的示例包括:数字、字符串、数组(例如:[1,2,3]
)和映射(例如:{name: 'Tom Hanks', born:1964, movies: ['Forrest Gump', ...], count: 13}
)。可以使用点语法访问任何节点、关系或映射的各个属性,例如:n.name
。可以使用下标检索数组的各个元素或切片,例如:names[0]
和 movies[1..-1]
。每个函数评估,例如:length(array)
、toInteger('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,
toUpper(p.name),
coalesce(p.nickname, 'n/a') AS nickname,
{name: p.name, label: head(labels(p))} AS person
Rows: 3
+-------------------------------------------------------------------------------------------------------------------------------------------------+
| p | name | toUpper(p.name) | nickname | person |
+-------------------------------------------------------------------------------------------------------------------------------------------------+
| (:Person {name: 'Keanu Reeves', born: 1964}) | 'Keanu Reeves' | 'KEANU REEVES' | 'n/a' | {name: 'Keanu Reeves', label: 'Person'} |
| (:Person {name: 'Robert Zemeckis', born: 1951}) | 'Robert Zemeckis' | 'ROBERT ZEMECKIS' | 'n/a' | {name: 'Robert Zemeckis', label: 'Person'} |
| (:Person {name: 'Tom Hanks', born: 1956}) | 'Tom Hanks' | 'TOM HANKS' | 'n/a' | {name: 'Tom Hanks', label: 'Person'} |
+-------------------------------------------------------------------------------------------------------------------------------------------------+
如果您希望仅显示唯一结果,则可以在 RETURN
之后使用 DISTINCT
关键字:
MATCH (n)
RETURN DISTINCT labels(n) AS Labels
Rows: 2
+------------+
| Labels |
+------------+
| ['Movie'] |
| ['Person'] |
+------------+
返回唯一结果
您可以使用 Cypher 中的 DISTINCT
关键字返回唯一结果。由于节点的多个路径或满足多个条件的节点,您的某些查询可能会返回重复的结果。这种冗余会使结果混乱,并且难以筛选长列表以找到您需要的内容。
要删除重复的实体,您可以使用 DISTINCT
关键字。
//Query: find people who have a twitter and like graphs or query languages
MATCH (user:Person)
WHERE user.twitter IS NOT null
WITH user
MATCH (user)-[:LIKES]-(t:Technology)
WHERE t.type IN ['Graphs','Query Languages']
RETURN DISTINCT user.name
Query results:
Rows: 3
+-----------------------+
| user.name |
+-----------------------+
| 'Jennifer' |
| 'Melissa' |
| 'Mark' |
+-----------------------+
对于前面的查询,用例是您正在为 Cypher 推出一个新的 Twitter 帐户,其中包含提示和技巧,并且您想要通知拥有 Twitter 帐户并且喜欢图或查询语言的用户。查询的前两行查找具有 Twitter 句柄的 Person
节点。然后,您使用 WITH
将这些用户传递到下一个 MATCH
,您可以在其中找出该人是否喜欢图或查询语言。请注意,在没有 DISTINCT
关键字的情况下运行此语句会导致“Melissa”显示两次。这是因为她喜欢图,而且她也喜欢查询语言。当使用 DISTINCT
时,您只会检索唯一用户。
限制结果数
有时您想要一个抽样集,或者您只想一次提取这么多结果来更新或处理。LIMIT
关键字获取查询的输出,并根据您指定的数字限制返回的卷。
例如,您可以找到图中每个人的朋友数。如果该图有成千上万或数百万个节点和关系,则返回的结果数将是巨大的。如果您只关心拥有最多朋友的前三个人怎么办?让我们为此编写一个查询!
//Query: find the top 3 people who have the most friends
MATCH (p:Person)-[r:IS_FRIENDS_WITH]-(other:Person)
RETURN p.name, count(other.name) AS numberOfFriends
ORDER BY numberOfFriends DESC
LIMIT 3
Rows: 3
+--------------------------------+
| p.name | numberOfFriends |
+--------------------------------+
| 'Jennifer' | 5 |
| 'Mark' | 2 |
| 'Ann' | 2 |
+--------------------------------+
该查询提取人员以及他们连接的朋友,并返回人员姓名和他们的朋友的计数。您可以只运行查询的那么多内容并返回名称和朋友计数的混乱列表,但您可能想要根据每个人拥有的朋友数量(从顶部最大的数字开始 (DESC))对列表进行排序。您也可以运行查询的那么多内容以查看所有排序的朋友和计数,但在上面的示例中,已从图中提取了拥有最多朋友的前三个人。LIMIT
从排序列表中提取顶部结果。
尝试通过删除 ORDER BY
和 LIMIT
行来混合查询,然后分别添加每一行。请注意,仅删除 ORDER BY
行会从列表中提取起始三个值,从而获得返回结果的随机抽样。
聚合信息
在许多情况下,我们希望在遍历图中的模式时聚合或分组遇到的数据。在 Cypher 中,聚合发生在 RETURN
子句中,同时计算最终结果。支持许多常见的聚合函数,例如 count
、sum
、avg
、min
和 max
,但还有更多。
可以通过以下方式实现计算电影数据库中的人数:
MATCH (:Person)
RETURN count(*) AS people
Rows: 1
+--------+
| people |
+--------+
| 3 |
+--------+
如果要跳过空值,请使用函数 count(variable)
。
要仅聚合唯一值,请使用 DISTINCT
运算符,例如:count(DISTINCT role)
。
聚合在 Cypher 中隐式工作。您指定要聚合的结果列。Cypher 使用所有非聚合列作为分组键。
聚合会影响在排序或后续查询部分中仍然可见的数据。
以下语句找出演员和导演一起工作的频率:
MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie)<-[:DIRECTED]-(director:Person)
RETURN actor, director, count(*) AS collaborations
Rows: 1
+--------------------------------------------------------------------------------------------------------------+
| actor | director | collaborations |
+--------------------------------------------------------------------------------------------------------------+
| (:Person {name: 'Tom Hanks', born: 1956}) | (:Person {name: 'Robert Zemeckis', born: 1951}) | 1 |
+--------------------------------------------------------------------------------------------------------------+
有三种不同的方法可以使用 count()
函数:
count(*)
:计算结果并返回匹配行数。count(n)
:计算n
的出现次数(排除空值)。您可以指定节点、关系或属性,供 Cypher 计数。count(DISTINCT variable)
:DISTINCT
运算符从结果中删除重复项。
在数据集“人员、技术和公司”中,某些 Person
节点具有 Twitter 句柄,而另一些则没有。如果您运行下面的第一个示例查询,您将看到 twitter
属性对四个人有一个值,而对其他五个人则为 null。第二个和第三个查询显示了如何使用不同的计数选项。
//Query1: see the list of Twitter handle values for Person nodes
MATCH (p:Person)
RETURN p.twitter;
Query1 结果:
Rows: 9
+--------------+
| p.twitter |
+--------------+
| '@jennifer' |
| '@melissa' |
| null |
| '@mark' |
| '@dan' |
| null |
| null |
| null |
| null |
+--------------+
//Query2: count of the non-null `twitter` property of the Person nodes
MATCH (p:Person)
RETURN count(p.twitter);
Query2 结果:
Rows: 1
+-------------------+
| count(p.twitter) |
+-------------------+
| 4 |
+-------------------+
//Query3: count on the Person nodes
MATCH (p:Person)
RETURN count(*);
Query3 结果:
Rows: 1
+-------------------+
| count(*) |
+-------------------+
| 9 |
+-------------------+
收集聚合
一个非常有用的聚合函数是 collect(expression)
,它返回由表达式返回的值的单个聚合列表。这在许多情况下非常有用,因为在聚合时不会丢失任何详细信息。
collect()
非常适合检索典型的父子结构,其中每行返回一个核心实体(父级、根或头),所有依赖信息都在使用 collect()
创建的关联列表中。这意味着无需为每个子行重复父信息,也无需运行 n+1 个语句来单独检索父及其子项。
以下语句可用于检索数据库中每部电影的演员阵容:
MATCH (m:Movie)<-[:ACTED_IN]-(a:Person)
RETURN m.title AS movie, collect(a.name) AS cast, count(*) AS actors
Rows: 2
+-----------------------------------------+
| movie | cast | actors |
+-----------------------------------------+
| 'Forrest Gump' | ['Tom Hanks'] | 1 |
| 'Cloud Atlas' | ['Tom Hanks'] | 1 |
+-----------------------------------------+
由 collect()
创建的列表可以从使用 Cypher 结果的客户端使用,也可以直接在具有任何列表函数或谓词的语句中使用。
循环遍历列表值
如果您有一个想要检查或分离值的列表,Cypher 提供了 UNWIND
子句。这与 collect()
的作用相反,并将列表分离为单独行上的各个值。
在导入数据以及日常数组和其他类型的列表时,UNWIND
经常用于循环遍历 JSON 和 XML 对象。让我们看几个示例,我们假设某人喜欢的技术也意味着他们对每项技术都有一些经验。如果您有兴趣雇用熟悉图或查询语言的人员,您可以编写以下查询来查找要面试的人员。
//Query1: for a list of techRequirements, look for people who have each skill
WITH ['Graphs','Query Languages'] AS techRequirements
UNWIND techRequirements AS technology
MATCH (p:Person)-[r:LIKES]-(t:Technology {type: technology})
RETURN t.type, collect(p.name) AS potentialCandidates;
Query1 结果:
Rows: 2
+-------------------+------------------------------------------+
| t.type | potentialCandidates |
+-------------------+------------------------------------------+
| 'Graphs' | ['Diana', 'Mark', 'Melissa', 'Jennifer'] |
| 'Query Languages' | ['Diana', 'Melissa', 'Joe'] |
+-------------------+------------------------------------------+
//Query2: for numbers in a list, find candidates who have that many years of experience
WITH [4, 5, 6, 7] AS experienceRange
UNWIND experienceRange AS number
MATCH (p:Person)
WHERE p.yearsExp = number
RETURN p.name, p.yearsExp;
Query2 结果:
Rows: 4
+--------------+-----------------+
| p.name | p.yearsExp |
+--------------+-----------------+
| 'Sally' | 4 |
| 'Jennifer' | 5 |
| 'John' | 5 |
| 'Dan' | 6 |
+--------------+-----------------+
排序和分页
通常在使用 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
Rows: 1
+---------------------------------------------------------+
| a | appearances |
+---------------------------------------------------------+
| (:Person {name: 'Tom Hanks', born: 1956}) | 2 |
+---------------------------------------------------------+
排序结果
如果您可以按最多或最少的经验对我们先前示例中的潜在招聘候选人列表进行排序,那么它可能会更有用。或者,您可能想要按年龄对我们所有的人员进行排名。
ORDER BY
关键字根据您在升序或降序中指定的值对结果进行排序(升序是默认值)。让我们使用与 UNWIND
相同的示例查询,看看如何对候选人进行排序。
//Query1: for a list of techRequirements, look for people who have each skill
WITH ['Graphs','Query Languages'] AS techRequirements
UNWIND techRequirements AS technology
MATCH (p:Person)-[r:LIKES]-(t:Technology {type: technology})
WITH t.type AS technology, p.name AS personName
ORDER BY technology, personName
RETURN technology, collect(personName) AS potentialCandidates;
Query1 结果:
Rows: 2
+-------------------+------------------------------------------+
| technology | potentialCandidates |
+-------------------+------------------------------------------+
| 'Graphs' | ['Diana', 'Jennifer', 'Mark', 'Melissa'] |
| 'Query Languages' | ['Diana', 'Joe'] |
+-------------------+------------------------------------------+
//Query2: for numbers in a list, find candidates who have that many years of experience
WITH [4, 5, 6, 7] AS experienceRange
UNWIND experienceRange AS number
MATCH (p:Person)
WHERE p.yearsExp = number
RETURN p.name, p.yearsExp ORDER BY p.yearsExp DESC;
Query2 结果:
Rows: 4
+--------------+-----------------+
| p.name | p.yearsExp |
+--------------+-----------------+
| 'Dan' | 6 |
| 'Jennifer' | 5 |
| 'John' | 5 |
| 'Sally' | 4 |
+--------------+-----------------+
请注意,第一个查询必须先按人员姓名排序,然后才能将值收集到列表中。如果您不先排序(将 ORDER BY
放在 RETURN
子句之后),您将根据列表的大小而不是列表中值的首字母进行排序。结果也被排序为两个值:技术,然后是人。这允许您对技术进行排序,以便将喜欢某项技术的所有人员列在一起。
您可以通过运行以下查询来尝试按两个值或一个值进行排序的差异:
//only sorted by person's name in alphabetical order
WITH ['Graphs','Query Languages'] AS techRequirements
UNWIND techRequirements AS technology
MATCH (p:Person)-[r:LIKES]-(t:Technology {type: technology})
WITH t.type AS technology, p.name AS personName
ORDER BY personName
RETURN technology, personName;
//only sorted by technology (person names are out of order)
WITH ['Graphs','Query Languages'] AS techRequirements
UNWIND techRequirements AS technology
MATCH (p:Person)-[r:LIKES]-(t:Technology {type: technology})
WITH t.type AS technology, p.name AS personName
ORDER BY technology
RETURN technology, personName;
//sorted by technology, then by person's name
WITH ['Graphs','Query Languages'] AS techRequirements
UNWIND techRequirements AS technology
MATCH (p:Person)-[r:LIKES]-(t:Technology {type: technology})
WITH t.type AS technology, p.name AS personName
ORDER BY technology, personName
RETURN technology, personName;
计算列表中的值
如果您有一个值列表,您还可以使用 size()
函数查找该列表中的项目数或计算表达式的大小。以下示例返回找到的项目数。
//Query1: find number of items in collected list
MATCH (p:Person)-[:IS_FRIENDS_WITH]->(friend:Person)
RETURN p.name, size(collect(friend.name)) AS numberOfFriends;
Query1 结果:
Rows: 4
+--------------+-----------------+
| p.name | numberOfFriends |
+--------------+-----------------+
| 'John' | 1 |
| 'Jennifer' | 5 |
| 'Ann' | 1 |
| 'Joe' | 2 |
+--------------+-----------------+
在 Neo4j v5 中,如果您需要查找关系模式的数量,请使用 COUNT {}
表达式。请看以下 Cypher 查询示例。
//Query2: find number of friends who have other friends
MATCH (p:Person)-[:IS_FRIENDS_WITH]->(friend:Person)
WHERE count{(friend)-[:IS_FRIENDS_WITH]-(:Person)} > 1
RETURN p.name, collect(friend.name) AS friends, count{(friend)-[:IS_FRIENDS_WITH]-(:Person)} AS numberOfFoFs;
Query2 结果:
Rows: 3
+--------------+----------------------------------+---------------+
| p.name | friends | numberOfFoFs |
+--------------+----------------------------------+---------------+
| 'Joe' | ['Mark'] | 2 |
| 'Jennifer' | ['Mark', 'John', 'Sally', 'Ann'] | 2 |
| 'John' | ['Sally'] | 2 |
+--------------+----------------------------------+---------------+
资源
- Neo4j Cypher 手册:WITH、UNWIND 等
- Neo4j Cypher 手册:聚合
- Neo4j Cypher 手册:size()