You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript配合MongoDB开发时循环给对象新增属性失效如何解决

问题核心原因

  1. Mongoose的find查询默认返回的是Mongoose Document实例,默认开启严格模式,未在Schema中定义的属性不会被序列化输出,所以即使手动挂载了votes属性,返回给前端时也会被过滤。
  2. forEach和map不会等待内部的async函数执行完成,你通过判断索引是否等于数组长度来返回响应的逻辑不可靠:异步查询的返回顺序是不确定的,很可能最后一个索引的查询先完成,此时前面的用户还没统计完投票数就提前返回了。

正确实现方案

基础修复版

查询时添加.lean()方法直接返回普通JS对象,配合Promise.all等待所有异步统计操作完成后再统一返回:

// 加lean()直接返回普通对象,可自由添加属性,同时查询性能更高
const users = await User.find(
  { teams: { $in: req.user.team_oid } },
  { _id: 1, firstname: 1, lastname: 1 }
).lean()

// 用Promise.all等待所有投票统计完成
const usersWithVotes = await Promise.all(
  users.map(async user => {
    const voteCount = await Survey.countDocuments({
      _id: mongoose.Types.ObjectId(req.params.s_oid),
      'facilitators.nominee': mongoose.Types.ObjectId(user._id)
    })
    user.votes = voteCount
    return user
  })
)

// 所有操作完成后统一返回
res.json({ users: usersWithVotes })

如果不想用lean(),也可以在挂载属性前手动将Document转成普通对象:

const userObj = user.toObject()
userObj.votes = voteCount
return userObj

性能优化版

上面的方案会产生N+1次数据库请求(1次查用户,N次查每个用户的投票数),如果用户数量较多,推荐用聚合查询一次性关联统计所有用户的投票数,只需要2次数据库请求即可完成:

// 先统计所有符合条件的用户投票数
const voteStats = await Survey.aggregate([
  { $match: { _id: mongoose.Types.ObjectId(req.params.s_oid) } },
  { $unwind: '$facilitators' },
  { $group: {
    _id: '$facilitators.nominee',
    votes: { $sum: 1 }
  }}
])
// 把统计结果转成映射表方便匹配
const voteMap = new Map(voteStats.map(item => [item._id.toString(), item.votes]))

// 查询用户后直接匹配投票数
const users = await User.find(
  { teams: { $in: req.user.team_oid } },
  { _id: 1, firstname: 1, lastname: 1 }
).lean()

const usersWithVotes = users.map(user => ({
  ...user,
  votes: voteMap.get(user._id.toString()) || 0
}))

res.json({ users: usersWithVotes })

内容的提问来源于stack exchange,提问作者Izzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 05:09:03