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

Mongoose读取数据库中已存在的必填字段userId为undefined导致save()失败的问题求助

Mongoose读取数据库中已存在的必填字段userId为undefined导致save()失败的问题求助

我完全能理解你现在的困惑——明明数据库里userId字段好好的,用Compass和原生查询都能看到,结果Mongoose读出来就成了undefined,调用save()还触发必填校验失败,甚至冒出个完全不相关的Cannot read properties of undefined (reading 'options')错误,这简直离谱!

结合你给出的版本(mongoose 6.5.5、mongodb驱动5.1.0)和描述的细节,我认为最核心的问题是Schema字段类型和数据库实际存储类型不匹配,下面给你拆解原因和解决方法:

最可能的根因:Schema类型与数据库存储类型不兼容

你提到数据库里的userId是ObjectId类型,但你的Schema里定义的是type: String:

userId: { type: String, required: true, ref: 'user' }

Mongoose在把数据库中的文档转换为Mongoose Document对象(也就是你用findById()/findOne()拿到的实例)时,会严格按照Schema的类型做校验和转换。当数据库里的ObjectId转成String的过程中,由于你用的Mongoose版本相对较旧,搭配MongoDB驱动5.1.0可能存在小版本适配bug,就会导致这个字段解析失败,最终变成undefined。

而那个莫名其妙的options读取错误,大概率是Mongoose内部在处理类型不匹配的字段时抛出的未被正确格式化的异常——等你修复了类型问题,这个错误会自动消失。

解决方法

1. 修正Schema类型(推荐,从根源解决)

把Schema中userId的类型改成和数据库一致的ObjectId:

const mongoose = require('mongoose');

const eventSchema = new mongoose.Schema({
  userId: { 
    type: mongoose.Types.ObjectId, // 改为ObjectId类型,匹配数据库存储
    required: true, 
    ref: 'user' 
  },
  // 其他字段...
});

如果你的业务代码里需要把userId当成字符串使用,可以加个transform选项,在把Document转成普通对象时自动转成字符串:

userId: {
  type: mongoose.Types.ObjectId,
  required: true,
  ref: 'user',
  transform: (val) => val.toString() // 读取时自动转字符串
}

2. 临时Workaround(不推荐,仅应急)

如果暂时不想修改Schema,可以在fetch文档后,直接从_doc属性(Mongoose Document的原始数据库数据)中取出userId,手动赋值回去:

const event = await Model.findById(eventId);
// 从原始数据中取userId,转成字符串后赋值
if (!event.userId) {
  event.userId = event._doc.userId.toString();
}
// 再进行更新和save操作

这个方法本质上和你现在的“hack”类似,但更明确地取原始数据,避免Mongoose的类型解析干扰。

3. 版本兼容性排查

你使用的mongoose6.5.5是2022年8月的版本,mongodb驱动5.1.0是2022年10月的版本,两者之间可能存在小的适配问题。可以尝试:

  • 把Mongoose升级到6.x系列的最新稳定版(比如6.12.0)
  • 或者把MongoDB驱动降到4.x版本(比如4.17.0)
    升级后再测试是否还会出现字段解析异常的问题。

额外排查点

虽然你已经排除了大部分情况,但可以再确认:

  • 有没有给userId字段加自定义的getter函数?如果有,可能在某些场景下返回了undefined
  • 有没有开启Mongoose的strict模式?不过strict模式默认是开启的,只会过滤Schema中没有的字段,不会影响已存在的字段

验证方法

修改Schema类型后,重新用findById()获取文档,console.log(event.userId)应该能看到正确的ObjectId(或字符串,如果你加了transform),此时再调用save()就不会触发必填字段的校验错误了,那个options的异常也会消失。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:24:43