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

Using mongodb go driver for decoding documents into structs with custom type fields

我是 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)

PRINTED RESULT: "42 The Answer" //"ABCD" is missing

提前致谢

前言: 具有 string 作为其基础类型的自定义类型现在由驱动程序自动处理。此答案早于驱动程序 1.x 版本,这是必要的。


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

bson/decode.go, currently line #387:

中实现了解码为结构字段的处理方式
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 字符串类型。如果结构字段的类型是以下任何一种,它只会尝试解码到结构字段中:stringinterface{}json.Number or url.URL(或指向这些的指针)。

不幸地实现了 bson.Unmarshaler on your custom type does not help either, as it is not checked in case of struct fields, only if the struct itself implements it. But implementing on the struct itself, you would have to duplicate the struct with the field being one of the above listed supported types (or use a map or a bson.Document 类型)。

这是库的一个严重限制,很容易解决,所以我们希望他们能在不久的将来增加对此的支持。

I try to decode a DocumentResult into a struct using bson tags, and it does not work for a custom type wrapping a string

使用您当前的 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.x 版本的 MongoDB Go 驱动程序(撰写本文时的最新版本是 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)
}

这 returns:42 The Answer ABCD 符合预期