dougang6821 2018-08-23 07:12
浏览 330

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

I am working on a project that uses combination of Go and MongoDB. I am stuck at a place where I have a struct like:

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"`
    }

In this struct, the field Id is of int type. But as we know MongoDB's default id is of bsonObject type. Some times, the system generates the default MongoDB id in Id field.

To overcome this I have modified the struct like this:

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"`
        }

In above struct, I have mapped the same _id field in two different struct fields Id (type int) and BsonId (type bson.ObjectId). I want if id of integer type comes, it maps under Id otherwise under BsonId.

But this thing is giving following error:

Duplicated key '_id' in struct models.Booking

How can I implement this type of thing with Go Structs ??

Update:

Here is the code I have written for custom marshaling/unmarshaling:

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
}

But this is giving type mismatch error of Id and BsonId fields. What should I do now ?

  • 写回答

1条回答

  • dongshan1036 2018-08-23 07:24
    关注

    In your original struct you used this tag for the Id field:

    bson:"_id,omitempty"
    

    This means if the value of the Id field is 0 (zero value for the int type), then that field will not be sent to MongoDB. But the _id property is mandatory in MongoDB, so in this case the MongoDB server will generate an ObjectId for it.

    To overcome this, the easiest is to ensure the Id will is always non-zero; or if the 0 is a valid id, remove the omitempty option from the tag.

    If your collection must allow ids of mixed type (int and ObjectId), then the easiest would be to define the Id field with type of interface{} so it can accomodate both (actually all) types of key values:

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

    Yes, working with this may be a little more cumbersome (e.g. if you explicitly need the id as int, you need to use type assertion), but this will solve your issue.

    If you do need 2 id fields, one with int type and another with ObjectId type, then your only option will be to implement custom BSON marshaling and unmarshaling. This basically means to implement the bson.Getter and / or bson.Setter interfaces (one method for each) on your struct type, in which you may do anything you like to populate your struct or assemble the data to be actually saved / inserted. For details and example, see Accessing MongoDB from Go.

    Here's an example how using custom marshaling it may look like:

    Leave out the Id and BsonId fields from marshaling (using the bson:"-" tag), and add a 3rd, "temporary" id field:

    type Booking struct {
            Id     int           `bson:"-"`
            BsonId bson.ObjectId `bson:"-"`
            TempId interface{}   `bson:"_id"`
            // rest of your fields...
    }
    

    So whatever id you have in your MongoDB, it will end up in TempId, and only this id field will be sent and saved in MongoDB's _id property.

    Use the GetBSON() method to set TempId from the other id fields (whichever is set) before your struct value gets saved / inserted, and use SetBSON() method to "copy" TempId's value to one of the other id fields based on its dynamic type after the document is retrieved from MongoDB:

    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
    }
    

    Note: if you dislike the TempId field in your Booking struct, you may create a copy of Booking (e.g. tempBooking), and only add TempId into that, and use tempBooking for marshaling / unmarshaling. You may use embedding (tempBooking may embed Booking) so you can even avoid repetition.

    评论

报告相同问题?

悬赏问题

  • ¥15 求差集那个函数有问题,有无佬可以解决
  • ¥15 【提问】基于Invest的水源涵养
  • ¥20 微信网友居然可以通过vx号找到我绑的手机号
  • ¥15 寻一个支付宝扫码远程授权登录的软件助手app
  • ¥15 解riccati方程组
  • ¥15 display:none;样式在嵌套结构中的已设置了display样式的元素上不起作用?
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名
  • ¥65 汇编语言除法溢出问题