说明
定义可在流水线的后续阶段中引用的临时变量。
除非在后续阶段中明确将 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(...)
阶段类似,但它不是向文档添加字段,而是向
变量分配值。