Mongoose如何统计最新会话国家为USA的用户数量
问题背景
现有Mongoose用户表Schema定义如下:
const userSchema = new Schema({ name: { type: String, required: true }, sessions: [{ token: { type: String, required: true }, agent: { ip: { type: String, required: true }, country: { type: String, required: true }, }, update: { type: Date, default: Date.now }, }] })
需求为:提取每个用户sessions数组中update字段值最大(最新)的会话,统计该最新会话agent.country值为usa的用户总数量。
原有错误实现代码如下:
let count = await User.countDocuments({ 'sessions.agent.country': 'usa' }) console.log('count: ' + count)
该逻辑的问题是:countDocuments的匹配规则会遍历sessions数组的所有元素,只要用户任意一条历史会话的country为usa就会被统计,完全没有校验该会话是否为用户最新会话,统计结果会偏大。
正确实现代码
使用MongoDB聚合管道实现,无需拆分数组,性能更优:
const countResult = await User.aggregate([ // 为每个用户提取最新的会话记录 { $addFields: { latestSession: { $reduce: { input: "$sessions", initialValue: { update: new Date(0) }, in: { $cond: [ { $gt: ["$$this.update", "$$value.update"] }, "$$this", "$$value" ] } } } } }, // 仅匹配最新会话国家为usa的用户 { $match: { "latestSession.agent.country": "usa" } }, // 统计符合条件的总用户数 { $count: "total" } ]) // 无匹配结果时聚合返回空数组,总数默认取0 const count = countResult[0]?.total || 0 console.log('count: ' + count)
逻辑说明
- 用
$reduce遍历单用户的所有sessions记录,逐次对比update时间,始终保留时间更大的会话,最终得到该用户的最新会话,该操作不需要拆分数组,相比$unwind实现性能更高,兼容MongoDB 4.0及以上版本 - 匹配阶段仅针对提取出的最新会话做国家字段校验,不会命中历史旧会话数据
- 最终通过
$count直接返回符合条件的用户总数,结果完全匹配需求
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

