MongoDB聚合管道优化:实现单用户最多2篇帖子的降序查询
MongoDB聚合管道优化需求
文档结构
Post文档结构如下:
post = { "_id": "uid1", "userID": "user1", "likeNum": 30, }
当前聚合管道
现有聚合管道使用$nin排除指定用户ID和已浏览帖子ID,结构如下:
fake_pipeline = [ {'$match': {'userID': {'$nin': user_ids}, '_id': {"$nin": seen_ids}}}, {'$group': {'_id': '$userID', 'posts': {'$push': '$likeNum'}}}, {'$project': {'posts': {'$slice': [{'$sortArray': {'input': '$posts', 'sortBy': {'likeNum': -1}}}, 2]}}}, {'$limit': 10} ]
需求
修改上述管道,实现:
- 返回最多10条完整的post文档,每个
userID对应的帖子不超过2条 - 最终结果按
likeNum降序排列(可在业务代码中完成排序,聚合管道尽量精简)
示例数据
数据库中的帖子列表:
post_list_in_db = [ {"_id": "uid1", "userID": "user1", "likeNum": 29}, {"_id": "uid2", "userID": "user1", "likeNum": 2}, {"_id": "uid3", "userID": "user1", "likeNum": 13}, {"_id": "uid4", "userID": "user2", "likeNum": 21}, {"_id": "uid5", "userID": "user2", "likeNum": 19}, {"_id": "uid6", "userID": "user3", "likeNum": 1}, {"_id": "uid7", "userID": "user3", "likeNum": 8}, {"_id": "uid8", "userID": "user3", "likeNum": 14}, {"_id": "uid9", "userID": "user3", "likeNum": 4}, {"_id": "uid10", "userID": "user4", "likeNum": 20}, {"_id": "uid11", "userID": "user4", "likeNum": 9}, {"_id": "uid12", "userID": "user4", "likeNum": 11}, ]
预期输出
[ {"_id": "uid1", "userID": "user1", "likeNum": 29}, {"_id": "uid4", "userID": "user2", "likeNum": 21}, {"_id": "uid10", "userID": "user4", "likeNum": 20}, {"_id": "uid5", "userID": "user2", "likeNum": 19}, {"_id": "uid8", "userID": "user3", "likeNum": 14}, {"_id": "uid3", "userID": "user1", "likeNum": 13}, {"_id": "uid12", "userID": "user4", "likeNum": 11}, {"_id": "uid7", "userID": "user3", "likeNum": 8}, ]
优化后的聚合管道
optimized_pipeline = [ # 过滤排除指定用户和已浏览帖子 {'$match': {'userID': {'$nin': user_ids}, '_id': {'$nin': seen_ids}}}, # 按赞数降序排序,确保每组取到的是该用户最高赞的帖子 {'$sort': {'likeNum': -1}}, # 按用户分组,推送完整帖子文档到数组 {'$group': { '_id': '$userID', 'posts': {'$push': '$$ROOT'} }}, # 每组只保留前2条帖子 {'$project': { 'posts': {'$slice': ['$posts', 2]} }}, # 展开帖子数组为单个文档 {'$unwind': '$posts'}, # 将帖子内容恢复为根文档结构 {'$replaceRoot': {'newRoot': '$posts'}}, # 限制最多返回10条结果 {'$limit': 10} ]
管道说明
- $match:保留原过滤逻辑,先剔除不需要的用户和已浏览帖子,减少后续处理的数据量,提升性能。
- $sort:全局按
likeNum降序排序,保证后续分组时每组内的帖子是按赞数从高到低排列的,避免在分组内二次排序。 - $group:按
userID聚合,使用$$ROOT推送完整的帖子文档,确保不会丢失字段。 - $project:用
$slice截取每组的前2条帖子,满足单用户最多2条的限制。 - $unwind + $replaceRoot:将分组后的数组展开为单个完整的post文档,恢复原文档结构。
- $limit:限制最终结果最多10条。
如果需要严格的全局likeNum降序,可在业务代码中对聚合返回的结果再执行一次排序操作,这样能让聚合管道更精简,减少数据库端的计算压力。
内容的提问来源于stack exchange,提问作者Tom3652
相关产品推荐
相关产品推荐

