基于赞成票数的项目排名异常:所有项目均显示排名1
问题分析
你的calculateRank函数存在核心逻辑缺陷:聚合管道的$match阶段仅筛选了当前项目的投票数据,导致最终聚合结果里只有这一个项目。排序后它必然是第1位,所以无论该项目得票多少,返回的rank永远是1——你根本没把这个项目和其他项目的赞成票做对比。
解决方案
1. 修正排名计算逻辑
修改calculateRank函数,统计所有项目的总赞成票,再按票数降序排序,最后计算每个项目的排名(支持同票同排名的标准规则):
async function calculateRank(userVotesCollection, projectId) { const rankData = await userVotesCollection.aggregate([ // 筛选所有赞成票数据 { $match: { vote_type: "up" } }, // 按项目分组,统计每个项目的总赞成票 { $group: { _id: "$project_id", totalUpVotes: { $sum: 1 } } }, // 按赞成票数量降序排序 { $sort: { totalUpVotes: -1 } }, // 使用窗口函数计算排名,支持同票同排名 { $setWindowFields: { sortBy: { totalUpVotes: -1 }, output: { rank: { $rank: {} } } } } ]).toArray(); if (rankData.length === 0) { return null; } // 定位当前项目的排名 const projectRank = rankData.find(doc => doc._id.toString() === projectId); return projectRank ? projectRank.rank : null; }
2. 调整投票更新流程
原代码先计算排名再插入新投票,导致用旧数据计算排名。需要把插入投票的逻辑移到计算排名之前:
// 先保存新投票记录 const insertResult = await votesCollection.insertOne(newUserVote); if (insertResult.insertedCount !== 1) { return res.status(500).json({ error: 'Failed to save vote' }); } // 针对up类型投票,用最新数据计算并更新排名 if (vote_type === 'up') { const rank = await calculateRank(votesCollection, project_id); if (rank !== null) { await projectCollection.updateOne( { _id: new ObjectId(project_id) }, { $set: { project_rank: rank } } ); } }
3. 补充其他高权重投票的排名更新
如果10-up、20-up这类投票也需要触发排名更新,需要在对应的分支里添加相同的排名计算逻辑。
关键说明
- 使用
$setWindowFields+$rank操作符可以高效实现标准排名规则,避免手动遍历排序的误差和性能问题。 - 必须先插入新投票再计算排名,确保基于最新的投票数据统计相对排名。
- 聚合时不能限制单个项目,必须统计所有项目的票数才能得到正确的排名顺序。
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

