说明

定义可在流水线的后续阶段中引用的临时变量。

除非在后续阶段中明确将 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(...) 阶段类似,但它不是向文档添加字段,而是向 变量分配值。