WITH
示例图
以下示例使用了具有以下模式的图
要重建该图,请对空的 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)
创建新变量
WITH 可以与 AS 关键字结合使用,以绑定可传递给后续子句的新变量。
WITH [1, 2, 3] AS list
RETURN list
| list |
|---|
|
行:1 |
在下面的示例中,WITH 子句将所有匹配到的 Customer 节点绑定到一个新变量 customers。绑定的节点是 MAP 值,之后可以通过该新变量进行引用。
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Chocolate'})
WITH c AS customers
RETURN customers.firstName AS chocolateCustomers
| chocolateCustomers |
|---|
|
|
|
行:3 |
控制作用域中的变量
WITH 可用于控制哪些变量保留在查询的作用域内。任何被 WITH 子句引用的变量都会保留在查询作用域内,并可供后续子句使用。如果变量在 WITH 子句中被重命名,则后续子句只能通过新名称引用它。如果变量未在 WITH 子句中显式引用,它将从查询作用域中移除,且后续子句无法再引用它。若要保留作用域内的所有变量,请使用 WITH *。
在下面的查询中,WITH 子句移除了 p 变量的作用域。因此,后续的 RETURN 子句将无法使用它。同样,c 变量也无法使用——由于之前的 WITH 子句,仅有 chocolateCustomers 可用。
MATCH (c:Customer)-[:BUYS]->(p:Product {name: 'Chocolate'})
WITH c.name AS chocolateCustomers
RETURN chocolateCustomers,
p.price AS chocolatePrice
42N62: 错误: 语法错误或访问规则冲突 - 变量未定义。变量 42001:错误:语法错误或访问规则冲突 - 无效语法 |
WITH * 保留所有变量MATCH (supplier:Supplier)-[r]->(product:Product)
WITH *
RETURN supplier.name AS company,
type(r) AS relType,
product.name AS product
| company | relType | product |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:5 |
||
WITH 不能移除导入到 CALL 子查询 中的变量的作用域,因为导入到子查询中的变量在其内部作用域中被视为全局变量。更具体地说,导入 CALL 子查询的变量即使在前面的 WITH 子句中未被引用,也可供后续子句使用。
在下面的示例中,x 变量被导入到 CALL 子查询的内部作用域中,即使前面的 WITH 子句忽略了它,RETURN 子句依然可以成功引用它。
WITH 11 AS x
CALL (x) {
UNWIND [2, 3] AS y
WITH y
RETURN x*y AS a
}
RETURN x, a
| x | a |
|---|---|
|
|
|
|
行:2 |
|
更多信息,请参阅 CALL 子查询 → 导入变量。
将值绑定到变量
WITH 可用于将表达式的值赋给变量。在下面的查询中,STRING 连接表达式的值被绑定到新变量 customerFullName,表达式 chocolate.price * (1 - customer.discount) 的值被绑定到 chocolateNetPrice,这两个变量随后都可在 RETURN 子句中使用。
MATCH (customer:Customer)-[:BUYS]->(chocolate:Product {name: 'Chocolate'})
WITH customer.firstName || ' ' || customer.lastName AS customerFullName,
chocolate.price * (1 - customer.discount) AS chocolateNetPrice
RETURN customerFullName,
chocolateNetPrice
| customerFullName | chocolateNetPrice |
|---|---|
|
|
|
|
|
|
行:3 |
|
由于 WITH 可用于将变量赋值给表达式的值,因此它可用于链式表达式。
WITH 进行链式表达式MATCH (p:Product)
WITH p, p.price >= 500 AS isExpensive
WITH p, isExpensive, NOT isExpensive AS isAffordable
WITH p, isExpensive, isAffordable,
CASE
WHEN isExpensive THEN 'High-end'
ELSE 'Budget'
END AS discountCategory
RETURN p.name AS product,
p.price AS price,
isAffordable,
discountCategory
ORDER BY price
| product | 价格 | isAffordable | discountCategory |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:5 |
|||
LET 子句也可用于给变量赋值和链式表达式,且比 WITH 更清晰简洁。更多信息,请参阅 LET → 链式表达式。 |
聚合
WITH 子句可以执行聚合并将结果绑定到新变量。在此示例中,使用 sum() 函数计算每个客户的总消费额,并将每个结果绑定到新变量 totalSpent。使用 collect() 函数将每个产品收集到绑定到 productsBought 变量的 LIST 值中。
WITH 执行聚合MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH c.firstName AS customer,
sum(p.price) AS totalSpent,
collect(p.name) AS productsBought
RETURN customer,
totalSpent,
productsBought
ORDER BY totalSpent DESC
| customer | totalSpent | productsBought |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:7 |
||
移除重复值
如果使用 DISTINCT 修饰符,WITH 可用于从结果集中移除重复值。
在下面的查询中,WITH DISTINCT 用于从 Customer 节点中移除重复的 discount 属性值。
WITH DISTINCT 移除重复值MATCH (c:Customer)
WITH DISTINCT c.discount AS discountRates
RETURN discountRates
ORDER BY discountRates
| discountRates |
|---|
|
|
|
|
|
行:5 |
显式投影值仅限 Cypher 25Neo4j 2025.06 引入
WITH ALL 可用于显式投影绑定到变量的所有值。其功能与使用普通的 WITH 相同。
WITH ALL 进行显式结果投影MATCH (c:Customer)
WITH ALL c.discount AS discountRates
RETURN discountRates
ORDER BY discountRates
| discountRates |
|---|
|
|
|
|
|
|
|
行:7 |
排序与分页
如果与 ORDER BY、LIMIT 和 SKIP 子句配合使用,WITH 可以对结果进行排序和分页。在这种情况下,这些子句应理解为 WITH 执行的结果操作的一部分——而不是独立的子句——在将结果传递给后续子句之前进行处理。
在下面的查询中,结果先使用 ORDER BY 按 Customer 的总消费额降序排列,然后再传递给最后的 RETURN 子句。
ORDER BY 对结果进行排序MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH c,
sum(p.price) AS totalSpent
ORDER BY totalSpent DESC
RETURN c.firstName AS customer, totalSpent
| customer | totalSpent |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:7 |
|
在下一个示例中,LIMIT 用于在排序后仅保留 totalSpent 值最高的 3 位客户。然后,SET 将一个新属性 (topSpender = true) 分配给消费最高的那些客户。
LIMIT 限制结果MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH c,
sum(p.price) AS totalSpent
ORDER BY totalSpent DESC
LIMIT 3
SET c.topSpender = true
RETURN c.firstName AS customer,
totalSpent,
c.topSpender AS topSpender
| customer | totalSpent | topSpender |
|---|---|---|
|
|
|
|
|
|
|
|
|
行:3 |
||
SKIP 可在 WITH 子句之后使用,以从结果集中丢弃行。下面,SKIP 排除了已排序结果集中的前 3 行(即 totalSpent 值最高的 3 个 Customer 节点),并将 false 值分配给剩余 Customer 节点的新 topSpender 属性。
SKIP 排除结果MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH c,
sum(p.price) AS totalSpent
ORDER BY totalSpent DESC
SKIP 3
SET c.topSpender = false
RETURN c.firstName AS customer,
totalSpent,
c.topSpender AS topSpender
| customer | totalSpent | topSpender |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
行:4 |
||
ORDER BY、LIMIT 和 SKIP 也可在 WITH 子句之后使用,以在继续后续模式匹配之前缩小行集。在下面的查询中,首先匹配由 Foodies Inc. 供应的所有产品。WITH 将这些产品向前传递,ORDER BY 按 price 降序排序,LIMIT 仅保留最昂贵的那一个。第二个 MATCH 子句随后仅从该单一产品进行匹配,以找到所有购买过该产品的客户。
MATCH (:Supplier {name: 'Foodies Inc.'})-[:SUPPLIES]->(p:Product)
WITH p
ORDER BY p.price DESC
LIMIT 1
MATCH (p)<-[:BUYS]-(c:Customer)
RETURN p.name AS product,
p.price AS price,
collect(c.firstName) AS customers
| product | 价格 | customers |
|---|---|---|
|
|
|
行:1 |
||
过滤结果
WITH 之后可以紧跟 WHERE 子句来过滤结果。类似于用于排序和分页的子句,WHERE 应理解为 WITH 执行的结果操作的一部分——而不是独立子句——在将结果传递给后续子句之前进行处理。更多信息,请参阅 在 WITH 之后使用 WHERE。
WITH 和 WHERE 进行过滤UNWIND [1, 2, 3, 4, 5, 6] AS x
WITH x
WHERE x > 2
RETURN x
| x |
|---|
|
|
|
|
行:4 |
在下面的查询中,WITH 和 WHERE 用于过滤掉 totalSales 小于 1000 的任何 Supplier 节点。注意在 collect() 中使用 DISTINCT 来移除重复的 Customer 节点。
WITH 和 WHERE 过滤属性值MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)<-[:BUYS]-(c:Customer)
WITH s,
sum(p.price) AS totalSales,
count(DISTINCT c) AS uniqueCustomers
WHERE totalSales > 1000
RETURN s.name AS supplier,
totalSales,
uniqueCustomers
| supplier | totalSales | uniqueCustomers |
|---|---|---|
|
|
|
行:1 |
||
FILTER 子句可作为 WITH * WHERE <predicate> 结构的更简洁替代方案。更多信息,请参阅 FILTER 作为 WITH * WHERE 的替代方案。 |