使用mongodb go驱动程序将文档解码为具有自定义类型字段的结构

时间:2018-08-26 08:30:10

标签: mongodb go unmarshalling bson mongo-go

我是go和mongodb的初学者。 我尝试使用bson标记将DocumentResult解码为结构,但不适用于包裹字符串的自定义类型。 可以在不将字段类型更改为字符串的情况下完成此操作吗?

    import (
    "context"
    "github.com/mongodb/mongo-go-driver/mongo"
)

type MyDoc struct {
    SomeInt int `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType MyType `bson:"custom_type,omitempty"`
}

type MyType string

const myType MyType = "ABCD"

func main() {

    //Connect to db
    client, _ := mongo.Connect(context.Background(), "mongodb://localhost:27017", nil)
    db := client.Database("example_db")
    collection := db.Collection("col")

    //Insert document
    docToInsert := MyDoc{42, "The Answer", myType}
    collection.InsertOne(nil, docToInsert)

    //Retrieve document
    filterDoc := MyDoc{SomeInt: 42}
    resultDoc := &MyDoc{}
    result := collection.FindOne(nil, filterDoc)
    result.Decode(resultDoc)

    println(resultDoc.SomeInt, resultDoc.SomeString, resultDoc.CustomType)
  

打印结果:“ 42答案” //“ ABCD”丢失了

预先感谢

3 个答案:

答案 0 :(得分:1)

  

我尝试使用bson标记将DocumentResult解码为结构,但不适用于包裹字符串的自定义类型

使用当前的MyType,将在MongoDB中存储的文档如下:

{
  "_id": ObjectId("..."),
  "some_int": NumberLong("42"),
  "some_string": "The Answer",
  "custom_type": "ABCD"
}

即使基础类型是string,由于类型包装,使用当前版本的mongo-go-driver(v0.0.12)进行解码也可能很棘手。

但是,如果您希望具有这样的自定义类型,则可以将结构更改为an embedded field。例如:

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType MyType `bson:"custom_type,omitempty"`
}

type MyType struct {
    Value string `bson:"value,omitempty"`
}

var myType = MyType{Value: "ABCD"}

docToInsert := MyDoc{42, "The Answer", "ABCD"}

insertResult, err := collection.InsertOne(nil, docToInsert)

resultDoc := collection.FindOne(context.Background(), nil)
if err != nil {
    log.Fatal(err)
}
elem := &MyDoc{}
err = resultDoc.Decode(elem)
if err != nil {
    log.Fatal(err)
}
fmt.Println(elem.SomeInt, elem.SomeString, elem.CustomType.Value)
// 42 The Answer ABCD

该文档将存储在MongoDB中,如下所示:

{
  "_id": ObjectId("..."),
  "some_int": NumberLong("42"),
  "some_string": "The Answer",
  "custom_type": {
    "value": "ABCD"
  }
}

否则,直接使用string类型,因为数据库中生成的文档将与类型包装版本相同:

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType string `bson:"custom_type,omitempty"`
} 

您可能还会发现MongoDB Data Modeling是有用的参考。

答案 1 :(得分:1)

使用用于Go的MongoDB驱动程序1.x版本(撰写本文时的最新版本是1.3.1),完全可以对别名类型进行编码和解码。

您的示例现在可以按预期工作,只要调整mongo.Connect使其与新的1.x API匹配即可。

package main

import (
    "context"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType MyType `bson:"custom_type,omitempty"`
}

type MyType string

const myType MyType = "ABCD"

func main() {

    // Connect to db
    clientOpts := options.Client().
        ApplyURI("mongodb://localhost/example_db")
    client, _ := mongo.Connect(context.Background(), clientOpts)
    db := client.Database("example_db")
    collection := db.Collection("col")

    // Insert document
    docToInsert := MyDoc{42, "The Answer", myType}
    collection.InsertOne(nil, docToInsert)

    // Retrieve document
    filterDoc := MyDoc{SomeInt: 42}
    resultDoc := &MyDoc{}
    result := collection.FindOne(nil, filterDoc)
    result.Decode(resultDoc)

    println(resultDoc.SomeInt, resultDoc.SomeString, resultDoc.CustomType)
}

这将返回:42 The Answer ABCD如预期

答案 2 :(得分:0)

不幸的是,你不走运。官方mongo go驱动程序的当前状态不支持将string值从BSON解组到Go类型的值,该类型的类型是具有string作为其基础类型的自定义类型。将来可能会有所改变,但目前不支持。

bson/decode.go, currently line #387中实现了处理解码为struct字段的方式:

case 0x2:
    str := v.StringValue()
    switch containerType {
    case tString, tEmpty:
        val = reflect.ValueOf(str)
    case tJSONNumber:
        _, err := strconv.ParseFloat(str, 64)
        if err != nil {
            return val, err
        }
        val = reflect.ValueOf(str).Convert(tJSONNumber)

    case tURL:
        u, err := url.Parse(str)
        if err != nil {
            return val, err
        }
        val = reflect.ValueOf(u).Elem()
    default:
        return val, nil
    }

0x02是BSON字符串类型。仅当struct字段的类型为以下任何一种时,才尝试解码到struct字段中:stringinterface{}json.Numberurl.URL(或指向这些变量的指针) )。

不幸的是,在自定义类型上实现bson.Unmarshaler也无济于事,因为只有在结构本身实现的情况下,才在结构字段的情况下不对其进行检查。但是要在结构本身上实现,则必须复制该结构,且字段是上面列出的受支持类型之一(或使用映射或bson.Document类型)。

这是图书馆方面的一个严重限制,可以很容易地解决,所以我们希望最好的是他们在不久的将来增加对此的支持。