使用不同结构的查询文档来获取结果。

huangapple go评论69阅读模式
英文:

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:&quot;userStatus&quot; bson:&quot;userStatus&quot;`
   ... a bunch more fields
}

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

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:&quot;lastName&quot;  bson:&quot;lastName&quot;`
        FirstName        string        `json:&quot;firstName&quot; bson:&quot;firstName&quot;`
        ... etc
        UserStats        Stats         `json:&quot;stats&quot; bson:&quot;stats&quot;` 
    }
 
    projection := bson.M{&quot;lastName&quot;:1, &quot;firstName&quot;:1, etc}
    result := []UserNames{}
    err := x.Find({query user collection}).Select(projection).All(&amp;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{&quot;lastName&quot;:1, ..., &quot;stats.userStatus&quot;:1}  &lt;-- stats.userStatus doesn&#39;t work
...
err := x.Find({query user collection}).Select(projection).All(&amp;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 (
&quot;fmt&quot;
&quot;gopkg.in/mgo.v2&quot;
&quot;gopkg.in/mgo.v2/bson&quot;
&quot;log&quot;
)
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(&quot;localhost&quot;)
if err != nil {
panic(err)
}
defer session.Close()
// Remove people collection if any
c := session.DB(&quot;test&quot;).C(&quot;people&quot;)
c.DropCollection()
// Add some data
err = c.Insert(
&amp;Person{1, &quot;UUID1&quot;, &quot;Joe&quot;, []Statistics{Statistics{&quot;a&quot;, 1}, Statistics{&quot;b&quot;, 2}}},
&amp;Person{2, &quot;UUID2&quot;, &quot;Jane&quot;, []Statistics{Statistics{&quot;c&quot;, 3}, Statistics{&quot;d&quot;, 4}}},
&amp;Person{3, &quot;UUID3&quot;, &quot;Didier&quot;, []Statistics{Statistics{&quot;e&quot;, 5}, Statistics{&quot;f&quot;, 6}}})
if err != nil {
log.Fatal(err)
}
result := []Person{}
err = c.Find(bson.M{&quot;$or&quot;: []bson.M{bson.M{&quot;uuid&quot;: &quot;UUID3&quot;}, bson.M{&quot;name&quot;: &quot;Joe&quot;}}}).Select(bson.M{&quot;num&quot;: 1, &quot;name&quot;: 1, &quot;stats.hits&quot;: 1}).All(&amp;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 &quot;github.com/goinggo/mapstructure&quot;
type Stats struct {
UserStatus string `json:&quot;userStatus&quot; bson:&quot;userStatus&quot;`
... a bunch more fields
}
type User struct {
ID               bson.ObjectId `json:&quot;-&quot; bson:&quot;_id&quot;`
LastName         string        `json:&quot;lastName&quot;  bson:&quot;lastName&quot;`
FirstName        string        `json:&quot;firstName&quot; bson:&quot;firstName&quot;`
Role             string        `json:&quot;role&quot; bson:&quot;role&quot;`
Tags             []string      `json:&quot;tags&quot; bson:&quot;tags&quot;`
... (a bunch more fields)
Stats            UserStats     `json:&quot;stats&quot; bson:&quot;stats&quot;`
}
type MyReportItem struct {
FirstName   string `json:&quot;firstName&quot; jpath:&quot;firstName&quot;`
LastName    string `json:&quot;lastName&quot;  jpath:&quot;lastName&quot;`
Status      string `json:&quot;status&quot;    jpath:&quot;stats.userStatus&quot;`
}
type MyReport struct {
Results []MyReportItem `json:&quot;results&quot;`
}
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(&amp;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, &amp;docMap)
err := mapstructure.DecodePath(docMap, &amp;user)
if err == nil {
users.Results = append(users.Results, user)
}
}
... send back the results ...
_ := json.NewEncoder(w).Encode(&amp;users)
}

Now I get a slice of objects in the form:

results: [
{
firstName: &quot;John&quot;,
lastName: &quot;Doe&quot;,
status: &quot;active&quot;
}
...
]

Instead of:

{
firstName: &quot;John&quot;,
lastName: &quot;Doe&quot;,
stats: {
status: &quot;active&quot;
}
}

huangapple
  • 本文由 发表于 2015年1月1日 11:48:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/27728146.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定