如何在MongoDB聚合查询中为指定用户添加积分排名字段?
问题:为MongoDB聚合结果添加用户排名字段
现有MongoDB的Users和Points集合数据如下:
Users集合数据
{ _id: ObjectId("62a2a0422ec90fea68390aaa"), name: 'Robert Yamashita', username: 'robyama', email: 'robert.yamashita@rocketmail.com', }, { _id: ObjectId("62a2a0452ec90fea68390aad"), name: 'Charles X', username: 'cvx', email: 'charles.xxx@rocketmail.com', }
Points集合数据
{ userId: ObjectId("62a2a0422ec90fea68390aaa"), action: 'Liked a post', points: 10, } { userId: ObjectId("62a2a0422ec90fea68390aaa"), action: 'Liked a post', points: 10, } { userId: ObjectId("62a2a0452ec90fea68390aad"), action: 'Liked a comment', points: 5, }
原聚合查询用于获取用户robyama的总积分,但需要补充rank字段(Robert积分20排第1,Charles积分5排第2),原查询及结果如下:
原聚合查询
db.users.aggregate([ { $match: { username: 'robyama' } }, { $lookup: { from: 'points', localField: '_id', foreignField: 'user', // 注意:此处原查询存在错误,Points集合关联字段为userId as: 'userPoints' } }, { $unwind: '$userPoints' }, { $group: { _id: { name: '$name', email: '$email', username: '$username', }, count: { $sum: '$userPoints.points' } } } ]);
原查询结果
{ "_id": { "name": "Robert Yamashita", "email": "robert.yamashita@rocketmail.com", "username": "robyama", }, "count": 20 }
期望输出
{ "_id": { "name": "Robert Yamashita", "email": "robert.yamashita@rocketmail.com", "username": "robyama", }, "count": 20, "rank": 1 }
解决方案:修改聚合管道
要实现排名,需先统计所有用户的总积分并排序,再为目标用户匹配排名,具体调整如下:
完整修正后的聚合管道
db.users.aggregate([ // 1. 修正关联字段,正确关联Points集合 { $lookup: { from: 'points', localField: '_id', foreignField: 'userId', as: 'userPoints' } }, // 2. 展开用户积分记录 { $unwind: '$userPoints' }, // 3. 按用户分组计算总积分 { $group: { _id: { name: '$name', email: '$email', username: '$username', }, count: { $sum: '$userPoints.points' } } }, // 4. 按总积分降序排序 { $sort: { count: -1 } }, // 5. 使用窗口函数计算用户排名(从1开始) { $setWindowFields: { sortBy: { count: -1 }, output: { rank: { $rank: {} } } } }, // 6. 最后筛选目标用户robyama { $match: { "_id.username": 'robyama' } } ]);
关键调整说明
- 修正
$lookup关联字段:将原查询中错误的foreignField: 'user'改为userId,确保关联逻辑正确。 - 移除初始
$match:必须先统计所有用户的积分总和,才能计算正确排名,不能一开始就过滤单个用户。 - 添加
$sort和$setWindowFields:$sort按总积分降序排列,确保排名顺序正确;$setWindowFields(MongoDB 5.0+支持)通过$rank函数生成排名,若需要连续排名(忽略并列情况),可改用$denseRank。
- 最后筛选目标用户:在计算完所有用户排名后,再筛选出
robyama的结果。
内容的提问来源于stack exchange,提问作者The Bassman
相关产品推荐
相关产品推荐

