WITH

示例图

以下示例使用具有以下模式的图

with clause

要重新创建图,请针对空的 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
结果
列表

[1, 2, 3]

行数:1

在下面的示例中,WITH 子句将所有匹配的 Customer 节点绑定到一个新变量 customers。绑定的节点是 MAP 值,可以从新变量中引用。

创建绑定到匹配节点的新变量
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Chocolate'})
WITH c AS customers
RETURN customers.firstName AS chocolateCustomers
结果
chocolateCustomers

"Amir"

"Mateo"

"Yusuf"

行数: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
错误消息
Variable `p` not defined
使用 WITH * 保留所有变量
MATCH (supplier:Supplier)-[r]->(product:Product)
WITH *
RETURN supplier.name AS company,
       type(r) AS relType,
       product.name AS product
结果
company relType product

"TechCorp"

"SUPPLIES"

"Laptop"

"TechCorp"

"SUPPLIES"

"Phone"

"TechCorp"

"SUPPLIES"

"Headphones"

"Foodies Inc."

"SUPPLIES"

"Chocolate"

"Foodies Inc."

"SUPPLIES"

"Coffee"

行数:5

WITH 无法取消导入到 CALL 子查询 的变量的作用域,因为导入到子查询的变量被认为是其内部作用域的全局变量。更具体地说,即使前面的 WITH 子句未引用它,导入到 CALL 子查询中的变量也将可供后续子句使用。

在下面的示例中,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

11

22

11

33

行数: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

"Amir Rahman"

4.5

"Mateo Ortega"

4.75

"Yusuf Abdi"

4.5

行数: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 price isAffordable discountCategory

"Chocolate"

5

true

'Budget'

"Coffee"

10

true

'Budget'

"Headphones"

250

true

'Budget'

"Phone"

500

false

'High-end'

"Laptop"

1000

false

'High-end'

行数:5

聚合

WITH 子句可以执行聚合并将结果绑定到新变量。在此示例中,sum() 函数用于计算每个客户的总支出,每个值都绑定到新变量 totalSpentcollect() 函数用于将每个产品收集到绑定到 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

"Mateo"

1015

["Laptop", "Chocolate", "Coffee"]

"Amir"

1005

["Laptop", "Chocolate"]

"Yusuf"

1005

["Laptop", "Chocolate"]

"Leila"

1000

["Laptop"]

"Niko"

760

["Phone", "Headphones", "Coffee"]

"Hannah"

260

["Headphones", "Coffee"]

"Keisha"

250

["Headphones"]

行数:7

删除重复值

如果 WITH 附加修饰符 DISTINCT,则可用于从结果集中删除重复值。

在下面的查询中,WITH DISTINCT 用于从 Customer 节点中删除任何重复的 discount 属性值。

使用 WITH DISTINCT 删除重复值
MATCH (c:Customer)
WITH DISTINCT c.discount AS discountRates
RETURN discountRates
ORDER BY discountRates
结果
discountRates

0.05

0.1

0.15

0.2

0.25

行数:5

排序和分页

WITH 可以与 ORDER BYLIMITSKIP 子句一起使用,以对结果进行排序和分页。如果这样使用,这些子句应被理解为 WITH 执行的结果操作的一部分——而不是独立的子句——在结果传递给后续子句之前。

在下面的查询中,结果首先使用 ORDER BYCustomer 消费最多的降序排序,然后才传递给最终的 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

"Mateo"

1015

"Amir"

1005

"Yusuf"

1005

"Leila"

1000

"Niko"

760

"Hannah"

260

"Keisha"

250

行数: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

"Mateo"

1015

true

"Amir"

1005

true

"Yusuf"

1005

true

行数:3

SKIP 可以在 WITH 子句之后使用,以从结果集中丢弃行。下面,SKIP 排除排序结果集中的前 3 行(即 totalSpent 值最高的 3 个 Customer 节点),并为剩余 Customer 节点的新 topSpender 属性分配一个 false 值。

使用 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

"Leila"

1000

false

"Niko"

760

false

"Hannah"

260

false

"Keisha"

250

false

行数:4

ORDER BYLIMITSKIP 也可以在 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 price customers

"Coffee"

10

["Mateo", "Hannah", "Niko"]

行数:1

过滤结果

WITH 后面可以跟 WHERE 子句来过滤结果。与用于排序和分页的子句类似,WHERE 应被理解为 WITH 执行的结果操作的一部分——而不是独立的子句——在结果传递给后续子句之前。

使用 WITHWHERE 过滤
UNWIND [1, 2, 3, 4, 5, 6] AS x
WITH x
  WHERE x > 2
RETURN x
x

3

4

5

6

行数:4

在下面的查询中,WITHWHERE 用于过滤掉任何 totalSales 小于 1000Supplier 节点。请注意在 collect() 中使用 DISTINCT 来删除任何重复的 Customer 节点。

使用 WITHWHERE 过滤属性值
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

"TechCorp"

5250

7

行数:1

组合写入和读取子句

如果写入子句后跟读取子句,则必须使用 WITH 作为两者之间的分隔符。

WITH 用作写入子句和读取子句之间的分隔符
MATCH (yusuf:Customer {firstName: 'Yusuf'}),
      (coffee:Product {name: 'Coffee'}),
      (headphones:Product {name: 'Headphones'})
CREATE (yusuf)-[:BUYS {date: date('2025-04-15')}]->(coffee),
       (yusuf)-[:BUYS {date: date('2025-04-15')}]->(headphones)
WITH yusuf
MATCH (yusuf)-[r:BUYS]->(p:Product)
RETURN collect(p.name) AS yusufPurchases,
       r.date AS date
ORDER BY date DESC
yusufPurchases date

["Coffee", "Headphones"]

2025-04-15

["Laptop"]

2025-01-02

["Chocolate"]

2024-12-24

行数:3

© . All rights reserved.