我是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”丢失了
预先感谢
答案 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字段中:string
,interface{}
,json.Number
或url.URL
(或指向这些变量的指针) )。
不幸的是,在自定义类型上实现bson.Unmarshaler
也无济于事,因为只有在结构本身实现的情况下,才在结构字段的情况下不对其进行检查。但是要在结构本身上实现,则必须复制该结构,且字段是上面列出的受支持类型之一(或使用映射或bson.Document
类型)。
这是图书馆方面的一个严重限制,可以很容易地解决,所以我们希望最好的是他们在不久的将来增加对此的支持。