必须用[]bson.D或[]bson.M传聚合管道,禁用JSON字符串;优先选bson.D保障顺序;时间字段须为time.Time;游标需循环Next+Decode并检查Err();上下文必须带超时且贯穿全程;分片集群不支持Slot-based引擎。

Aggregate() 必须传 []bson.D 或 []bson.M,不能用 JSON 字符串
Go 官方驱动(mongo-go-driver)对聚合管道类型校验极严:传 string、map[string]interface{} 或裸 JSON 会编译通过,但运行时要么返回空结果,要么 panic:invalid pipeline operator。mgo 已停更,在 MongoDB 7.0 上静默失效(比如 $facet 不触发、连接串参数丢失),必须切换。
推荐用 bson.D——它是有序切片,适合含多个同名阶段(如连续两个 $addFields)或依赖顺序的场景($lookup 后紧跟 $unwind)。bson.M 写起来快,但字段顺序不保证,旧版 MongoDB 对 $group._id 的键序敏感,可能出错。
-
bson.D正确写法:[]bson.D{{{ "$match", bson.M{"status": "active"} }}, {{ "$group", bson.M{"_id": "$category", "count": bson.M{"$sum": 1}} }}} - 错误写法:
bson.D{{"$match", ...}, {"$group", ...}}(没包成独立bson.D元素) - 时间字段必须是
time.Time类型;传int64时间戳会静默失败,需显式转:bson.M{"$gte": start, "$lt": end},其中start/end是time.Time
游标分批读取必须用 cursor.Next() + cursor.Decode(),别碰 All()
副本集上聚合结果量大时,cursor.All() 会一次性把全部数据拉进内存,OOM 风险高,且无法控制每批大小。MongoDB 7.0 的 getMore 命令默认按 16 MiB 分批,但 Go 驱动不暴露 batchSize 参数,得靠循环控制节奏。
- 每次
cursor.Next(ctx)后必须立即调用cursor.Decode(&one),不能复用同一变量地址解多条(后 decode 会覆盖前值) - 循环结束时,
cursor.Next()返回false不代表成功读完,必须检查cursor.Err()——超时、网络断连、mongos 路由失败都会在这里暴露 - 别省略
cursor.Close(ctx),否则连接泄漏,副本集节点连接数缓慢上涨直至拒绝新请求
副本集上下文必须带超时,且 context 要贯穿全程
副本集天然存在节点间同步延迟,一次聚合可能跨 Primary 和 Secondary 读取。若不设超时,Aggregate() 卡在慢节点上会永久阻塞,或因心跳超时被 mongod 主动 kill,但 Go 驱动不会主动报错。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 始终用
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second),再传给collection.Aggregate(ctx, pipeline) - 后续所有操作(
Next()、Decode()、Close())都复用这个ctx,不能换新的 - 如果聚合涉及
$lookup到其他集合,且目标集合也在副本集上,超时要预留双倍余量(主从延迟 + 网络 RTT)
分片集群下别信 Slot-based 引擎,副本集才真正受益
MongoDB 7.0 的 Slot-based 查询执行引擎仅在单一分片(即未分片集合或副本集)上启用。如果你的集合已分片,无论聚合写得多规范,explain 输出里只会看到 "stage": "SHARD_MERGE",引擎自动回退到经典模式——这不是配置问题,是设计限制。
这意味着:副本集上的聚合才能真正用上 Slot-based 加速(如 $group、$lookup 的向量化执行),而分片集群用户得专注优化 mongos 层行为:确保 $match 尽量下推到 shard、避免跨分片 $sort、用 maxTimeMS 控制单 shard 超时。
验证是否命中 Slot-based:跑 explain("executionStats"),只看 "executionStages" 里有没有 "stage": "SLOT_BASED_EXECUTION"。没有?说明你在副本集上也没触发——大概率是聚合阶段用了不支持的操作符(如 $merge)或驱动版本太低。

















