Использование драйвера mongodb go для декодирования документов в структуры с полями нестандартного типа
Я новичок в обоих го и mongodb. Я пытаюсь расшифровать DocumentResult
в структуру с использованием тегов bson, и она не работает для пользовательского типа, заключающего строку. Можно ли это сделать, не меняя тип поля на строку?
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 The Answer" //"ABCD" отсутствует
заранее спасибо
2 ответа
К сожалению, вам не повезло. Текущее состояние официального драйвера Монго Го не поддерживает демаршалинг 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.Number
или же url.URL
(или указатель на них).
К сожалению реализация bson.Unmarshaler
Ваш пользовательский тип также не помогает, так как он не проверяется в случае полей структуры, только если сама структура реализует его. Но, реализуя саму структуру, вам придется дублировать структуру с полем, являющимся одним из перечисленных выше поддерживаемых типов (или использовать карту или bson.Document
тип).
Это серьезное ограничение со стороны библиотеки, которое очень легко решить, поэтому давайте надеяться на лучшее, что они добавят поддержку для этого в ближайшем будущем.
Я пытаюсь декодировать DocumentResult в структуру с помощью тегов bson, и он не работает для пользовательского типа, заключающего строку
С вашим текущим MyType
документ, который будет храниться в MongoDB, будет выглядеть следующим образом:
{
"_id": ObjectId("..."),
"some_int": NumberLong("42"),
"some_string": "The Answer",
"custom_type": "ABCD"
}
Хотя базовый тип является 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.
С версиями 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)
}
Это возвращает: 42 The Answer ABCD
как и ожидалось