mongodb连接与操作必须显式设置context超时并正确使用primitive.m和objectid类型:连接用context.withtimeout,查询/写入用独立超时ctx;filter和update需用primitive.m避免类型歧义;_id字段必须为primitive.objectid类型并加bson tag;错误"context deadline exceeded"表示超时生效,非bug。

连接 MongoDB 时 context 超时没设,程序卡死或假成功
Go 的 mongo-driver 所有操作都强依赖 context,不传或传 context.Background() 看似能跑,但网络抖动、副本集选举、DNS 解析失败时会无限等待。官方默认无超时,不是“没超时”,是“超时时间等于 forever”。
- 用
context.WithTimeout()包一层,比如连接阶段设 10 秒:ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) -
client.Connect(ctx)后必须调cancel(),否则 goroutine 泄漏 - 查询类操作(
Find、FindOne)也要带独立context,别复用连接时的 ctx;读超时建议 5 秒起,写操作可略长 - 错误里出现
context deadline exceeded是正常反馈,说明超时机制生效了,不是 bug
mongo.Collection.Find() 返回空结果却没报错,数据其实存在
常见于 filter 构造错误:BSON 字段名拼错、类型不匹配、嵌套路径写法不对。Go 的 map[string]interface{} 或 struct tag 写错一个字母,MongoDB 就安静地返回空游标——它不报错,因为“查不到”在语义上完全合法。
- 检查 filter 是否用了正确字段名,比如结构体字段
UserName对应 BSON 是"user_name"还是"username",看bsontag - 用
primitive.M{"name": "alice"}比map[string]interface{}{"name": "alice"}更安全,避免类型推导歧义 - 执行前先用
fmt.Printf("%+v", filter)看实际生成的 BSON,确认结构符合预期 - 别跳过
err判断:即使Find()不报错,也要用cursor.Next()和cursor.Err()检查迭代过程是否出错
插入文档后 ID 是空字符串,ObjectIdHex 解析失败
新文档插入时,driver 默认自动生成 ObjectID 并写入 _id 字段。但如果结构体里把 _id 定义成 string 类型,driver 不会自动赋值,导致存进去的是空字符串,后续用 primitive.ObjectIDHex() 解析就会 panic:invalid ObjectIdHex。
- 结构体中
_id字段类型必须是primitive.ObjectID,且加bson:"_id,omitempty"tag - 插入前不手动设
_id,让 driver 自动注入;如果要指定,用primitive.NewObjectID()生成 - 从 DB 读出来再传给前端时,记得转成字符串:
doc.ID.Hex(),别直接 JSON 序列化ObjectID(会变成 base64) - 批量插入(
InsertMany)时,每个文档的_id都得是有效ObjectID,不能混 string
更新操作没生效,UpdateOne 返回 matched=0 modified=0
这通常不是 driver 问题,而是 filter 找不到目标文档,或者 update 操作符写错。MongoDB 的 $set、$inc 等必须用 primitive.M 包裹,写成普通 map 或 struct 会导致整个 update 字段被当作文档内容覆盖过去。
立即学习“go语言免费学习笔记(深入)”;
- 更新语句必须用
primitive.M{"$set": primitive.M{"status": "done"}},不能写map[string]interface{}{"status": "done"} - filter 和 update 都要用
primitive.M,尤其注意嵌套字段:更新user.profile.age得写"user.profile.age"字符串路径 - 用
Upsert: true选项前想清楚:它会在没匹配时插入一条新文档,而不是报错 - 调试时打开 MongoDB 日志(
SetLogLevel)或用mongosh手动执行相同 filter,确认数据真实存在且格式一致
cancel(),或 filter 里少个 $,就足够让你对着日志盯半小时。










