如何使用MongoDB C#驱动Aggregate按合同号获取最大处理日期对应文档
C# MongoDB驱动实现「按合同号分组取最新处理记录」查询逻辑
对应目标SQL的内连接子查询逻辑,最适配的实现方式是使用MongoDB聚合管道,整体执行流程和SQL逻辑完全对齐,不存在分组后无法获取完整原始文档字段的问题。
管道阶段与SQL逻辑的对应关系
$match阶段对应SQL的WHERE过滤条件,提前筛除不符合要求的文档,减少后续聚合的计算量$group阶段对应子查询的分组计算逻辑:按ContractNumer分组,统计每个分组内的最大DataProcessing值$lookup阶段对应SQL的INNER JOIN操作:将分组计算得到的「合同号+最大处理时间」结果与原集合关联,匹配同合同号且处理时间等于最大值的原始文档$unwind阶段用于拆解关联得到的文档数组,过滤无匹配结果的条目$project阶段对应SQL的Select字段列表,仅返回要求的5个目标字段
可直接使用的C#实现代码
// 替换为实际获取到的TAB集合实例 var collection = db.GetCollection<BsonDocument>("TAB"); var pipeline = new EmptyPipelineDefinition<BsonDocument>() // 1. 替换为实际业务需要的WHERE过滤条件 .Match(new BsonDocument("Status", 1)) // 2. 按合同号分组,计算每组的最大数据处理时间 .Group( id: new BsonDocument("_id", "$ContractNumer"), groupMembers: new BsonDocument { { "MaxDataProcessing", new BsonDocument("$max", "$DataProcessing") } } ) // 3. 与原集合关联,匹配同合同号的原始文档 .Lookup( foreignCollection: collection, localField: "_id", foreignField: "ContractNumer", @as: "matchedDocs" ) // 4. 拆解关联结果,仅保留处理时间等于组内最大值的文档 .Unwind("$matchedDocs") .Match(new BsonDocument("$expr", new BsonDocument("$eq", new Bson[] { "$matchedDocs.DataProcessing", "$MaxDataProcessing" }))) // 5. 投影返回指定的5个字段 .Project(new BsonDocument { { "ContractNumer", "$matchedDocs.ContractNumer" }, { "SomeField", "$matchedDocs.SomeField" }, { "ValueContract", "$matchedDocs.ValueContract" }, { "DataProcessing", "$matchedDocs.DataProcessing" }, { "Status", "$matchedDocs.Status" } }); // 执行查询获取结果 var queryResult = await pipeline.ToListAsync();
实现注意事项
- 字段拼写适配:原集合的合同号字段为
ContractNumer(拼写缺少末尾的b,并非ContractNumber),编写代码时不要拼错字段名,否则会出现查询无结果的问题 - 日期字段排序正确性:示例中的
DataProcessing是定长格式字符串(从年到毫秒逐位固定长度),字符串大小比较结果与实际时间先后顺序一致,可以直接用于最大值计算;如果存储的日期字符串格式不统一,建议将字段改为BsonDateTime类型存储,避免排序结果错误 - 行为一致性:如果同一个合同号下存在多个相同最大处理时间的文档,会全部返回,与原SQL的执行结果完全一致;如果业务上仅需要返回单条记录,可以在投影前增加
$limit(1)阶段 - 常见误区:不要在
$group阶段直接用$first、$last搭配排序取文档,这种写法如果没有提前按排序字段排序,结果会有随机性,无法保证与原SQL的取数逻辑完全一致 - 性能优化:可以为
ContractNumer、DataProcessing建立联合索引,大幅降低聚合计算的扫描耗时
内容的提问来源于stack exchange,提问作者Marcos Mauri
相关产品推荐
相关产品推荐

