为何函数参数传入的MongoDB文档无法删除_id,自定义普通对象却可以?
Mongoose文档对象删除
_id属性失效问题梳理 业务场景说明
- 异步函数
randomFunction接收id、metaData两个参数,在控制器层的调用逻辑为:
await randomFunction(id, doc);
- 入参
doc是MongoDB查询返回的文档对象,示例结构如下:
{ _id: "123456789012345678901234", age: 30, name: 'Lorem Ipsum', gender: 'male' }
- 预期目标:移除
metaData上的_id属性,将处理后的对象传给Mongoose的findOneAndUpdate()方法执行更新。
异常现象复现过程
基础实现失效
直接在函数内部执行删除操作,发现_id无法被正常移除:
const randomFunction = async (id, metaData) => { console.log(metaData) // 打印原始入参 delete metaData._id console.log(metaData) // 打印结果:_id属性仍然存在,删除未生效 }
临时绕开该问题的方案:手动新建普通对象,逐一提取metaData中除_id外的属性赋值,后续传入findOneAndUpdate()的逻辑可正常运行。
对照测试定位差异
为了明确两种对象的行为差异,做了对照测试:
const randomFunction = async (id, metaData) => { console.log(metaData) // 打印MongoDB来源的文档对象 delete metaData._id console.log(metaData) // 结果:_id无法删除 // 手动构造普通对象 const data = { _id: metaData._id, name: metaData.name, age: metaData.age, gender: metaData.gender } console.log(data) // 打印新建的普通对象 delete data._id console.log(data) // 结果:_id可正常删除 }
测试得出的明确差异:
- 从MongoDB传入的
metaData对象,执行delete移除_id完全失效 - 手动创建的原生JS普通对象,删除
_id的操作可正常生效 - 在Chrome控制台中构造普通对象传参测试删除逻辑,完全无法复现该异常
现象产生原因
两类对象的实现机制存在本质差异,才导致了删除操作的不同表现:
- 函数入参
metaData不是原生普通JS对象,是Mongoose框架生成的Document类实例。这类实例的字段值不是直接存储在对象的自有可删除属性上,而是通过Schema定义的get/set访问器对外暴露读写能力 - Mongoose初始化Document实例时,会将
_id这类框架维护的核心字段的属性描述符配置为configurable: false。根据JavaScript语言规则,对configurable: false的属性执行delete操作会直接静默失败,既不会移除属性,也不会抛出错误提示 - Chrome控制台无法复现问题,是因为控制台里手动创建的普通对象,所有属性默认都是
configurable: true的可配置状态,和Mongoose Document实例的属性配置逻辑完全不同,自然可以正常执行删除操作。
常规解决方案
不需要手动逐字段提取属性,拿到Mongoose文档对象后,先调用文档内置的toObject()方法将其转换为普通JavaScript对象,再执行删除_id的操作即可正常生效:
const randomFunction = async (id, metaData) => { // 先将Mongoose文档实例转为原生普通JS对象 const plainMetaData = metaData.toObject() delete plainMetaData._id // 后续传入findOneAndUpdate的逻辑可正常执行 }
内容的提问来源于stack exchange,提问作者Siddhesh Swami
相关产品推荐
相关产品推荐

