问题
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 ?
回答1:
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
}
回答2:
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
.
来源:https://stackoverflow.com/questions/41595577/enforce-a-type-mapping-with-mgo