我正在研究一个使用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
字段映射到两个不同的结构字段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字段的类型不匹配错误。我现在该怎么办?
答案 0 :(得分:3)
在原始结构中,您将此标签用于Id
字段:
bson:"_id,omitempty"
这意味着如果Id
字段的值为0
(int
类型为zero value),则该字段将不会发送到MongoDB。但是_id
属性在MongoDB中是必需的,因此在这种情况下,MongoDB服务器将为其生成一个ObjectId
。
要克服此问题,最简单的方法是确保Id
始终为非零;或者如果0
是有效的ID,请从标记中删除omitempty
选项。
如果您的集合必须允许使用混合类型的ID(int
和ObjectId
),那么最简单的方法就是定义类型为Id
的{{1}}字段可以容纳两种(实际上是所有)类型的键值:
interface{}
是的,使用它可能会比较麻烦(例如,如果您将id明确指定为Id interface{} `json:"_id,omitempty" bson:"_id,omitempty"`
,则需要使用type assertion),但这将解决您的问题。
如果您确实需要2个id字段,其中一个类型为int
,而另一个类型为int
,那么您唯一的选择是实现自定义BSON封送和封送。这基本上意味着要在结构类型上实现bson.Getter
和/或bson.Setter
接口(每个接口一个方法),在其中您可以执行任何您想填充结构或将数据实际组装的操作保存/插入。有关详细信息和示例,请参见Accessing MongoDB from Go。
下面是一个使用自定义封送处理的示例:
从封送处理中排除ObjectId
和Id
字段(使用BsonId
标签),并添加第三个“临时” id字段:
bson:"-"
因此,无论您在MongoDB中拥有什么id,它都将以type Booking struct {
Id int `bson:"-"`
BsonId bson.ObjectId `bson:"-"`
TempId interface{} `bson:"_id"`
// rest of your fields...
}
结尾,并且只有此id字段将被发送并保存在MongoDB的TempId
属性中。
在保存/插入结构值之前,使用_id
方法从其他id字段(无论设置了哪个)中设置GetBSON()
,并使用TempId
方法“复制” {从MongoDB中检索文档后,根据其动态类型,{1}}的值将用于其他id字段之一:
SetBSON()
注意:如果您不喜欢TempId
结构中的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
),而仅添加{{1 }},然后使用Booking
进行封送/拆组。您可以使用嵌入(tempBooking
可以嵌入TempId
),这样甚至可以避免重复。