Mongoose查询时排除依赖字段后如何获取virtual虚拟属性值
你之前使用的虚拟属性是客户端计算逻辑,必须依赖完整的likes数组返回才能计算,既然你查询时排除了likes字段,文档内没有对应数据,虚拟属性自然无法正常生成点赞数。你可以选择以下两种方案解决问题:
方案1:使用MongoDB聚合管道在数据库侧直接计算点赞数
用$size聚合操作符可以直接在数据库层面统计likes数组的长度,不需要把整个数组返回给服务端,性能开销极低:
const post = await Post.aggregate([ // 匹配指定ID的帖子 { $match: { _id: mongoose.Types.ObjectId(postId) } }, // 新增numLikes字段,值为likes数组的长度 { $addFields: { numLikes: { $size: "$likes" } } }, // 排除不需要返回的字段 { $project: { likes: 0, shares: 0 } } ]) // 聚合返回的是数组,取第一个元素即为目标帖子 const targetPost = post[0]
方案2:新增持久化的点赞数字段(高并发场景更推荐)
如果点赞数是高频查询的属性,建议直接在Schema中新增numLikes普通字段存储点赞数,每次点赞/取消点赞操作时原子更新该字段,后续查询直接返回字段值即可,不需要额外计算:
- 首先修改Schema定义:
const postSchema = new mongoose.Schema({ UserId: mongoose.Types.ObjectId, Title: String, Desc: String, likes: [mongoose.Types.ObjectId], shares: [mongoose.Types.ObjectId], // 新增持久化点赞数字段,默认值0 numLikes: { type: Number, default: 0 } })
- 点赞/取消点赞操作的原子更新示例:
// 点赞:同时给numLikes加1,把用户ID加入likes数组(原子操作避免数据不一致) await Post.findByIdAndUpdate(postId, { $inc: { numLikes: 1 }, $push: { likes: userId } }) // 取消点赞:同时给numLikes减1,把用户ID从likes数组移除 await Post.findByIdAndUpdate(postId, { $inc: { numLikes: -1 }, $pull: { likes: userId } })
- 后续查询直接正常取字段即可,不需要额外处理:
const post = await Post.findById(postId).select("-likes -shares") // 直接获取点赞数 console.log(post.numLikes)
内容的提问来源于stack exchange,提问作者Juno
相关产品推荐
相关产品推荐

