使用 mgo 强制执行类型映射
Enforce a type mapping with mgo
当 _id 成员的类型仅派生自 bson.ObjectId:
时,它不再映射到 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)
}
_id 应该是 ObjectId in Mongo。
但事实证明 string 被选中:
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 !
这可能是有意为之,因为 bson.ObjectId 本身是从 string 派生的。但是在这里,这对我们不利。
我们可以告诉 mgo 将 _id 映射到数据库中的 ObjectId 吗?
当你这样做时:
type CustomId bson.ObjectId
您正在创建一个新类型,mgo
包将不再将其视为/识别为 bson.ObjectId
(类型 bson.ObjectId
在 bson 包中是 "hardcoded" ).新类型将有 0 个方法。
我会坚持bson.ObjectId
。但是,如果您仍然想要自定义 ID 类型,则可以在创建 CustomId
时使用嵌入:嵌入类型 bson.ObjectId
的值,并使用 inline
bson 标志作为 ID1
字段:
type CustomId struct {
bson.ObjectId `bson:"_id"`
}
type Foo struct {
ID1 CustomId `bson:",inline"`
ID2 bson.ObjectId
}
使用它:
doc := Foo{
CustomId{bson.NewObjectId()},
bson.NewObjectId(),
}
这样做的好处是 CustomId
将拥有 bson.ObjectId
拥有的所有方法,并且您可以添加新方法和 "override" 现有方法。
另一种选择是为您的 CustomId
使用接口类型(例如 interface{}
),使用它会很多 "simpler":
type CustomId interface{}
type Foo struct {
ID1 CustomId `bson:"_id"`
ID2 bson.ObjectId // mapped as expected
}
使用它:
doc := Foo{
bson.NewObjectId(),
bson.NewObjectId(),
}
当然,沿着这条路走下去,你必须使用 type assertion,如果你需要访问 CustomId
的包装 bson.ObjectId
。
使用Setter and Getter接口来控制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
}
当 _id 成员的类型仅派生自 bson.ObjectId:
时,它不再映射到 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)
}
_id 应该是 ObjectId in Mongo。 但事实证明 string 被选中:
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 !
这可能是有意为之,因为 bson.ObjectId 本身是从 string 派生的。但是在这里,这对我们不利。
我们可以告诉 mgo 将 _id 映射到数据库中的 ObjectId 吗?
当你这样做时:
type CustomId bson.ObjectId
您正在创建一个新类型,mgo
包将不再将其视为/识别为 bson.ObjectId
(类型 bson.ObjectId
在 bson 包中是 "hardcoded" ).新类型将有 0 个方法。
我会坚持bson.ObjectId
。但是,如果您仍然想要自定义 ID 类型,则可以在创建 CustomId
时使用嵌入:嵌入类型 bson.ObjectId
的值,并使用 inline
bson 标志作为 ID1
字段:
type CustomId struct {
bson.ObjectId `bson:"_id"`
}
type Foo struct {
ID1 CustomId `bson:",inline"`
ID2 bson.ObjectId
}
使用它:
doc := Foo{
CustomId{bson.NewObjectId()},
bson.NewObjectId(),
}
这样做的好处是 CustomId
将拥有 bson.ObjectId
拥有的所有方法,并且您可以添加新方法和 "override" 现有方法。
另一种选择是为您的 CustomId
使用接口类型(例如 interface{}
),使用它会很多 "simpler":
type CustomId interface{}
type Foo struct {
ID1 CustomId `bson:"_id"`
ID2 bson.ObjectId // mapped as expected
}
使用它:
doc := Foo{
bson.NewObjectId(),
bson.NewObjectId(),
}
当然,沿着这条路走下去,你必须使用 type assertion,如果你需要访问 CustomId
的包装 bson.ObjectId
。
使用Setter and Getter接口来控制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
}