Storing nested structs with mgo

Verran picture Verran · Dec 31, 2013 · Viewed 7.6k times · Source

I'm trying to build a mongo document from a go struct that is heavily nested, and I'm running into a problem with the transition from go struct to a mongo object. I've built a very simplified version of what I'm trying to work with here: http://play.golang.org/p/yPZW88deOa

package main

import (
    "os"
    "fmt"
    "encoding/json"
)

type Square struct {
    Length int 
    Width int
}

type Cube struct {
    Square
    Depth int
}

func main() {
    c := new(Cube)
    c.Length = 2
    c.Width = 3
    c.Depth = 4

    b, err := json.Marshal(c)
    if err != nil {
        panic(err)
    }

    fmt.Println(c)
    os.Stdout.Write(b)
}

Running this produces the following output:

&{{2 3} 4}
{"Length":2,"Width":3,"Depth":4}

Which makes complete sense. It seems either the Write function or the json.Marshal function has some functionality that collapses the nested struct, but my problem comes when I try to insert this data into a mongo database using the mgo function func (*Collection) Upsert (http://godoc.org/labix.org/v2/mgo#Collection.Upsert). If I use the json.Marshal() function first and pass the bytes to collection.Upsert(), it is stored as binary, which I don't want, but if I use collection.Upsert(bson.M("_id": id, &c) it appears as a nested struct with the form:

{
    "Square": {
        "Length": 2
        "Width": 3
    }
    "Depth": 4
}

But what I want to do is upsert to mongo with the same structure as I get when I use the os.Stdout.Write() function:

{
     "Length":2,
     "Width":3,
     "Depth":4
}

Is there some flag I'm missing that would easily handle this? The only alternative I can see at this point is severely cutting down on the readability of the code by removing the nesting of the structs, which I really hate to do. Again, my actual code is way more complex than this example, so if I can avoid complicating it even more by keeping things nested, that would definitely be preferable.

Answer

ANisus picture ANisus · Dec 31, 2013

I think using the inline field tag is the best option for you. The mgo/v2/bson documentation states:

inline     Inline the field, which must be a struct or a map,
           causing all of its fields or keys to be processed as if
           they were part of the outer struct. For maps, keys must
           not conflict with the bson keys of other struct fields.

Your struct should then be defined as follows:

type Cube struct {
    Square `bson:",inline"`
    Depth  int
}

Edit

inline also exists in mgo/v1/bson incase you are using that one.