说明
定义可在流水线的后续阶段引用的临时变量。
除非在后续阶段中明确将 let(...) 阶段中创建的变量分配给某个字段(例如,使用 add_fields(...) 或 select(...)),否则这些变量不会包含在最终结果中。这样,您就可以将复杂的逻辑分解为更小的可重用组件,从而简化逻辑,而不会使输出文档杂乱无章。let(...) 阶段对于相关子流水线特别有用,因为子流水线需要引用父文档范围内的值。
示例
Node.js
const results = await db.pipeline()
.collection("/awards")
// `let(...)` referred to as `define(...)` in the Web SDK.
.define(rand().as("r"))
.addFields(
switchOn(
lessThan(variable("r"), 0.05), constant("rare"),
lessThan(variable("r"), 0.25), constant("uncommon"),
constant("common")).as("random_score"))
.execute();
行为
变量与字段
字段表示存储在文档中的数据,而变量是仅在流水线执行期间存在的临时值。
| 字段 | 变量 | |
|---|---|---|
| 用途 | 访问或将字段存储到文档中 | 在流水线执行期间生成或访问临时值 |
| SDK 用法 | field("name") |
variable("name") |
| 范围 | 当前文档的本地 | 从全局到流水线和子流水线 |
| 未定义的引用 | 计算结果为 absent |
生成运行时错误 |
范围:
字段的作用域限定为本地文档,而变量是在单独的作用域中定义的,并且在首次出现将多个文档“合并”在一起的阶段(例如 aggregate(...) 或 distinct(...))之前,在各个阶段中都保持可访问状态。将多个文档“合并”在一起的阶段不允许在之后使用变量引用,因为通过将前一阶段的结果合并在一起,变量不再具有一个值。
正确做法:在过滤文档的字段后引用变量。
Node.js
const results = db.pipeline()
.collection("/awards")
.define(min(field("score").abs(), constant(100)).as("normalized_score"))
.select(field("__name__"), field("owner_id"))
// Successfully able to use the variable.
.where(variable("normalized_score").greaterThan(10))
.execute();
错误:在聚合之后引用变量。
Node.js
const results = db.pipeline()
.collection("/awards")
.define(min(field("score").abs(), constant(100)).as("normalized_score"))
.aggregate({
accumulators: [ field("score").avg().as("avg_score") ],
groups: [ field("owner_id") ]
})
// Attempting to use the variable throws a request validation error.
.where(variable("normalized_score").greaterThan(10))
.execute();
未定义的引用:
引用未定义的字段没有问题(只会评估为 absent),但在请求验证期间尝试引用未定义的变量会失败。从这个意义上讲,字段引用可以视为在运行时在映射中执行查找,而变量引用更类似于静态编译的编程语言中的变量。
全局范围和子查询
使用嵌套流水线时,变量至关重要。子流水线在其自己的范围内执行,并且只能访问当前正在处理的文档的字段。如需在子查询中使用“父”文档中的值,您必须先使用 let(...) 阶段将其定义为变量。
Node.js
// Fetch reviewers alongside their negative reviews.
const pipeline = db.pipeline()
.collection("/reviewers")
// `let(...)` referred to as `define(...)` in the Web SDK.
.define(field("__name__").as("reviewer_name"))
.select("__name__", array(db.pipeline().collectionGroup("reviews")
.where(field("author").equals(variable("reviewer_name")))
.where(field("rating").lessThan(2))
.select("review", "rating")).as("negative_reviews"))
.execute();
重叠变量
如果定义的变量的名称与之前 let(...) 阶段中已定义的变量的名称相同,则会覆盖之前的变量。这可用于在流水线运行过程中更新临时状态。
子流水线中引用的变量遵循许多编程语言中常见的词法作用域规则,是指由最接近的(父)流水线定义的同名变量。
与 add_fields(...) 比较
let(...) 阶段的行为与 add_fields(...) 阶段类似,但它不是向文档添加字段,而是为变量赋值。