如何将Mongoose对象推入另一Mongoose对象的数组中
问题原因
你观察到的现象是Mongoose的类型强制转换机制导致的,属于预期行为,不是代码bug。
- 你在User模型中将
snippets字段定义为ObjectId类型数组,无论你传入push方法的是完整Mongoose文档实例、普通JS对象还是ID字符串,Mongoose都会自动做类型转换,仅提取目标对象的_id值存入数组,不会保留完整对象内容。 - 你提到Snippet模型中
author字段没有出现类似问题,本质是认知偏差:author本身也是单个ObjectId类型的引用字段,你传入完整User文档时,Mongoose同样只会存储_id,你能看到完整用户信息是关联查询populate()填充的结果,并非字段中真的存储了完整User对象。
对应解决方案
根据你实际的存储需求,选对应方案调整即可:
方案1:保留关联引用模式(和你当前Schema设计匹配)
如果你本来就是想通过ObjectId做集合关联,不把Snippet内容冗余存在User文档中:
- 你现在用的
user.snippets.push(snippet)写法本身是正确的,比updateOne的写法更适配Mongoose文档实例操作逻辑,push完成后记得执行await user.save()将修改持久化到数据库即可。 - 之前尝试的
updateOne写法不生效,核心原因是你没有加await等待异步执行完成,且直接传入Mongoose文档实例作为更新值时,数据库驱动无法正确识别序列化内容;就算执行成功,在当前Schema定义下,数组里依然只会存储ObjectId。 - 要获取snippets对应的完整文档内容,查询User时链式调用
.populate('snippets')即可,返回结果中snippets字段会被替换为匹配的完整Snippet文档,不会只显示ObjectId。
注:你给ObjectId类型字段配置的
trim、lowercase属性是字符串类型专用的配置项,对ObjectId类型完全不生效,可以直接删除,不影响功能。
方案2:使用子文档嵌入模式
如果你希望把完整Snippet内容直接嵌入User文档的snippets数组,不做跨集合关联:
你需要修改User模型的snippets字段定义,不能使用ObjectId类型,而是直接传入Snippet对应的Schema作为数组成员类型,示例如下:
// 先定义/引入Snippet的Schema结构 const snippetSchema = new mongoose.Schema({ title: String, content: String, // 其余Snippet业务字段 }) // 修改User模型中的snippets字段定义 snippets: { type: [snippetSchema], required: false }
修改Schema后再执行user.snippets.push(snippet),数组中就会存储完整的Snippet子文档内容,不会只保留ObjectId。
内容的提问来源于stack exchange,提问作者Dominus
相关产品推荐
相关产品推荐

