You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Mongoose的findOneAndUpdate中结合聚合管道使用$set与$setOnInsert

Mongoose聚合管道与upsert字段处理问题解答

问题场景

我使用Mongoose的findOneAndUpdate配合聚合管道的$set操作符处理User模型的registeredAt和createdAt字段,代码如下:

const newUser = await User.findOneAndUpdate(
  findOneAndUpdateQuery,
  [
    {
      $set: {
        registeredAt: {
          $cond: [
            {
              $not: ["$registeredAt"],
            },
            new Date(),
            "$registeredAt",
          ],
        },
        createdAt:{
          $cond: [
            {
              $not: ["$createdAt"],
            },
            new Date(),
            "$createdAt",
          ],
        },
    },
    // {$setOnInsert: {
    //   registeredAt: new Date(),
    // }},
  ],
  {
    new: true,
    runValidators: true,
    upsert: true,
    setDefaultsOnInsert: true,
  }
);

User模型定义:

const UserSchema = new mongoose.Schema(
  {
    registeredAt: Date,
    createdAt: {
      type: Date,
      default: Date.now,
      required: true,
    },
  },
);

当前遇到两个问题:

  • 执行upsert操作时,若不在$set管道中指定createdAt字段,该字段不会被设置;我需要仅在插入文档时设置registeredAt和createdAt,后续更新时不修改这两个字段,请问能否在上述查询中结合$setOnInsert实现需求?
  • 为何使用聚合管道的$set时模型默认值不生效?

解答

1. 可以结合$setOnInsert实现需求

$setOnInsert在聚合管道更新中依然有效,它只会在插入新文档时执行字段赋值,更新已有文档时完全不会触发,正好匹配你的需求。

修改后的代码可以简化为:

const newUser = await User.findOneAndUpdate(
  findOneAndUpdateQuery,
  [
    {
      $setOnInsert: {
        registeredAt: new Date(),
        createdAt: new Date() // 也可以用Date.now(),和模型默认值逻辑保持一致
      }
    }
  ],
  {
    new: true,
    runValidators: true,
    upsert: true,
    setDefaultsOnInsert: true,
  }
);

如果希望createdAt沿用模型定义的默认值,也可以不在$setOnInsert中指定该字段,保留setDefaultsOnInsert: true即可,插入时Mongoose会自动填充模型默认值:

const newUser = await User.findOneAndUpdate(
  findOneAndUpdateQuery,
  [
    {
      $setOnInsert: {
        registeredAt: new Date()
      }
    }
  ],
  {
    new: true,
    runValidators: true,
    upsert: true,
    setDefaultsOnInsert: true,
  }
);

2. 聚合管道$set不触发模型默认值的原因

这是因为聚合管道更新会绕过Mongoose的模型层逻辑,直接和MongoDB原生交互:

  • 聚合管道属于MongoDB原生功能,Mongoose不会自动为其应用模型默认值、中间件等逻辑,仅会在显式开启runValidators时触发验证
  • setDefaultsOnInsert选项仅对普通对象形式的更新操作生效(比如{ $set: {...}, $setOnInsert: {...} }),对聚合管道更新不起作用
  • 你之前用$cond判断字段是否存在并赋值,是在聚合管道内手动实现了类似默认值的逻辑,但这和Mongoose模型的默认值机制完全独立

如果要在聚合管道更新中使用默认值,要么手动在管道内处理字段赋值,要么改用普通对象形式的更新操作。


内容的提问来源于stack exchange,提问作者YulePale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 06:56:54