Search code examples
gomgo

Query Document with different struct for results


I have a collection of documents that were inserted into Mongo looking something like this:

type Stats struct {
   UserStatus string `json:"userStatus" bson:"userStatus"`
   ... a bunch more fields
}

type User struct {
    ID               bson.ObjectId `json:"-" bson:"_id"`
    LastName         string        `json:"lastName"  bson:"lastName"`
    FirstName        string        `json:"firstName" bson:"firstName"`
    Role             string        `json:"role" bson:"role"`
    Tags             []string      `json:"tags" bson:"tags"`
    ... (a bunch more fields)
    Stats            UserStats     `json:"stats" bson:"stats"`
}

I want to query it to get a specific report, so I tried this:

func UserNameReport() {
    ... get mongo session, etc.

   // create struct of just the data I want returned
    type UserNames struct {
        LastName         string        `json:"lastName"  bson:"lastName"`
        FirstName        string        `json:"firstName" bson:"firstName"`
        ... etc
        UserStats        Stats         `json:"stats" bson:"stats"` 
    }

    projection := bson.M{"lastName":1, "firstName":1, etc}
    result := []UserNames{}
    err := x.Find({query user collection}).Select(projection).All(&result)
    ...
}

This works - my question is, how can I include just ONE field from the 'Stats' struct? In other words, I essentially want the "projection" to be this:

projection := bson.M{"lastName":1, ..., "stats.userStatus":1}  <-- stats.userStatus doesn't work
...
err := x.Find({query user collection}).Select(projection).All(&result)

I get the entire "Stats" embedded struct in the results - how can I filter out just one field from the sub-document in and put it into the result set?

Thanks!


Solution

  • It works perfectly for me, with MongoDB 2.6.5

    The following code:

    package main
    
    import (
        "fmt"
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"
        "log"
    )
    
    type Statistics struct {
        Url  string
        Hits int
    }
    
    type Person struct {
        Num   int
        Uuid  string
        Name  string
        Stats []Statistics
    }
    
    func main() {
    
        // Connect to the database
        session, err := mgo.Dial("localhost")
        if err != nil {
            panic(err)
        }
        defer session.Close()
    
        // Remove people collection if any
        c := session.DB("test").C("people")
        c.DropCollection()
    
        // Add some data
        err = c.Insert(
            &Person{1, "UUID1", "Joe", []Statistics{Statistics{"a", 1}, Statistics{"b", 2}}},
            &Person{2, "UUID2", "Jane", []Statistics{Statistics{"c", 3}, Statistics{"d", 4}}},
            &Person{3, "UUID3", "Didier", []Statistics{Statistics{"e", 5}, Statistics{"f", 6}}})
        if err != nil {
            log.Fatal(err)
        }
    
        result := []Person{}
        err = c.Find(bson.M{"$or": []bson.M{bson.M{"uuid": "UUID3"}, bson.M{"name": "Joe"}}}).Select(bson.M{"num": 1, "name": 1, "stats.hits": 1}).All(&result)
        if err != nil {
            log.Fatal(err)
        }
    
        fmt.Println(result)
    }
    

    results in:

    [{1  Joe [{ 1} { 2}]} {3  Didier [{ 5} { 6}]}]
    

    ... which is precisely what I would expect.