英文:
Query Document with different struct for results
问题
我有一个文档集合,它们被插入到Mongo中,看起来像这样:
type Stats struct {
   UserStatus string `json:"userStatus" bson:"userStatus"`
   ... 其他一些字段
}
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"`
    ... (其他一些字段)
    Stats            UserStats     `json:"stats" bson:"stats"`
}
我想查询特定的报告,所以我尝试了这个:
func UserNameReport() {
    ... 获取mongo会话,等等。
   // 创建只返回我想要的数据的结构体
    type UserNames struct {
        LastName         string        `json:"lastName"  bson:"lastName"`
        FirstName        string        `json:"firstName" bson:"firstName"`
        ... 等等
        UserStats        Stats         `json:"stats" bson:"stats"` 
    }
    projection := bson.M{"lastName": 1, "firstName": 1, ...}
    result := []UserNames{}
    err := x.Find({查询用户集合}).Select(projection).All(&result)
    ...
}
这个方法是有效的 - 我的问题是,如何只包含'Stats'结构体中的一个字段?换句话说,我实际上希望"projection"是这样的:
projection := bson.M{"lastName": 1, ..., "stats.userStatus": 1}  // <-- stats.userStatus不起作用
...
err := x.Find({查询用户集合}).Select(projection).All(&result)
我得到了整个"Stats"嵌入结构体的结果 - 如何过滤出子文档中的一个字段并将其放入结果集中?
谢谢!
英文:
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!
答案1
得分: 1
这是一个使用MongoDB 2.6.5的示例代码,它对我来说完美运行。
以下是翻译好的代码:
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() {
	// 连接数据库
	session, err := mgo.Dial("localhost")
	if err != nil {
		panic(err)
	}
	defer session.Close()
	// 如果存在people集合,则删除
	c := session.DB("test").C("people")
	c.DropCollection()
	// 添加一些数据
	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)
}
运行结果为:
[{1 Joe [{1} {2}]} {3 Didier [{5} {6}]}]
这正是我所期望的结果。
英文:
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.
答案2
得分: 0
也许这对其他人有所帮助-本质上,我试图获取一个包含嵌套文档的文档,并返回一个类似于在SQL中使用select a.LastName + ', ' + a.FirstName as Name, b.OtherData的结果集,从而实现不同的'table'/'document'。
这是我的当前解决方案-虽然我很想得到更好的解决方案(性能更好?)!
我创建了一个新的结构体,并使用'mapstructure'库。
import "github.com/goinggo/mapstructure"
type Stats struct {
   UserStatus string `json:"userStatus" bson:"userStatus"`
   ...更多字段
}
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"`
    ...(更多字段)
    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
   // 结果将作为map[string]interface{}的切片返回
   mgoResult := []map[string]interface{}{}
   // 执行查询
   err := c.Find(finder).Select(projection).All(&mgoResult)
   if err != nil {
      http.Error(w, err.Error(), http.StatusInternalServerError)
      return
   }
   user := MyReportItem{}
   // 遍历结果并将其解码为MyReport结构体
   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)
       }
   }
   ...发送结果...
   _ := json.NewEncoder(w).Encode(&users)
}
现在,我得到了一个形式为对象切片的结果:
results: [
  {
     firstName: "John",
     lastName: "Doe",
     status: "active"
  }
  ...
]
而不是:
{
    firstName: "John",
    lastName: "Doe",
    stats: {
             status: "active"
           }
}
英文:
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"
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论