
本文详解 Go 中使用 mgo 驱动更新 MongoDB 文档时的关键要点:结构体 BSON 标签映射、UpdateId 与 $set 的配合使用,以及查询条件必须匹配数据库字段名(如 _id 而非 Id),避免因字段名不一致导致更新失败或查询为空。
本文详解 go 中使用 mgo 驱动更新 mongodb 文档时的关键要点:结构体 bson 标签映射、`updateid` 与 `$set` 的配合使用,以及查询条件必须匹配数据库字段名(如 `_id` 而非 `id`),避免因字段名不一致导致更新失败或查询为空。
在 Go 中通过 mgo(v2)驱动执行 MongoDB 更新操作时,一个常见却隐蔽的错误是混淆了 Go 结构体字段名与 BSON 序列化后的实际字段名。这直接导致 Update 或 UpdateId 表面成功但数据未变更,或后续 Find 查询返回空结果。
核心问题在于:
✅ 结构体字段 Id 通过 bson:"_id" 正确映射到 MongoDB 的 _id 字段;
❌ 但在查询时误用 bson.M{"Id": obj1.Id} —— MongoDB 并不存在名为 Id 的字段,它只认 _id;
❌ 同样,若错误地将整个结构体指针(如 &obj1)直接传给 $set,mgo 会尝试将 Id 字段也写入 $set,而 _id 是不可变字段,虽不报错但可能干扰预期行为(尽管 UpdateId 本身已隐式指定 _id,$set 中包含 _id 会被忽略)。
✅ 正确做法如下:
- 使用 col.UpdateId(obj1.Id, bson.M{"$set": &obj1}) —— UpdateId 自动按 _id 匹配文档,无需手动构造查询条件;
- $set 的值应为结构体指针(&obj1),mgo 会依据结构体的 bson 标签自动提取需更新的字段(如 fruit),并忽略 _id(因 _id 不可修改);
- 后续查询必须使用 bson.M{"_id": obj1.Id},严格匹配数据库存储的字段名。
以下是修正后的完整示例:
package testmgo
import (
"testing"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type MyStruct struct {
Id bson.ObjectId `bson:"_id"` // Go 字段 Id → MongoDB _id
Fruit string `bson:"fruit"` // Go 字段 Fruit → MongoDB fruit
}
func TestUpdate(t *testing.T) {
obj1 := MyStruct{Id: bson.NewObjectId(), Fruit: "apple"}
var obj2 MyStruct
session, err := mgo.Dial("localhost")
if err != nil {
t.Fatal(err)
}
defer session.Close()
col := session.DB("test").C("collection")
// 插入原始文档
if err := col.Insert(&obj1); err != nil {
t.Fatal(err)
}
// 修改 Go 对象
obj1.Fruit = "cherry"
// ✅ 正确:UpdateId 按 _id 更新,$set 引用结构体指针,自动映射字段
if err := col.UpdateId(obj1.Id, bson.M{"$set": &obj1}); err != nil {
t.Errorf("Update failed: %v", err)
return
}
// ✅ 正确:查询时使用 "_id"(而非 "Id")作为键
if err := col.Find(bson.M{"_id": obj1.Id}).One(&obj2); err != nil {
t.Errorf("Find failed: %v", err)
return
}
if obj1.Fruit != obj2.Fruit {
t.Errorf("Expected %s, got %s", obj1.Fruit, obj2.Fruit)
}
}⚠️ 注意事项:
- mgo 已归档,生产环境推荐迁移到官方 MongoDB Go Driver,其 API 更现代、类型安全更强(如使用 primitive.ObjectID 和 bson.D);
- 若需部分更新(仅改特定字段),可显式构造 $set 映射(如 bson.M{"fruit": "cherry"}),避免序列化整个结构体;
- UpdateId 是语义清晰的安全选择,比 Update(bson.M{"_id": id}, ...) 更不易出错;
- 始终检查 session.Dial 和 col.Insert/Update/Find 的错误返回,避免静默失败。
总结:Go 与 MongoDB 交互的本质是 BSON 序列化协议对齐。务必确保结构体标签(bson:"xxx")、查询条件键名、更新操作目标字段三者完全一致,才能实现可靠、高效的文档更新。

















