C#操作MongoDB查询考生成绩时如何获取其按总分排序的排名
解决方案
实现思路
- 首先需要先在
ExamCandidateResult实体类中新增Rank属性,用于接收计算得到的排名值 - 采用MongoDB聚合管道实现排名计算:先过滤指定考试的全部考生、按总分降序排序、用窗口函数计算排名、最后匹配目标考生记录即可同时拿到成绩和排名
代码修改(适配MongoDB 5.0+版本,推荐)
MongoDB 5.0以上版本支持$setWindowFields窗口函数,可直接在数据库侧高效计算排名:
// 先给ExamCandidateResult类新增Rank属性 public class ExamCandidateResult { // 原有其他属性保持不变 public int Rank { get; set; } }
public ExamCandidateResult ExaminationGetCandidateResultStatus(Guid examinationId, Guid candidateId) { var con = new MongoClient(DBConnection.ExamConnectionString); var db = con.GetDatabase(ExamDB); var collection = db.GetCollection<ExamCandidateResult>("Examination"); var examIdStr = examinationId.ToString(); var candidateIdStr = candidateId.ToString(); // 聚合管道计算排名 var data = collection.Aggregate() // 第一步:过滤指定考试的所有考生 .Match(Builders<ExamCandidateResult>.Filter.Eq("ExaminationID", examIdStr)) // 第二步:按总分降序排序,同分可自定义排序规则,比如按交卷时间升序 .SortByDescending(x => x.TotalMarks) // 第三步:窗口函数计算排名 .AppendStage<ExamCandidateResult>(new BsonDocument("$setWindowFields", new BsonDocument { { "sortBy", new BsonDocument("TotalMarks", -1) }, { "output", new BsonDocument("Rank", new BsonDocument("$rank", new BsonDocument())) } })) // 第四步:过滤出目标考生 .Match(Builders<ExamCandidateResult>.Filter.Eq("CandidateID", candidateIdStr)) .FirstOrDefault(); // 若需要使用同分连续排名,把上面的$rank替换为$denseRank即可 return data; }
低版本MongoDB兼容方案(<5.0版本)
如果你的MongoDB版本不支持窗口函数,可以用两次查询的方式实现:
public ExamCandidateResult ExaminationGetCandidateResultStatus(Guid examinationId, Guid candidateId) { var con = new MongoClient(DBConnection.ExamConnectionString); var db = con.GetDatabase(ExamDB); var collection = db.GetCollection<ExamCandidateResult>("Examination"); var examIdStr = examinationId.ToString(); var candidateIdStr = candidateId.ToString(); // 先查询目标考生的成绩 var filter = Builders<ExamCandidateResult>.Filter.Eq("ExaminationID", examIdStr) & Builders<ExamCandidateResult>.Filter.Eq("CandidateID", candidateIdStr); var candidateData = collection.Find(filter).FirstOrDefault(); if (candidateData == null) return null; // 统计同考试下总分高于当前考生的人数,+1就是当前考生的排名 var higherScoreCountFilter = Builders<ExamCandidateResult>.Filter.Eq("ExaminationID", examIdStr) & Builders<ExamCandidateResult>.Filter.Gt(x => x.TotalMarks, candidateData.TotalMarks); var higherCount = collection.CountDocuments(higherScoreCountFilter); candidateData.Rank = (int)higherCount + 1; // 若需要同分按其他维度排序,可在统计条件里额外加规则 return candidateData; }
注意事项
$rank规则:同分考生排名相同,后续排名会跳过重复位数,比如2个并列第1,下一名排名为3$denseRank规则:同分考生排名相同,后续排名连续,比如2个并列第1,下一名排名为2- 低版本方案在考生数据量极大的场景下性能略低于窗口函数方案,有条件建议升级MongoDB到5.0以上版本
内容的提问来源于stack exchange,提问作者Aneesh S
相关产品推荐
相关产品推荐

