Mongoose查询优化:获取唯一用户的最优成绩排行榜
解决Mongoose排行榜中用户去重并保留最优成绩的问题
这个问题确实很常见——做答题排行榜时,肯定不想让同一个用户霸榜对吧?你的需求核心是每个用户只保留其最优成绩(分数最高,同分则用时最短),然后取前10个不同的用户。用普通的find()+sort()+limit()确实搞不定,得用MongoDB的聚合管道来实现,我给你一步步拆解:
实现思路
我们需要通过四个聚合阶段来达成目标:
- 全局预排序:先把所有文档按「分数降序、用时升序」排列,这样每个用户的最优成绩会排在该用户所有文档的最前面。
- 按用户分组:以
userId为分组依据,提取每组的第一个文档(也就是该用户的最优成绩)。 - 最终排序:对分组后的结果再次按「分数降序、用时升序」排序,确保排行榜的顺序完全符合规则。
- 限制数量:取前10个用户的最优成绩。
完整代码示例
const topUsers = await QuizModel.aggregate([ // 1. 全局排序:让每个用户的最优成绩排在该用户文档的最顶部 { $sort: { "result.score": -1, // 分数从高到低 "result.time": 1 // 用时从短到长 } }, // 2. 按userId分组,取每组第一个文档(即该用户的最优成绩) { $group: { _id: "$userId", // 分组键:userId quizId: { $first: "$quizId" }, // 保留该用户最优成绩的quizId userId: { $first: "$userId" }, // 保留userId字段 result: { $first: "$result" } // 保留完整的result对象 } }, // 3. 对分组后的结果再次排序,确保排行榜顺序正确 { $sort: { "result.score": -1, "result.time": 1 } }, // 4. 取前10个用户 { $limit: 10 }, // 可选:移除自动生成的_id字段,让输出更干净 { $project: { _id: 0 } } ]);
为什么这个方案可行?
拿你给出的例子验证:
- User1的5条文档经过第一步排序后,
score:100的文档会排在最前面; - User2的5条文档排序后,
score:95的文档排在最前面; - 分组阶段会分别提取User1和User2的第一条文档,得到两个唯一用户的最优成绩;
- 最终排序后,User1的成绩会排在User2前面,完全符合你的期望输出。
额外说明
- 如果你的数据集很大,建议给
userId、result.score、result.time建立复合索引,能大幅提升聚合操作的性能; - 如果你需要保留更多原文档的字段,只需要在
$group阶段用$first添加对应的字段即可。
内容的提问来源于stack exchange,提问作者Dharmaraj
相关产品推荐
相关产品推荐

