虚拟字段默认不出现在查询结果中,因其仅内存计算、不存入数据库;需显式调用.toobject({virtuals:true})或.tojson({virtuals:true})才能包含,lean()会彻底禁用虚拟字段。

虚拟字段默认不会出现在查询结果里,这是设计使然,不是 bug。 因为 virtual 只在内存中计算、不存入 MongoDB,所以 find、findOne 等原生查询方法返回的文档对象天然不含它们。开启 toJSON 选项本身不能让虚拟字段“进数据库”,但它能控制模型实例序列化时是否包含虚拟字段——这仅影响 .toJSON() 或 JSON.stringify() 的输出,不影响查询原始结果。
为什么 toJSON: { virtuals: true } 不起作用?
常见误解是:只要在 Schema 中写了 toJSON: { virtuals: true },查询出来的对象就自动带虚拟字段。其实不然:
-
toJSON选项只对模型实例(Document)生效,比如doc.toJSON()或res.json(doc)时才触发; -
find()返回的是原始 JavaScript 对象(plain object),不是 Document 实例,所以跳过所有toJSON配置; - 如果你用
lean()查询,哪怕返回的是 Document,也会被强制转成 plain object,toJSON完全失效。
如何让查询结果真正包含虚拟字段?
必须显式调用 .toObject() 或 .toJSON(),且确保传入正确选项:
- 用
.toObject({ virtuals: true }):适用于单个文档,比如await User.findOne().then(u => u.toObject({ virtuals: true })); - 用
.toJSON({ virtuals: true }):效果同上,但更常用于 API 响应(如 Express 中res.json(user)会自动调用它); - 批量查询需逐个转换:
users.map(u => u.toJSON({ virtuals: true })),不能直接对数组调用; - 如果用了
lean(),必须先去掉它,否则toObject和toJSON都不可用——因为 lean 返回的是 plain object,没有这些方法。
Schema 中 toJSON/virtuals 的典型写法及陷阱
正确配置 Schema 是前提,但容易漏掉关键点:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
- 必须同时设置
toObject: { virtuals: true }和toJSON: { virtuals: true },否则toObject()可能不生效; - 虚拟字段本身要设
cache: false(默认值),否则在多次调用间可能返回旧值; - 若虚拟字段依赖其他字段,记得在
get函数里加this.markModified('xxx')(极少需要,仅当内部修改了非虚拟字段时); - 不要在虚拟字段的
get里做异步操作(如 DB 查询),Mongoose 不支持,会静默失败或返回undefined。
替代方案:用 transform 钩子统一注入(适合 API 层)
如果每次都要手动调用 toJSON 太啰嗦,可以在 Schema 中用 transform 钩子自动处理:
userSchema.set('toJSON', {
virtuals: true,
transform: function(doc, ret) {
// 所有 toJSON 输出都会经过这里
ret.fullname = ret.firstName + ' ' + ret.lastName;
}
});
注意:transform 只对 toJSON 生效,不影响 toObject;它运行在序列化阶段,不能访问 this 上下文(doc 是原始数据,ret 是即将返回的对象),所以不适合复杂逻辑。
最易忽略的一点:虚拟字段是否出现在最终响应,完全取决于你用的是 find().lean() 还是 find() + 显式序列化。很多线上问题都卡在没意识到 lean() 会彻底绕过所有 Mongoose 的虚拟、getter、hook 机制。










