LET

LET 将表达式绑定到变量。对于涉及多个链式表达式的查询,它是一种比 WITH 更简洁且易读的替代方案。与 WITH 不同,LET 不会从后续子句的作用域中删除变量。它也不能用于聚合或与 DISTINCT 结合使用;它只能用于绑定新变量。

示例图

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

要重建该图,请对空的 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)

将值绑定到变量

LET 用于将变量绑定到表达式的结果。

语法
LET variable = expression, variable = expression
使用 LET 绑定变量
MATCH (c:Customer)
LET fullName = c.firstName + ' ' + c.lastName
RETURN fullName
结果
fullName

"Amir Rahman"

"Keisha Nguyen"

"Mateo Ortega"

"Hannah Connor"

"Leila Haddad"

"Niko Petrov"

"Yusuf Abdi"

行:7

使用 LET 绑定多个变量
MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
LET supplier = s.name, product = p.name
RETURN supplier, product
结果
supplier product

"TechCorp"

"Laptop"

"TechCorp"

"Phone"

"TechCorp"

"Headphones"

"Foodies Inc."

"Chocolate"

"Foodies Inc."

"Coffee"

行:4

LETWITH 的区别

LETWITH 之间存在重要差异,可以分为以下几类:

作用域中的变量

LET 不会从后续子句的作用域中删除变量,而 WITH 会。因此,LET <variable> = <expression>WITH *, <expression> AS <variable> 的替代品,而不是 WITH <expression> AS <variable>(后者会删除前一个子句中存在但未在 <expression> 中引用的任何变量)。

示例 1. 作用域中的变量:比较 LETWITH

任何未被 WITH 显式引用(或通过 WITH * 携带)的变量都会从后续子句的作用域中删除。

不允许的操作 —— 引用被 WITH 删除的变量
MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
WITH s.name AS supplier
RETURN supplier, p.name AS product
GQLSTATUS 错误链

42N62: 错误: 语法错误或访问规则冲突 - 变量未定义。变量 p 未定义。

42001:错误:语法错误或访问规则冲突 - 无效语法

然而,LET 无法控制哪些变量保留在作用域中。因此,在上述查询中将 WITH 替换为 LET 将会返回结果。

LET 不会删除变量
MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
LET supplier =  s.name
RETURN supplier, p.name AS product
结果
supplier product

"TechCorp"

"Laptop"

"TechCorp"

"Phone"

"TechCorp"

"Headphones"

"Foodies Inc."

"Chocolate"

"Foodies Inc."

"Coffee"

行:5

链式表达式

LET 不会删除变量这一事实意味着它可用于以清晰简洁的方式链接表达式,其中在一个 LET 子句中绑定的变量可以在后续子句中被引用。

示例 2. 链式表达式:比较 LETWITH

下面的查询显示,由 LET 子句绑定的变量可以在后续子句中引用,而无需显式携带。具体来说,变量 isExpensive 在第一个 LET 子句中创建,并在后续子句中再次引用。还需要注意,在 MATCH 子句中绑定的变量 p,尽管未在任何 LET 子句中引用,但在最终的 RETURN 子句中依然可用。

LET 引用先前 LET 中分配的变量
MATCH (p:Product)
LET isExpensive = p.price >= 500
LET isAffordable = NOT isExpensive
LET discountCategory = CASE
    WHEN isExpensive THEN 'High-end'
    ELSE 'Budget'
END
RETURN p.name AS product, p.price AS price, isAffordable, discountCategory
ORDER BY price
结果
product 价格 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 时,相同的查询会变得不那么简洁,因为 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

聚合和 DISTINCT

WITH 不同,LET 不能执行聚合,也不能与 DISTINCT 结合使用。例如,在以下查询中,WITH 不能被 LET 替换

结合 WITH DISTINCT 和表达式上的聚合
MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH DISTINCT c, sum(p.price) AS totalSpent
RETURN c.firstName AS customer, totalSpent
结果
customer totalSpent

"Amir"

1005

"Keisha"

250

"Mateo"

1015

"Hannah"

260

"Leila"

1000

"Niko"

760

"Yusuf"

1005

行:7

结合 WITHLET
MATCH (c:Customer)-[:BUYS]->(p:Product)
WITH DISTINCT c, sum(p.price) AS totalSpent
LET fullName = c.firstName + ' ' + c.lastName
RETURN fullName, totalSpent
结果
customer totalSpent

"Amir Rahman"

1005

"Keisha Nguyen"

250

"Mateo Ortega"

1015

"Hannah Connor"

260

"Leila Haddad"

1000

"Niko Petrov"

760

"Yusuf Abdi"

1005

行:7

高级示例

以下示例演示了 LET 及其链接表达式的能力如何用于更高级的查询中

示例 3. 场景 1:关于客户购买和折扣的供应商通知

此示例检索有关 Customer(客户)购买了什么 Product(产品)以及从哪个 Supplier(供应商)处购买的信息。然后,它计算应用 discount(折扣)后的 price(价格),并为每次购买构建一条 message(消息),其中包括每个 CustomerfullName(全名)和应用折扣后产品的 effectivePrice(有效价格),并发送到 Supplieremail(邮箱)。

此示例强调了 LET 不会删除变量。在 MATCH 和后续 LET 子句中引入的所有变量在最终的 RETURN 子句中均可用。

检索 Customer 购买详情,包括折扣信息
MATCH (c:Customer)-[b:BUYS]->(p:Product)<--(s:Supplier)
LET fullname = c.firstName + ' ' + c.lastName,
    effectivePrice = p.price * (1 - c.discount)
LET message = fullname + " bought " + p.name + " for $" + effectivePrice + " after a " + (c.discount * 100) + "% discount"
RETURN b.date AS date, message, s.email AS toSupplier
ORDER BY date
结果
date message toSupplier

2023-05-17

"Leila Haddad 以 10.0% 的折扣购买了 Laptop,价格为 $900.0"

"contact@techcorp.com"

2023-07-09

"Keisha Nguyen 以 20.0% 的折扣购买了 Headphones,价格为 $200.0"

"contact@techcorp.com"

2023-12-11

"Hannah Connor 以 15.0% 的折扣购买了 Coffee,价格为 $8.5"

"info@foodies.com"

2024-04-11

"Mateo Ortega 以 5.0% 的折扣购买了 Laptop,价格为 $950.0"

"contact@techcorp.com"

2024-06-02

"Hannah Connor 以 15.0% 的折扣购买了 Headphones,价格为 $212.5"

"contact@techcorp.com"

2024-08-23

"Niko Petrov 以 25.0% 的折扣购买了 Headphones,价格为 $187.5"

"contact@techcorp.com"

2024-10-09

"Amir Rahman 以 10.0% 的折扣购买了 Laptop,价格为 $900.0"

"contact@techcorp.com"

2024-12-24

"Yusuf Abdi 以 10.0% 的折扣购买了 Chocolate,价格为 $4.5"

"info@foodies.com"

2024-12-24

"Niko Petrov 以 25.0% 的折扣购买了 Coffee,价格为 $7.5"

"info@foodies.com"

2025-01-02

"Yusuf Abdi 以 10.0% 的折扣购买了 Laptop,价格为 $900.0"

"contact@techcorp.com"

2025-01-10

"Amir Rahman 以 10.0% 的折扣购买了 Chocolate,价格为 $4.5"

"info@foodies.com"

2025-02-27

"Niko Petrov 以 25.0% 的折扣购买了 Phone,价格为 $375.0"

"contact@techcorp.com"

2025-03-05

"Mateo Ortega 以 5.0% 的折扣购买了 Chocolate,价格为 $4.75"

"info@foodies.com"

2025-03-05

"Mateo Ortega 以 5.0% 的折扣购买了 Coffee,价格为 $9.5"

"info@foodies.com"

行数: 14

示例 4. 场景 2:基于客户消费的供应商礼品卡分发

该示例计算每个 Customer 在对其购买的每件 Product 应用 discount 后的 customerRevenue(客户收入)。然后,根据客户的总消费将他们分为三组:消费超过 850 的归为 A 类,消费超过 350 但小于等于 850 的归为 B 类,消费 350 或以下的归为 C 类。C 类客户通过 FILTER 子句被排除在结果之外,仅留下有资格获得礼品卡的 A 类和 B 类客户。礼品卡中的 amount(金额)根据类别分配,A 类获得 20,B 类获得 10。礼品卡的详细信息随后被发送到相关客户的 email 中。

此示例强调了 LET 如何用于简洁地链接表达式,以及它不能用于执行聚合。

根据消费计算客户礼品卡分发
MATCH (customer:Customer)-[bought:BUYS]->(product:Product)
LET effectivePrice = product.price * (1 - customer.discount)
WITH customer, bought, sum(effectivePrice) AS customerRevenue
LET category = CASE
    WHEN customerRevenue > 850 THEN 'A'
    WHEN customerRevenue > 350 THEN 'B'
    ELSE 'C'
  END
FILTER category <> 'C'
LET amount = CASE
    WHEN category = 'A' THEN 20
    WHEN category = 'B' THEN 10
END
LET message = {
    type: 'giftcard',
    addressee: customer.firstName + ' ' + customer.lastName,
    amount: amount,
    year: bought.date.year
}
RETURN message, customer.email AS toCustomer, customerRevenue
ORDER BY amount
结果
message toCustomer customerRevenue

{amount: 10, addressee: "Niko Petrov", type: "giftcard", year: 2025}

"niko.petrov@example.com"

375.0

{amount: 20, addressee: "Amir Rahman", type: "giftcard", year: 2024}

"amir.rahman@example.com"

900.0

{amount: 20, addressee: "Mateo Ortega", type: "giftcard", year: 2024}

"mateo.ortega@example.com"

950.0

{amount: 20, addressee: "Leila Haddad", type: "giftcard", year: 2023}

"leila.haddad@example.com"

900.0

{amount: 20, addressee: "Yusuf Abdi", type: "giftcard", year: 2025}

"yusuf.abdi@example.com"

900.0

行:5