应先用 Match() 过滤再 Group() 聚合,_id 必须显式指定,聚合字段用 $sum、$addToSet 等操作符封装在 BsonDocument 中,结果映射需 As() 且字段名与 DTO 属性严格匹配。

怎么用 Aggregate() 写 Group + Match 的基础聚合
直接调用集合的 Aggregate() 方法,传入 BsonDocument 数组或类型安全的 AggregateFluent 链式调用。别先写 Group() —— MongoDB 要求管道顺序:Match 尽量放前面过滤数据,再 Group,否则性能会明显变差。
- 用
Match()先筛出有效文档(比如{ status: "active" }),再Group()统计,比反过来快几倍甚至更多 -
Group()的_id字段必须显式指定,不能省略;想按多个字段分组就用new BsonDocument { { "type", "$type" }, { "region", "$region" } } - 聚合结果默认是
BsonDocument,如果要映射到 C# 类,得用As<t>()</t>,且类属性名需严格匹配输出字段名(比如Count对应$sum: 1输出的"count")
Group() 里怎么写计数、求和、去重统计
别硬套 SQL 思维——MongoDB 的 $sum、$avg、$addToSet 都是表达式操作符,必须包在 BsonDocument 里传给 Group() 的第二个参数(即聚合字段定义)。
- 计数用
$sum: 1,不是$count(那是单独的聚合阶段) - 求和字段值:写成
new BsonDocument("$sum", "$amount"),注意引号里的$amount是字段路径,不是字符串字面量 - 去重统计数量:先
$addToSet: "$category"收集唯一值,再用$size算长度,不能直接$count去重 - 空值处理:字段可能为
null,$sum会跳过,但$avg也会跳过,这点和 SQL 不同
为什么 ToListAsync() 报错 “Invalid cast from BsonDocument to xxx”
这是最常踩的坑:你用了 As<mydto>()</mydto>,但聚合输出字段名和 MyDto 属性名不一致,或者类型不匹配(比如服务端返回 Int32,DTO 里写了 long)。
- 检查聚合结果里实际字段名:用
FirstOrDefaultAsync()拿一个BsonDocument打印出来看,确认是"total"还是"sum_amount" -
[BsonElement("total")]必须加在 DTO 属性上,否则驱动不会做字段映射 - 避免用匿名类型接收结果——虽然编译通过,但序列化/反序列化容易崩,尤其涉及嵌套或 null 值时
- 如果只是临时查数,用
await cursor.ToListAsync()接BsonDocument最稳,别强转
复杂管道里怎么调试每一步输出
MongoDB Driver 不支持像 Studio 3T 那样“断点看中间结果”,但你可以把管道拆成多步,用 ToListAsync() 分别执行前 N 个阶段来验证。
- 例如:先只跑
Match()+Project(),确认筛选和投影字段正确;再加Group() - 用
Lookup()关联集合时,注意被关联集合的索引是否建好,否则整个聚合会极慢,错误日志里却只显示超时 - 聚合结果超过 16MB 会报
CursorNotFound,这时得加AllowDiskUse(true),但要注意服务器磁盘权限和性能影响 - 本地开发用
mongosh测试好管道逻辑,再粘贴进 C#,别在代码里盲调
聚合真正难的不是语法,是理解每个阶段的数据形状变化——上一阶段输出什么结构,决定了下一阶段怎么写字段路径。字段名拼错一个字母、少写一个 $、没处理 null,都会让结果静默异常。


















