查询具有不同结构的文档以获取结果

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!

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.

Maybe this will help others - essentially I was trying to take a document with an embedded document and return a result set like I would do in SQL with a select a.LastName + ', ' + a.FirstName as Name, b.OtherData and in essence have a different 'table' / 'document'.

So here is my current solution - love to get better ones (more performant?) though!

I created a new struct and I'm using the 'mapstructure' library

import "github.com/goinggo/mapstructure"


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"`
}


type MyReportItem struct {
   FirstName   string `json:"firstName" jpath:"firstName"`
   LastName    string `json:"lastName"  jpath:"lastName"`
   Status      string `json:"status"    jpath:"stats.userStatus"`
}

type MyReport struct {
     Results []MyReportItem `json:"results"`
}


func xxxx(w http.ResponseWriter, r *http.Request) {

   var users MyReportItem

   // the results will come back as a slice of map[string]interface{}
   mgoResult := []map[string]interface{}{}

   // execute the query
   err := c.Find(finder).Select(projection).All(&mgoResult)
   if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
   }

   user := MyReportItem{}
   // iterate through the results and decode them into the MyReport struct
   for _, x := range mgoResult {
      docScript, _ := json.Marshal(x)
      docMap := map[string]interface{}{}
      json.Unmarshal(docScript, &docMap)

      err := mapstructure.DecodePath(docMap, &user)
      if err == nil {
        users.Results = append(users.Results, user)
     }
   }

   ... send back the results ...
   _ := json.NewEncoder(w).Encode(&users)
}

Now I get a slice of objects in the form:

results: [
  {
     firstName: "John",
     lastName: "Doe",
     status: "active"
  }
  ...
]

Instead of:

{
    firstName: "John",
    lastName: "Doe",
    stats: {
             status: "active"
           }
}