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

amz picture amz · Aug 26, 2018 · Viewed 13.8k times · Source

I'm a beginner in both go and mongodb. I try to decode a DocumentResult into a struct using bson tags, and it does not work for a custom type wrapping a string. Can it be done without changing the field's type to a string?

    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

Thanks in advance

Answer

Wan Bachtiar picture Wan Bachtiar · Aug 29, 2018

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

With your current MyType, the document that would be stored in MongoDB would be as below:

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

Even though the underlying type is a string, this could be tricky to decode with the current version of mongo-go-driver (v0.0.12) due to the type wrapping.

However, if you would like to have a custom type as such, you could change the struct into an embedded field instead. For example:

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

The document would be stored in MongoDB as below:

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

Otherwise just use string type directly because the resulting document in the database would be the same as the type wrapping version:

type MyDoc struct {
    SomeInt    int    `bson:"some_int"`
    SomeString string `bson:"some_string,omitempty"`
    CustomType string `bson:"custom_type,omitempty"`
} 

You may also find MongoDB Data Modeling a useful reference.