6

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

    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”缺失

提前致谢

4

3 回答 3

3

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

使用您的 current MyType,将存储在 MongoDB 中的文档如下所示:

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

即使底层类型是 a ,由于类型包装string,使用当前版本的mongo-go-driver (v0.0.12) 解码可能会很棘手。

但是,如果您想拥有这样的自定义类型,则可以将结构更改为嵌入字段。例如:

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是一个有用的参考。

于 2018-08-29T07:44:48.423 回答
3

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


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

处理解码到结构字段的方式在bson/decode.go当前行 #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 字符串类型。仅当结构字段的类型为以下任何一种时,才会尝试解码到结构字段中:string、、或(或指向这些字段的指针)。interface{}json.Numberurl.URL

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

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

于 2018-08-27T08:56:55.080 回答
1

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

您的示例现在可以按预期工作,因为您可以调整mongo.Connect以匹配新的1.xAPI。

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正如预期的那样

于 2020-03-17T10:02:11.527 回答