在Golang中的两个不同结构字段中映射Mongo _id

时间:2018-08-23 07:12:20

标签: mongodb go struct mgo

我正在研究一个使用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"`
    }

在此结构中,字段Idint类型。但是我们知道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字段的类型不匹配错误。我现在该怎么办?

1 个答案:

答案 0 :(得分:3)

在原始结构中,您将此标签用于Id字段:

bson:"_id,omitempty"

这意味着如果Id字段的值为0int类型为zero value),则该字段将不会发送到MongoDB。但是_id属性在MongoDB中是必需的,因此在这种情况下,MongoDB服务器将为其生成一个ObjectId

要克服此问题,最简单的方法是确保Id始终为非零;或者如果0是有效的ID,请从标记中删除omitempty选项。

如果您的集合必须允许使用混合类型的ID(intObjectId),那么最简单的方法就是定义类型为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

下面是一个使用自定义封送处理的示例:

从封送处理中排除ObjectIdId字段(使用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),这样甚至可以避免重复。