我正在开发一个结合使用 Go 和 MongoDB 的项目。我被困在一个我有一个结构的地方:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
在这个结构中,字段Id
是int
类型。但正如我们所知,MongoDB 的默认 id 是 bsonObject
类型。有时,系统会在 Id
字段中生成默认的 MongoDB id。
为了克服这个问题,我修改了这样的结构:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
BsonId bson.ObjectId `json:"bson_id" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
在上面的结构中,我在两个不同的结构字段 Id(类型 int)和 BsonId
(类型 bson.ObjectId
)。我希望如果出现整数类型的 id,它会映射到 Id
下,否则会映射到 BsonId
下。
但是这个东西给出了以下错误:
Duplicated key '_id' in struct models.Booking
我怎样才能用 Go Structs 实现这种类型的东西??
更新:
这是我为自定义编码/解码编写的代码:
func (booking *Booking) SetBSON(raw bson.Raw) (err error) {
type bsonBooking Booking
if err = raw.Unmarshal((*bsonBooking)(booking)); err != nil {
return
}
booking.BsonId, err = booking.Id
return
}
func (booking *Booking) GetBSON() (interface{}, error) {
booking.Id = Booking.BsonId
type bsonBooking *Booking
return bsonBooking(booking), nil
}
但这会导致 Id 和 BsonId 字段出现类型不匹配错误。我现在该怎么办?
最佳答案
在您的原始结构中,您将此标记用于 Id
领域:
bson:"_id,omitempty"
这意味着如果 Id
的值字段是 0
(zero value 对于 int
类型),则该字段将不会发送到 MongoDB。但是 _id
属性在 MongoDB 中是强制性的,因此在这种情况下,MongoDB 服务器将生成一个 ObjectId
。
要克服这个问题,最简单的方法是确保 Id
will 总是非零的;或者如果 0
是一个有效的 ID,删除 omitempty
标签中的选项。
如果您的集合必须允许混合类型的 ID(int
和 ObjectId
),那么最简单的方法是定义 Id
类型为 interface{}
的字段因此它可以容纳两种(实际上是所有)类型的键值:
Id interface{} `json:"_id,omitempty" bson:"_id,omitempty"`
是的,使用它可能有点麻烦(例如,如果您明确需要 ID 作为 int
,则需要使用 type assertion),但这会解决您的问题。
如果您确实需要 2 个 ID 字段,其中一个带有 int
类型和另一个 ObjectId
类型,那么您唯一的选择就是实现自定义 BSON 编码(marshal)处理和解封处理。这基本上意味着实现 bson.Getter
和/或 bson.Setter
接口(interface)(每个方法一个)在你的结构类型上,你可以在其中做任何你喜欢的事情来填充你的结构或组装要实际保存/插入的数据。有关详细信息和示例,请参阅 Accessing MongoDB from Go .
这是一个使用自定义编码的示例:
省略 Id
和 BsonId
来自编码的字段(使用 bson:"-"
标签),并添加第三个“临时”id 字段:
type Booking struct {
Id int `bson:"-"`
BsonId bson.ObjectId `bson:"-"`
TempId interface{} `bson:"_id"`
// rest of your fields...
}
所以无论您在 MongoDB 中有什么 ID,它最终都会在 TempId
中, 只有这个 id 字段会被发送并保存在 MongoDB 的 _id
中属性(property)。
使用 GetBSON()
设置方法 TempId
在保存/插入结构值之前从其他 id 字段(以设置者为准),并使用 SetBSON()
“复制”的方法TempId
从 MongoDB 检索文档后,根据其动态类型将其值赋给其他 id 字段之一:
func (b *Booking) GetBSON() (interface{}, error) {
if b.Id != 0 {
b.TempId = b.Id
} else {
b.TempId = b.BsonId
}
return b, nil
}
func (b *Booking) SetBSON(raw bson.Raw) (err error) {
if err = raw.Unmarshal(b); err != nil {
return
}
if intId, ok := b.TempId.(int); ok {
b.Id = intId
} else bsonId, ok := b.TempId.(bson.ObjectId); ok {
b.BsonId = bsonId
} else {
err = errors.New("invalid or missing id")
}
return
}
注意:如果您不喜欢 TempId
在你的字段 Booking
struct,您可以创建 Booking
的副本(例如 tempBooking
),并且只添加 TempId
进去,然后使用 tempBooking
用于编码/解码。您可以使用嵌入( tempBooking
可以嵌入 Booking
)这样您甚至可以避免重复。
关于mongodb - 在 Golang 的两个不同结构字段中映射 Mongo _id,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51980202/