使用mgo强制进行类型映射

An _id member is not mapped to type ObjectId anymore, when its type is only derived from bson.ObjectId:

import (
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

type CustomId bson.ObjectId

type Foo struct {
    ID1    CustomId `bson:"_id"` // broken
    ID2    bson.ObjectId         // mapped as expected
}


func main() {
    session, _ := mgo.Dial("127.0.0.1")
    coll := session.DB("mgodemo").C("foocoll")

    doc := Foo{
        CustomId(bson.NewObjectId()),
        bson.NewObjectId(),
    }

    coll.Insert(doc)
}

The _id should have been an ObjectId in Mongo. But it turns out that string was choosen:

Mongo Shell:

> db.foocoll.findOne()
{ "_id" : "XvMn]K� �\f:�", "id2" : ObjectId("58764d6e5d4be120fa0c3ab1") }  // id2 is OK ...

> typeof db.foocoll.findOne()._id
string  // OOps. Should be ObjectId !

This may be intended, since bson.ObjectId itself is derived from string. But here, it's bad for us.

Can we tell mgo to map the _id to ObjectId in the Database ?

Use the Setter and Getter interfaces to control the representation in mongo:

type CustomId bson.ObjectId

func (id *CustomId) SetBSON(raw bson.Raw) error {
   var v bson.ObjectId
   err := raw.Unmarshal(&v)
   *id = CustomId(v)
   return err
}
func (id CustomId) GetBSON() (interface{}, error) {
   return bson.ObjectId(id), nil
}

When you do this:

type CustomId bson.ObjectId

You are creating a new type and the mgo package will not see / recognize it as bson.ObjectId anymore (the type bson.ObjectId is "hardcoded" in the bson package). The new type will have 0 methods.

I would just stick to bson.ObjectId. But if you still want a custom ID type, you may use embedding when creating your CustomId: embed a value of type bson.ObjectId, and use the inline bson flag for the ID1 field:

type CustomId struct {
    bson.ObjectId `bson:"_id"`
}

type Foo struct {
    ID1 CustomId      `bson:",inline"`
    ID2 bson.ObjectId 
}

Using it:

doc := Foo{
    CustomId{bson.NewObjectId()},
    bson.NewObjectId(),
}

This has the advantage that CustomId will have all the methods bson.ObjectId has, and you can add new ones and "override" existing methods.

Another option would be to use an interface type (e.g. interface{}) for your CustomId, using it would be a lot "simpler":

type CustomId interface{}

type Foo struct {
    ID1 CustomId      `bson:"_id"`
    ID2 bson.ObjectId // mapped as expected
}

Using it:

doc := Foo{
    bson.NewObjectId(),
    bson.NewObjectId(),
}

Of course, going down this road, you have to use type assertion, should you need to access the wrapped bson.ObjectId of the CustomId.