Mongoose深度populate关联字段时虚拟属性未返回问题求解
Mongoose深度populate场景下虚拟属性缺失修复方案
问题复现
深度populate关联的Comment模型虚拟属性formatted_date无法随接口响应返回,普通字段返回正常。
问题接口代码:
exports.get_user_feed = async (req, res, next) => { const options = { sort: { date: -1 } }; const user = await User.find( { username: req.params.user }, "username posts avatar followers following" ) .populate({ path: "posts", options, populate: [ { path: "author" }, { path: "comments", populate: { path: "author" } }, ], }) .sort({ "posts.date": 1 }); res.json({ ...user }); };
Comment模型定义:
const mongoose = require("mongoose"); const { DateTime } = require("luxon"); const Schema = mongoose.Schema; const CommentSchema = new Schema({ targetPost: { type: Schema.Types.ObjectId, ref: "Post", required: true }, author: { type: Schema.Types.ObjectId, ref: "User", required: true }, date: { type: Date, required: true }, content: { type: String, maxlength: 400 }, comments: [{ type: Schema.Types.ObjectId, ref: "Comment" }], stars: [{ type: Schema.Types.ObjectId, ref: "User" }], }); CommentSchema.virtual("url").get(function () { return "/" + this.targetPost.url + this._id; }); CommentSchema.virtual("formatted_date").get(function () { return ( DateTime.fromJSDate(this.date).toLocaleString(DateTime.DATE_MED) + " at " + DateTime.fromJSDate(this.date).toLocaleString(DateTime.TIME_SIMPLE) ); }); module.exports = mongoose.model("Comment", CommentSchema);
根本原因
Mongoose文档默认在序列化为JSON/普通对象时不会输出虚拟属性,和populate的嵌套深度没有关系,只要没有显式开启虚拟属性序列化配置,任何层级的虚拟属性都不会被返回。
另外原代码直接用展开运算符处理Mongoose文档数组,也会跳过Mongoose内部的序列化逻辑,增大属性丢失概率。
修复步骤
- 给CommentSchema添加配置,开启序列化时的虚拟属性输出:
const CommentSchema = new Schema({ // 原有字段定义保持不变 targetPost: { type: Schema.Types.ObjectId, ref: "Post", required: true }, author: { type: Schema.Types.ObjectId, ref: "User", required: true }, date: { type: Date, required: true }, content: { type: String, maxlength: 400 }, comments: [{ type: Schema.Types.ObjectId, ref: "Comment" }], stars: [{ type: Schema.Types.ObjectId, ref: "User" }], }, { // 开启toJSON、toObject时的虚拟属性输出 toJSON: { virtuals: true }, toObject: { virtuals: true } });
- 调整接口返回逻辑,避免直接展开Mongoose文档:
因为User.find返回的是匹配用户的数组,如果是按唯一用户名查询,建议改用User.findOne,再调用toJSON()做序列化后返回:
exports.get_user_feed = async (req, res, next) => { const options = { sort: { date: -1 } }; const user = await User.findOne( { username: req.params.user }, "username posts avatar followers following" ) .populate({ path: "posts", options, populate: [ { path: "author" }, { path: "comments", populate: { path: "author" } }, ], }) .sort({ "posts.date": 1 }); res.json(user.toJSON()); };
如果Post、User模型也有需要返回的虚拟属性,给对应Schema加上相同的toJSON/toObject配置即可。
内容的提问来源于stack exchange,提问作者filroy-codes
相关产品推荐
相关产品推荐

