在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个回答

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.

drgawfsf1069
drgawfsf1069 我添加了示例实现方式。
接近 2 年之前 回复
doulan9188
doulan9188 如果与其他模块高度集成,则无法将类型Id字段更改为与之连接的接口。 因此,我决定使用自定义封送/拆组。 我已经更新了有关元帅/非元帅职能的职位。
接近 2 年之前 回复
duankang8114
duankang8114 首先,您应该尝试我给的其他技巧。 如果您必须使用自定义封送处理/取消封送处理:显然,除非您显示代码,否则我将无法进一步为您提供帮助。
接近 2 年之前 回复
du1462
du1462 我已经通过从stackoverflow.com/questions/42342943/accessing-mongodb-from-go/…引用来编写SetBson()和GetBson()函数。 我已经为Id和BsonId字段编写了函数。 但这给了我类型不匹配错误:不能在分配中使用booking.BsonId(类型bson.ObjectId)作为类型int
接近 2 年之前 回复
dongzhong2674
dongzhong2674 我就是这么说的 在这些方法的实现中,您负责如何编组和取消编组数据以及如何编组数据。
接近 2 年之前 回复
dongzhenyin2001
dongzhenyin2001 您的意思是我只需要定义两个函数Ge​​tBson()和SetBson(),然后其余工作将由mgo完成?
接近 2 年之前 回复
dounao5856
dounao5856 你不知道 当需要对结构的值进行封送处理时,由mgo包调用它。 您只需描述应如何封送或取消封送,其余操作由mgo完成。
接近 2 年之前 回复
douzhang3822
douzhang3822 那么我应该在哪里调用SetBson()或GetBson()呢?
接近 2 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问