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

Mongoose findOneAndUpdate的pre hook修改字段无效,求正确实现方案

使用Mongoose的findOneAndUpdate pre钩子修改字段的正确方式

我在使用Mongoose的pre钩子处理findOneAndUpdate操作时,希望在文档保存到数据库前将password字段转为MD5值,但当前代码未生效,文档无任何变化。以下是实际文档、期望文档及代码,求正确实现方式:

实际文档

{
  _id: new ObjectId("622457f5555562da89b7a1dd"),
  id: '5982ca552aeb2b12344eb6cd',
  name: 'Test User',
  configuration: [
    {
      email: 'test2@gmail.com',
      password: 'p@ssw0rd',
      _id: new ObjectId("9473l58f2ad34efb816963dd"),
    },
    {
      email: 'test3@gmail.com',
      password: 'trUstN0oNe',
      _id: new ObjectId("8674884cec1877c59c8838e0")
    }
  ],
  __v: 0
}

期望文档

{
  _id: new ObjectId("622457f5555562da89b7a1dd"),
  id: '5982ca552aeb2b12344eb6cd',
  name: 'Test User',
  configuration: [
    {
      email: 'test2@gmail.com',
      password: '0f359740bd1cda994f8b55330c86d845',
      _id: new ObjectId("9473l58f2ad34efb816963dd"),
    },
    {
      email: 'test3@gmail.com',
      password: '3dba7872281dfe3900672545356943ce',
      _id: new ObjectId("8674884cec1877c59c8838e0")
    }
  ],
  __v: 0
}

当前代码

const UserSchema = new Schema({
   id: {
       type: String,
       required: [true, "'id' value is required"]
   },
   name: {
       type: String,
       required: [true, "'name' value is required"]
   },
   configuration: [ConfigModel.schema]
});

const ConfigSchema = new Schema({
   email: {
       type: String,
       required: [true, "Email is required"]
   },
   password: {
       type: String,
       required: [true, "Password is required"]
   }
});


UserSchema.pre('findOneAndUpdate', async function(next) {
   const docToUpdate = await this.model.findOne(this.getQuery());

   docToUpdate.configuration.forEach((item,i) => {
      docToUpdate.configuration[i].password = md5(item.password);
   });
   return next();
});

问题分析

你的代码问题在于:虽然查询到了要更新的文档并修改了密码,但没有将修改后的内容应用到findOneAndUpdate的更新操作中。findOneAndUpdate的pre钩子默认是基于你传入的更新参数执行操作,而非你修改后的查询文档。

解决方案

方案一:直接修改更新操作的参数(推荐)

这种方式更高效,符合findOneAndUpdate的设计逻辑,无需额外的数据库查询和保存操作。核心是获取当前更新操作中的configuration数据,对密码进行MD5加密后替换原数据:

const md5 = require('md5'); // 确保已安装并引入md5库

UserSchema.pre('findOneAndUpdate', function(next) {
  // 检查更新操作中是否包含configuration字段的修改
  if (this._update.$set && this._update.$set.configuration) {
    // 遍历configuration数组,加密每个密码
    this._update.$set.configuration = this._update.$set.configuration.map(item => ({
      ...item,
      password: md5(item.password)
    }));
  }
  next();
});

方案二:修改查询到的文档并手动保存

如果你的需求是更新数据库中已存在的文档密码(而非传入新的configuration数组),可以修改查询到的文档后手动保存,但需注意取消原findOneAndUpdate操作避免重复更新:

const md5 = require('md5');

UserSchema.pre('findOneAndUpdate', async function(next) {
  const docToUpdate = await this.model.findOne(this.getQuery());
  if (!docToUpdate) return next();

  let hasChanged = false;
  docToUpdate.configuration.forEach((item, i) => {
    const encryptedPwd = md5(item.password);
    if (item.password !== encryptedPwd) {
      docToUpdate.configuration[i].password = encryptedPwd;
      hasChanged = true;
    }
  });

  if (hasChanged) {
    await docToUpdate.save();
    // 取消原findOneAndUpdate操作,避免重复更新
    this._update = {};
    this.setOptions({ overwrite: false });
  }
  next();
});

注意事项

  • 确保已正确安装并引入md5库(可通过npm install md5安装)。
  • 如果你的更新操作使用了其他MongoDB操作符(如$push、$addToSet),需要对应调整钩子中的处理逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:12:32