InsertMany() 不自动填充原始结构体的 _id 字段,仅通过返回值 InsertManyResult.InsertedIDs 按输入顺序提供 ID 列表;需手动赋值或索引映射,且错误需检查 result.Err() 而非仅 err。

直接用 InsertMany(),但必须手动处理 _id、显式检查错误、控制批次大小——它不自动填充 ID,也不返回每条记录的插入结果。
为什么 InsertMany() 插完却拿不到新生成的 _id?
因为 InsertMany() 默认不修改你传入的原始结构体,只在返回值里提供一个 mongo.InsertManyResult,其中只有 InsertedIDs 切片(按顺序对应输入切片索引)。如果你没在结构体里定义 ID primitive.ObjectID `bson:"_id,omitempty"` 字段,插入后根本无法反向关联哪条数据对应哪个 _id。
- 正确做法:插入前手动赋值
doc.ID = primitive.NewObjectID(),再传给InsertMany() - 或者插入后用返回的
result.InsertedIDs按索引映射回原始数据 - 别依赖结构体字段“自动被填”,驱动不会写回原变量
InsertMany() 的错误怎么判断才不漏掉?
InsertMany() 返回 (result *mongo.InsertManyResult, err error),但 err 只表示“整个操作失败”(如网络中断、权限拒绝),不表示某几条写入失败。MongoDB 默认是 ordered 模式:一旦某条出错,后续全部跳过,且 err != nil;但如果想部分成功,得设 options.InsertMany().SetOrdered(false),这时 err 会是 nil,但你要检查 result.Err() 和每个 result.InsertedIDs 的完整性。
- 永远不要只判
if err != nil就认为全失败 - 启用 unordered 后,必须调用
result.Err()获取具体哪条失败(返回*mongo.BulkWriteException) - 即使
err == nil,也要确认len(result.InsertedIDs) == len(input),否则有静默丢数据风险
大批量插入时,context 超时和连接 timeout 有什么区别?
连接字符串里的 timeout(如 connectTimeoutMS=5000)只管初始建连;而 InsertMany() 这类操作级超时,必须靠带 deadline 的 context 控制,否则网络卡住或副本集选举时会无限等待。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)包裹操作 - 操作完成后立刻
cancel(),防 goroutine 泄漏 - 错误信息出现
context deadline exceeded是正常现象,说明超时生效了 - 别复用连接时的 ctx,读写操作应各自配独立超时
用 primitive.M 还是 map[string]interface{} 构造文档?
必须用 primitive.M。虽然它底层是 map[string]interface{} 的类型别名,但驱动内部对 primitive.M 做了 BSON 编码定制:保证字段顺序、正确处理 nil、时间/二进制类型不丢失精度。混用会导致写入字段消失、time.Time 变成零值、甚至触发 WriteException。
- 所有 filter、update、insert 文档一律写成
primitive.M{"name": "alice", "age": 30} - 结构体字段要映射到 BSON,必须加
bson:"name"tag - 插入含
time.Time的结构体时,字段类型必须是time.Time,不能是string
最容易被忽略的是:批量插入不是“把数据扔进去就完事”,而是要自己闭环 ID 关联、错误粒度控制、context 生命周期管理——驱动只做协议层转发,不替你做业务逻辑兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










