Je suis débutant en go et en mongodb. J'essaie de décoder un DocumentResult
en une structure à l'aide de balises bson, et cela ne fonctionne pas pour un type personnalisé enveloppant une chaîne. Peut-on le faire sans changer le type du champ en chaîne?
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)
RÉSULTAT IMPRIMÉ: "42 La réponse" // "ABCD" est manquant
Merci d'avance
Avant-propos: Les types personnalisés ayant string
comme types sous-jacents sont désormais gérés automatiquement par le pilote. Cette réponse est antérieure au pilote 1.x
versions où cela était nécessaire.
Malheureusement, vous n'avez pas de chance. L'état actuel du pilote officiel mongo go ne prend pas en charge le démasquage des valeurs string
de BSON vers une valeur Go dont le type est un type personnalisé ayant string
comme type sous-jacent. Cela peut changer à l'avenir, mais pour l'instant cela n'est pas pris en charge.
La façon dont le décodage dans un champ struct est géré est implémentée dans bson/decode.go
, actuellement ligne # 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
est le type de chaîne BSON. Il est uniquement tenté de décoder dans le champ struct si le type du champ struct est l'un des suivants: string
, interface{}
, json.Number
ou url.URL
(ou un pointeur vers ceux-ci).
Malheureusement, la mise en œuvre bson.Unmarshaler
sur votre type personnalisé n'aide pas non plus, car il n'est pas vérifié dans le cas des champs de struct, seulement si le struct lui-même l'implémente. Mais en implémentant sur la structure elle-même, vous devrez dupliquer la structure avec le champ étant l'un des types pris en charge répertoriés ci-dessus (ou utiliser une carte ou un bson.Document
type).
Il s'agit d'une sérieuse limitation de la part de la bibliothèque qui peut être résolue très facilement, alors espérons pour le mieux qu'ils ajoutent un support pour cela dans un proche avenir.
J'essaie de décoder un DocumentResult en une structure à l'aide de balises bson, et cela ne fonctionne pas pour un type personnalisé enveloppant une chaîne
Avec votre MyType
actuel, le document qui serait stocké dans MongoDB serait comme ci-dessous:
{
"_id": ObjectId("..."),
"some_int": NumberLong("42"),
"some_string": "The Answer",
"custom_type": "ABCD"
}
Même si le type sous-jacent est un string
, cela pourrait être difficile à décoder avec la version actuelle de mongo-go-driver (v0.0.12) en raison de l'habillage de type.
Cependant, si vous souhaitez avoir un type personnalisé en tant que tel, vous pouvez remplacer la structure par n champ incorporé à la place. Par exemple:
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
Le document serait stocké dans MongoDB comme ci-dessous:
{
"_id": ObjectId("..."),
"some_int": NumberLong("42"),
"some_string": "The Answer",
"custom_type": {
"value": "ABCD"
}
}
Sinon, utilisez simplement le type string
directement car le document résultant dans la base de données serait le même que la version d'habillage de type:
type MyDoc struct {
SomeInt int `bson:"some_int"`
SomeString string `bson:"some_string,omitempty"`
CustomType string `bson:"custom_type,omitempty"`
}
Vous pouvez également trouver MongoDB Data Modeling une référence utile.
Avec les versions 1.x du pilote MongoDB pour Go (la dernière version au moment de la rédaction est 1.3.1), il est tout à fait possible de coder et de décoder les types alias.
Votre exemple fonctionne maintenant comme prévu, étant donné que l'on ajuste le mongo.Connect
pour correspondre au nouveau 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)
}
Cela renvoie: 42 The Answer ABCD
comme prévu