获取正确的结果

示例图

在本节中,使用了两个示例数据集。第一个图基于电影数据库。以下代码块可帮助您创建用于探索 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)

这是生成的图

cypher intro results01 arr

第二个数据集是一个小型人员、他们工作的公司以及他们喜欢的技术的网络。您可以在下一章中找到其图像。

过滤结果

到目前为止,您已在图中匹配模式并始终返回找到的所有结果。现在让我们看看过滤结果并仅返回您感兴趣的数据子集的选项。这些过滤条件使用WHERE子句表达。此子句允许使用任意数量的布尔表达式(谓词),并结合使用ANDORXORNOT。最简单的谓词是比较;尤其是相等性。

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),查询结果必须满足以下一个要求

  • 该人的name以字母'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)

假设您想知道谁拥有 3 到 7 年的经验。以下代码块显示了此情况下的 Cypher 查询。

MATCH (p:Person)
WHERE 3 <= p.yearsExperience <= 7
RETURN p

测试属性是否存在

您可能只对节点或关系上是否存在属性感兴趣。例如,您可能希望检查系统中的哪些客户拥有 Twitter 句柄,以便显示相关内容。或者,您可以检查所有员工是否都具有开始日期属性,以验证哪些实体可能需要更新。

请记住:在 Neo4j 中,只有当属性具有值时,它才存在(存储)。不会存储null属性。这确保了仅为您的节点和关系保留有价值的必要信息。

要在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;

查询 1 结果

Rows: 4

+------------------------+
| p.name                 |
+------------------------+
| 'Melissa'              |
| 'Dan'                  |
| 'Jennifer'             |
| 'Mark'                 |
+---------- -------------+
查询 2 结果

cypher filter exists relProp arr

检查字符串——部分值、模糊搜索

某些场景需要查询语法,该语法匹配字符串中的部分值或广泛类别。要执行此类查询,您需要一些灵活性和字符串匹配和搜索选项。无论您是在查找以特定字符串开头、结尾或包含特定值的字符串,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;

查询 1 结果: cypher filter exists ptrn arr

查询 2 结果

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;

查询 1 结果

Rows: 3

+-----------------------+
| p.name                |
+-----------------------+
| 'Diana'               |
| 'Mark'                |
| 'Melissa'             |
+-----------------------+

查询 2 结果

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

查询结果

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 BYLIMIT行,然后分别添加每一行来混合查询。请注意,仅删除ORDER BY行会从列表中提取前三个值,从而获得返回结果的随机样本。

聚合信息

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

计算电影数据库中的人数可以通过以下方式实现

MATCH (:Person)
RETURN count(*) AS people
Rows: 1

+--------+
| people |
+--------+
| 3      |
+--------+

如果要跳过null值,请使用函数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()函数

  1. count(*):计算结果并返回匹配行的数量。

  2. count(n):计算n出现的次数(排除null值)。您可以在括号内指定节点、关系或属性,以便 Cypher 进行计数。

  3. count(DISTINCT variable)DISTINCT运算符会从结果中删除重复项。

人员、技术和公司数据集上,一些Person节点具有 Twitter 句柄,但其他节点没有。如果运行下面的第一个示例查询,您将看到twitter属性对四个人有值,对其他五个人为null。第二个和第三个查询显示了如何使用不同的count选项。

//Query1: see the list of Twitter handle values for Person nodes
MATCH (p:Person)
RETURN p.twitter;

查询 1 结果

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

查询 2 结果

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 对象,以及日常数组和其他类型的列表。让我们看几个示例,其中我们假设某人喜欢的技术也意味着他们对每项技术都有一些经验。如果您有兴趣聘用熟悉GraphsQuery Languages的人员,您可以编写以下查询以查找要面试的人员。

//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;

查询 1 结果

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;

查询 2 结果

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;

查询 1 结果

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;

查询 2 结果

Rows: 4

+--------------+-----------------+
| p.name       | p.yearsExp      |
+--------------+-----------------+
| 'Dan'        | 6               |
| 'Jennifer'   | 5               |
| 'John'       | 5               |
| 'Sally'      | 4               |
+--------------+-----------------+

请注意,第一个查询必须先按Person名称排序,然后再将值收集到列表中。如果您不先排序(将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;

查询 1 结果

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;

查询 2 结果

Rows: 3

+--------------+----------------------------------+---------------+
| p.name       | friends                          | numberOfFofs  |
+--------------+----------------------------------+---------------+
| 'Joe'        | ['Mark']                         | 2             |
| 'Jennifer'   | ['Mark', 'John', 'Sally', 'Ann'] | 2             |
| 'John'       | ['Sally']                        | 2             |
+--------------+----------------------------------+---------------+