顺序查询 (NEXT)仅限 Cypher 25Neo4j 2025.06 引入
NEXT 允许将查询线性组合成一系列更小、独立的片段,并将整个中间结果表从一个片段传递到下一个片段。
NEXT 具有以下优势:
-
NEXT可以提高复杂查询的模块化程度和可读性。 -
NEXT可用于替代 WITH 子句来构建复杂查询。 -
NEXT可以提高 条件查询 (WHEN) 的可用性。 -
NEXT允许将完整的中间结果表传递到UNION的各个分支中。
示例图
本页示例中使用以下图数据库结构
要重建该图,请对空的 Neo4j 数据库运行以下查询。
CREATE (techCorp:Supplier {name: 'TechCorp', email: 'contact@techcorp.com'}),
(foodies:Supplier {name: 'Foodies Inc.', email: 'info@foodies.com'}),
(laptop:Product {name: 'Laptop', price: 1000}),
(phone:Product {name: 'Phone', price: 500}),
(headphones:Product {name: 'Headphones', price: 250}),
(chocolate:Product {name: 'Chocolate', price: 5}),
(coffee:Product {name: 'Coffee', price: 10}),
(amir:Customer {firstName: 'Amir', lastName: 'Rahman', email: 'amir.rahman@example.com', discount: 0.1}),
(keisha:Customer {firstName: 'Keisha', lastName: 'Nguyen', email: 'keisha.nguyen@example.com', discount: 0.2}),
(mateo:Customer {firstName: 'Mateo', lastName: 'Ortega', email: 'mateo.ortega@example.com', discount: 0.05}),
(hannah:Customer {firstName: 'Hannah', lastName: 'Connor', email: 'hannah.connor@example.com', discount: 0.15}),
(leila:Customer {firstName: 'Leila', lastName: 'Haddad', email: 'leila.haddad@example.com', discount: 0.1}),
(niko:Customer {firstName: 'Niko', lastName: 'Petrov', email: 'niko.petrov@example.com', discount: 0.25}),
(yusuf:Customer {firstName: 'Yusuf', lastName: 'Abdi', email: 'yusuf.abdi@example.com', discount: 0.1}),
(amir)-[:BUYS {date: date('2024-10-09')}]->(laptop),
(amir)-[:BUYS {date: date('2025-01-10')}]->(chocolate),
(keisha)-[:BUYS {date: date('2023-07-09')}]->(headphones),
(mateo)-[:BUYS {date: date('2025-03-05')}]->(chocolate),
(mateo)-[:BUYS {date: date('2025-03-05')}]->(coffee),
(mateo)-[:BUYS {date: date('2024-04-11')}]->(laptop),
(hannah)-[:BUYS {date: date('2023-12-11')}]->(coffee),
(hannah)-[:BUYS {date: date('2024-06-02')}]->(headphones),
(leila)-[:BUYS {date: date('2023-05-17')}]->(laptop),
(niko)-[:BUYS {date: date('2025-02-27')}]->(phone),
(niko)-[:BUYS {date: date('2024-08-23')}]->(headphones),
(niko)-[:BUYS {date: date('2024-12-24')}]->(coffee),
(yusuf)-[:BUYS {date: date('2024-12-24')}]->(chocolate),
(yusuf)-[:BUYS {date: date('2025-01-02')}]->(laptop),
(techCorp)-[:SUPPLIES]->(laptop),
(techCorp)-[:SUPPLIES]->(phone),
(techCorp)-[:SUPPLIES]->(headphones),
(foodies)-[:SUPPLIES]->(chocolate),
(foodies)-[:SUPPLIES]->(coffee)
将值传递给后续查询
NEXT 将一个查询的结果表传递给后续查询。在以下示例中,NEXT 将变量 customer 传递给第二个查询
NEXT 将变量传递给另一个查询MATCH (c:Customer)
RETURN c AS customer
NEXT
MATCH (customer)-[:BUYS]->(:Product {name: 'Chocolate'})
RETURN customer.firstName AS chocolateCustomer
| chocolateCustomer |
|---|
|
|
|
行:3 |
NEXT 将多个变量传递给另一个查询MATCH (c:Customer)-[:BUYS]->(p:Product {name: 'Chocolate'})
RETURN c AS customer, p AS product
NEXT
RETURN customer.firstName AS chocolateCustomer,
product.price * (1 - customer.discount) AS chocolatePrice
| chocolateCustomer | chocolatePrice |
|---|---|
|
|
|
|
|
|
行:3 |
|
|
当后接 |
在 NEXT 之前的查询中属于局部变量且未显式作为该查询结果返回的变量,在 NEXT 的上下文中后续查询无法访问。这允许您像使用 WITH 一样控制变量作用域,请参阅 控制作用域中的变量。
NEXT 之后的聚合
NEXT 将整个结果表传递给后续查询。这在聚合值时特别有用。
在以下示例中,NEXT 将变量 customer 传递给第二个查询
NEXT 之后的聚合MATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c AS customer, p AS product
NEXT
RETURN product.name AS product,
COUNT(customer) AS numberOfCustomers
| product | numberOfCustomers |
|---|---|
|
|
|
|
|
|
|
|
|
|
行:5 |
|
与 UNION 查询的交互
在 NEXT 之后使用 UNION
如果 UNION 查询位于 NEXT 之后,则完整的中间结果表会传递到 UNION 查询的所有分支中。
NEXT 之后的 UNIONMATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c, p
NEXT
RETURN c.firstName AS name, COLLECT(p.price * (1 - c.discount)) AS purchases, "discounted price" AS type
UNION
RETURN c.firstName AS name, COLLECT(p.price) AS purchases, "real price" AS type
NEXT
RETURN * ORDER BY name, type
| 名称 (name) | purchases | type |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行数: 14 |
||
在 NEXT 之前使用 UNION
如果 UNION 查询位于 NEXT 之前,则 UNION 的完整结果会传递给后续查询。
NEXT 之前的 UNIONMATCH (c:Customer)-[:BUYS]->(:Product{name: "Laptop"})
RETURN c.firstName AS customer
UNION ALL
MATCH (c:Customer)-[:BUYS]-> (:Product{name: "Coffee"})
RETURN c.firstName AS customer
NEXT
RETURN customer AS customer, count(customer) as numberOfProducts
| customer | numberOfProducts |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
行数: 6 |
|
在此示例中,来自第一个片段的客户名称列表中包含“Mateo”的重复条目,因为他同时购买了笔记本电脑和咖啡。使用 UNION ALL 将他两次添加到列表中。第二个片段可以访问该列表,因为 UNION 的两部分都返回了别名为 customer 的列表的一部分。通过使用 count(),该列表在查询的 RETURN 部分对重复项进行了聚合。
使用 {} 在 UNION 中使用 NEXT
如果 UNION 查询的任何块中包含 NEXT,则必须用 {} 包裹该块。
UNION 内部的 NEXT{
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Chocolate'})
RETURN c AS customer
NEXT
RETURN customer.firstName AS plantCustomer
}
UNION ALL
{
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Coffee'})
RETURN c AS customer
NEXT
RETURN customer.firstName AS plantCustomer
}
| plantCustomer |
|---|
|
|
|
|
|
|
行数: 6 |
与 CALL 子查询的交互
CALL 子查询将中间结果表逐行传递给子查询,而 NEXT 则整体传递表。如果 NEXT 被包装在 CALL 子查询中,则前一个查询的结果将一次一行地传递给 NEXT。这可用于计算分组中更复杂的聚合。
CALL 内部的 NEXTMATCH (p:Product) WHERE p.name <> "Coffee"
CALL (p) {
MATCH (p)<-[:BUYS]-(c:Customer)-[:BUYS]->(otherProduct)
RETURN c, otherProduct
NEXT
RETURN count(DISTINCT c) AS customers, 0 AS customersAlsoBuyingCoffee
UNION
FILTER otherProduct.name = "Coffee"
RETURN 0 as customers, count(DISTINCT c) AS customersAlsoBuyingCoffee
NEXT
RETURN max(customers) AS customers, max(customersAlsoBuyingCoffee) AS customersAlsoBuyingCoffee
}
RETURN p.name AS product,
round(toFloat(customersAlsoBuyingCoffee) * 100 / customers, 1) AS percentageOfCustomersAlsoBuyingCoffee
ORDER BY product
| product | percentageOfCustomersAlsoBuyingCoffee |
|---|---|
|
|
|
|
|
|
|
|
行:5 |
|
此示例计算对于每个非咖啡产品,同时购买了咖啡的客户百分比。对于每个产品 p,子查询会查找产品 p 的客户 c 与该客户也购买过的另一个产品 otherProduct 的所有组合。第一个 NEXT 将这些组合作为一个整体传递给 UNION,以便查询可以
-
计算 union 第一个分支中的所有客户。
-
计算 union 第二个分支中也购买了咖啡的客户。
UNION 产生两行——每个分支一行。第二个 NEXT 将这两行作为一个整体传递给一个查询,该查询将它们聚合成一行,这就是 CALL 子查询的结果。
|
|
与条件查询的交互
在 NEXT 之前或之后使用条件查询
条件查询的处理方式与 CALL 类似,即逐行处理传入的中间结果表。位于 NEXT 之后的条件查询等同于包装在 CALL 子查询中的条件查询。
NEXT 内部的条件查询MATCH (c:Customer)-[:BUYS]->(:Product)<-[:SUPPLIES]-(s:Supplier)
RETURN c.firstName AS customer, s.name AS supplier
NEXT
WHEN supplier = "TechCorp" THEN
RETURN customer, "Tech enjoyer" AS personality
WHEN supplier = "Foodies Inc." THEN
RETURN customer, "Tropical plant enjoyer" AS personality
NEXT
RETURN customer, collect(DISTINCT personality) AS personalities
NEXT
WHEN size(personalities) > 1 THEN
RETURN customer, "Enjoyer of tech and plants" AS personality
ELSE
RETURN customer, personalities[0] AS personality
| customer | personality |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:7 |
|
在上面的查询中,根据客户购买的产品为其分配个性类型。第二个片段是一个条件查询,它根据客户购买的供应商返回不同的基础个性类型。第三个片段聚合个性类型。最后,第四个片段是另一个条件查询,如果存在多个基础个性类型,它会将它们合并为一种新的个性。
使用 {} 在条件查询中使用 NEXT
如果条件查询的任何 THEN 或 ELSE 块中有 NEXT,则必须用 {} 包裹 THEN 或 ELSE 之后的部分。
NEXTMATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c AS customer, sum(p.price) AS sum
NEXT
WHEN sum >= 1000 THEN {
RETURN customer.firstName AS customer, "club 1000 plus" AS customerType, sum AS sum
}
ELSE {
RETURN sum * (1 - customer.discount) AS finalSum
NEXT
RETURN customer.firstName AS customer, "club below 1000" AS customerType, finalSum AS sum
}
| customer | customerType | sum |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:3 |
||
上面的查询计算每个客户购买的产品总价,然后仅对低于 1000 的总额应用客户折扣。