使用mgo强制进行类型映射。

huangapple go评论101阅读模式
英文:

Enforce a type mapping with mgo

问题

_id成员不再映射到ObjectId类型,当其类型仅从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)
}

_id应该在Mongo中是一个ObjectId。但事实证明,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吗?

英文:

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

得分: 3

使用SetterGetter接口来控制在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
}
英文:

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

得分: 2

当你这样做时:

type CustomId bson.ObjectId

你创建了一个新的类型,mgo包将不再将其识别为bson.ObjectIdbson.ObjectId类型在bson包中是“硬编码”的)。这个新类型将没有任何方法。

我建议你仍然使用bson.ObjectId。但是如果你仍然想要一个自定义的ID类型,你可以在创建CustomId时使用嵌入:嵌入一个类型为bson.ObjectId的值,并为ID1字段使用inline bson标记:

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的所有方法,你还可以添加新的方法并“覆盖”现有的方法。

另一种选择是为你的CustomId使用接口类型(例如interface{}),这样使用起来会更加“简单”:

type CustomId interface{}

type Foo struct {
    ID1 CustomId      `bson:"_id"`
    ID2 bson.ObjectId // 正常映射
}

使用它:

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

当然,如果你需要访问CustomId的包装bson.ObjectId,你需要使用类型断言

英文:

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.

huangapple
  • 本文由 发表于 2017年1月12日 00:11:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/41595577.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定