【问题标题】:Map Mongo _id in two different struct fields in Golang在 Golang 的两个不同结构字段中映射 Mongo _id
【发布时间】:2018-08-23 07:12:20
【问题描述】:

我正在开发一个结合使用 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(int 类型)和 BsonIdbson.ObjectId)中映射了相同的 _id 字段。我想如果整数类型的 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 字段的类型不匹配错误。我现在该怎么办?

【问题讨论】:

    标签: mongodb go struct mgo


    【解决方案1】:

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

    bson:"_id,omitempty"
    

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

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

    如果您的集合必须允许混合类型的 id(intObjectId),那么最简单的方法是定义类型为 interface{}Id 字段,以便它可以容纳两种(实际上是所有)类型关键值:

    Id interface{} `json:"_id,omitempty" bson:"_id,omitempty"`
    

    是的,使用它可能会有点麻烦(例如,如果您明确需要 id 作为int,则需要使用type assertion),但这将解决您的问题。

    如果您确实需要 2 个 id 字段,一个具有 int 类型,另一个具有 ObjectId 类型,那么您唯一的选择是实现自定义 BSON 编组和解组。这基本上意味着在您的结构类型上实现bson.Getter 和/或bson.Setter 接口(每个接口一个方法),您可以在其中执行任何您喜欢的操作来填充您的结构或组装要实际保存/插入的数据。有关详细信息和示例,请参阅Accessing MongoDB from Go

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

    从封送处理中省略IdBsonId 字段(使用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 属性中。

    在保存/插入结构值之前,使用GetBSON() 方法从其他id 字段(以设置者为准)设置TempId,并使用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
    }
    

    注意:如果您不喜欢 Booking 结构中的 TempId 字段,您可以创建一个 Booking 的副本(例如 tempBooking),然后只在其中添加 TempId,然后使用 tempBooking用于编组/解组。您可以使用嵌入(tempBooking 可以嵌入Booking),这样您甚至可以避免重复。

    【讨论】:

    • 那么我应该在哪里调用 SetBson() 或 GetBson() ??
    • @Amandeepkaur 你没有。当你的结构的值需要被编组或解组时,它由mgo 包调用。你只需描述它应该如何编组或解组,其余的由mgo完成。
    • 你的意思是我只需要定义两个函数 GetBson() 和 SetBson() 然后剩下的工作将由 mgo 完成?
    • @Amandeepkaur 这正是我要说的。您负责在实施这些方法时如何以及如何编组和解组数据。
    • 我通过参考stackoverflow.com/questions/42342943/accessing-mongodb-from-go/… 编写了 SetBson() 和 GetBson() 函数。我已经为 Id 和 BsonId 字段编写了函数。但这给了我类型不匹配错误:不能使用 booking.BsonId (type bson.ObjectId) as type int in assignment
    猜你喜欢
    • 1970-01-01
    • 2015-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-06
    • 1970-01-01
    • 2016-01-21
    相关资源
    最近更新 更多