LET仅限 Cypher 25Neo4j 2025.06 引入
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 |
|---|
|
|
|
|
|
|
|
行:7 |
LET 绑定多个变量MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
LET supplier = s.name, product = p.name
RETURN supplier, product
| supplier | product |
|---|---|
|
|
|
|
|
|
|
|
|
|
行:4 |
|
LET 与 WITH 的区别
LET 和 WITH 之间存在重要差异,可以分为以下几类:
作用域中的变量
LET 不会从后续子句的作用域中删除变量,而 WITH 会。因此,LET <variable> = <expression> 是 WITH *, <expression> AS <variable> 的替代品,而不是 WITH <expression> AS <variable>(后者会删除前一个子句中存在但未在 <expression> 中引用的任何变量)。
LET 和 WITH任何未被 WITH 显式引用(或通过 WITH * 携带)的变量都会从后续子句的作用域中删除。
WITH 删除的变量MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
WITH s.name AS supplier
RETURN supplier, p.name AS product
42N62: 错误: 语法错误或访问规则冲突 - 变量未定义。变量 42001:错误:语法错误或访问规则冲突 - 无效语法 |
然而,LET 无法控制哪些变量保留在作用域中。因此,在上述查询中将 WITH 替换为 LET 将会返回结果。
LET 不会删除变量MATCH (s:Supplier)-[:SUPPLIES]->(p:Product)
LET supplier = s.name
RETURN supplier, p.name AS product
| supplier | product |
|---|---|
|
|
|
|
|
|
|
|
|
|
行:5 |
|
链式表达式
LET 不会删除变量这一事实意味着它可用于以清晰简洁的方式链接表达式,其中在一个 LET 子句中绑定的变量可以在后续子句中被引用。
LET 和 WITH下面的查询显示,由 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 |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行: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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:7 |
|
WITH 和 LETMATCH (c:Customer)-[:BUYS]->(p:Product)
WITH DISTINCT c, sum(p.price) AS totalSpent
LET fullName = c.firstName + ' ' + c.lastName
RETURN fullName, totalSpent
| customer | totalSpent |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:7 |
|
高级示例
以下示例演示了 LET 及其链接表达式的能力如何用于更高级的查询中
此示例检索有关 Customer(客户)购买了什么 Product(产品)以及从哪个 Supplier(供应商)处购买的信息。然后,它计算应用 discount(折扣)后的 price(价格),并为每次购买构建一条 message(消息),其中包括每个 Customer 的 fullName(全名)和应用折扣后产品的 effectivePrice(有效价格),并发送到 Supplier 的 email(邮箱)。
此示例强调了 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 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行数: 14 |
||
该示例计算每个 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 |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
行:5 |
||