You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Mongoose Upsert操作是否会更新Schema默认字段值?

这是Mongoose的预期行为,我来帮你理清原因和解决方案

你遇到的这个情况完全符合Mongoose的设计逻辑——当执行upsert操作时,Mongoose默认会对插入新文档更新已有文档两种场景都应用Schema中定义的默认值,哪怕你没有显式传入这些字段。

为什么会出现这个现象?

Mongoose在处理upsert请求时,会自动将你传入的更新文档与Schema的默认值做合并。也就是说,即使你没指定createDateupdateDate,它也会把default: Date.now生成的当前时间自动添加到操作中,不管是插入新数据还是更新已有数据。这是因为upsert的本质是“不存在则插入,存在则更新”,Mongoose默认会在这两个分支都执行默认值注入逻辑。

如何解决这个问题?

核心思路是区分两个日期字段的职责:createDate仅在文档首次插入时设置,updateDate在插入和更新时都要更新。这里有几种靠谱的方案:

1. 使用Mongoose内置的timestamps选项(最推荐)

Mongoose自带了时间戳自动处理功能,只要在Schema配置中加上timestamps: true,它会自动创建createdAtupdatedAt字段:

  • 插入文档时:createdAtupdatedAt都会设为当前时间
  • 更新文档时:仅updatedAt会更新为当前时间
  • Upsert操作时:完全符合你的需求——新文档生成两个时间,已有文档只更新updatedAt

代码示例:

const yourSchema = new Schema({
  // 你的业务字段,比如name、key等
}, { timestamps: true });

这样就不用手动定义createDateupdateDate了,Mongoose会帮你处理所有逻辑,省心又不易出错。

2. 手动控制字段的更新逻辑

如果你想保留自定义的字段名(比如createDateupdateDate),可以这样做:

  • createDate保留default: Date.now,确保首次插入时自动赋值
  • 移除updateDate的默认值,改为在每次更新操作时用$set显式设置当前时间

代码示例:

// Schema定义
const yourSchema = new Schema({
  // 其他字段
  createDate: { type: Date, default: Date.now },
  updateDate: { type: Date }
});

// Upsert操作
const upsertDoc = {
  $set: {
    // 你的业务更新字段
    updateDate: new Date() // 显式设置更新时间
  }
};
Model.update({ key: 123 }, upsertDoc, { upsert: true });

这样:

  • 插入新文档时:createDate用默认值,updateDate$set设为当前时间
  • 更新已有文档时:createDate保持不变,只有updateDate被更新

3. 配合setDefaultsOnInsert$setOnInsert精准控制

如果你需要更精细的控制,可以用Mongoose的setDefaultsOnInsert选项,结合$setOnInsert来确保createDate仅在插入时生效:

// Schema定义不变
const yourSchema = new Schema({
  // 其他字段
  createDate: { type: Date, default: Date.now },
  updateDate: { type: Date }
});

// Upsert操作
const upsertDoc = {
  $set: {
    // 业务字段
    updateDate: new Date()
  },
  $setOnInsert: {
    createDate: new Date() // 或者依赖默认值,配合setDefaultsOnInsert: true
  }
};
Model.update({ key: 123 }, upsertDoc, { upsert: true, setDefaultsOnInsert: true });

setDefaultsOnInsert: true会让Mongoose仅在插入新文档时应用默认值,更新时不会触发,再加上$setOnInsert兜底,能确保createDate不会被误更新。

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

火山引擎 最新活动