使用mgo强制执行类型映射

时间:2017-01-11 16:11:01

标签: mongodb go bson 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 在蒙戈。 但事实证明选择了 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吗?

2 个答案:

答案 0 :(得分: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
}

答案 1 :(得分:2)

执行此操作时:

type CustomId bson.ObjectId

您正在创建一个新类型,mgo包不会再将其视为/ bson.ObjectId(类型bson.ObjectId是"硬编码"在bson中包)。新类型将有0个方法。

我会坚持bson.ObjectId。但是,如果您仍需要自定义ID类型,则可以在创建CustomId时使用嵌入:嵌入类型为bson.ObjectId的值,并使用inline bson标志用于{{1} } field:

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使用接口类型(例如interface{}),使用它会很多"更简单":

CustomId

使用它:

type CustomId interface{}

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

当然,沿着这条路走下去,如果您需要访问doc := Foo{ bson.NewObjectId(), bson.NewObjectId(), } 的包裹bson.ObjectId,则必须使用type assertion