mgo,mongodb:查找一个嵌入在数组中的文档。

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

mgo, mongodb: find one document that is embedded and part of an array

问题

如下是要翻译的内容:

问题有两个部分。

第一个部分是关于mongodb查询本身的问题,下一个部分是如何在mgo中实现这个查询。

我如何查询一个类型为category的文档(结果应该是category类型),其中 slug: "general"

我选择这种布局的原因是因为我了解到mongodb在嵌入的“structs”方面具有性能优势,但我担心我必须将“categories”和“forum”作为它们自己的集合,并重写很多代码,我想避免这样做,因为客户端的每个视图都需要访问这些模型,否则每次加载新页面时都会导致1-2个额外的查询(用于category和forum),这样使用mongodb的优势就会消失。

接下来的问题是,如何更新或删除一个特定的嵌入式文档?

是否有一种方法可以直接从mongodb获取category文档,而无需将文档分开或在Go中编写find、update和delete函数?如果有,该如何实现?

给出的结构如下:

{
    "_id" : ObjectId("5303d1a2d6194c0f27000001"),
    "name" : "darko",
    "description" : "darko",
    "subdomain" : "darko",
    "domain" : "mango.dev",
    "created" : ISODate("2014-02-18T21:33:22.115Z"),
    "category" : "Brains",
    "owner" : "52b1d74dd6194c0646000002",
    "members" : [ 
        "52b1d74dd6194c0646000002"
    ],
    "categories" : [ 
        {
            "_id" : ObjectId("5303d1a2d6194c0f27000003"),
            "name" : "Admin and Moderator Area",
            "slug" : "admin-and-moderator-area",
            "adminonly" : true,
            "membersonly" : false,
            "forums" : [ 
                {
                    "_id" : ObjectId("5303d1a2d6194c0f27000005"),
                    "name" : "Admin Discussion",
                    "slug" : "admin-discussion",
                    "text" : "This is the main forum for administrative topics."
                }
            ]
        }, 
        {
            "_id" : ObjectId("5303d1a2d6194c0f27000002"),
            "name" : "General",
            "slug" : "general",
            "adminonly" : false,
            "membersonly" : false,
            "forums" : [ 
                {
                    "_id" : ObjectId("5303d1a2d6194c0f27000004"),
                    "name" : "General Discussion",
                    "slug" : "general-discussion",
                    "text" : "Talk about everything and anything here in this general discussion forum"
                }
            ]
        }
    ]
}

或者在Go中的表示如下:

Community struct {
    Id          bson.ObjectId `bson:"_id,omitempty" json:"id"`
    Name        string        `json:"name"`
    Description string        `bson:",omitempty" json:"description"`
    Subdomain   string        `bson:",omitempty" json:"subdomain"`
    Domain      string        `json:"domain"`
    Created     time.Time     `json:"created"`
    Category    string        `json:"category"`
    Owner       interface{}   `json:"owner"`                         //userid
    Members     []interface{} `json:"members"`                       //userid
    Moderators  []interface{} `bson:",omitempty" json:"moderators"`  //userid
    Logo        string        `bson:",omitempty" json:"logo"`        // relative path to file
    Stylesheets []string      `bson:",omitempty" json:"stylesheets"` // absolute path to files
    Javascripts []string      `bson:",omitempty" json:"javascripts"` // absolute path to files
    Categories  []*Category   `json:"categories"`
}

Category struct {
    Id          bson.ObjectId `bson:"_id,omitempty" json:"id"`
    Name        string        `json:"name"`
    Slug        string        `json:"slug"`
    AdminOnly   bool          `json:"-"`
    MembersOnly bool          `json:"-"`
    Forums      []*Forum      `json:"forums"`
}

Forum struct {
    Id         bson.ObjectId `bson:"_id,omitempty" json:"id"`
    Name       string        `json:"name"`
    Slug       string        `json:"slug"`
    Text       string        `json:"text"`
    Moderators []interface{} `bson:",omitempty" json:"moderators"` //userid
}
英文:

2 Parts to the question.

1 is the mongodb query itself, the next is how to do it in mgo.

How do I query for 1 document of type category (the result should be of type category) where the slug: "general"?

The reason I picked this layout is because I read the advantage of mongodb is performance with embedded "structs" however I fear I have to make "categories" and "forum" its own collection and rewrite a lot of code, I would like to avoid that because every view on the client side needs access to those models anyway and it would lead to 1-2 additional queries on each new page load (for category and forum) and the advantage of using mongodb would be gone.

And the followup question is, how would I update or delete one particular embedded document?

Is there a way to get the category document directly from mongodb without needing to either separate the documents or write find, update , delete functions in Go, and how?

this structure:

{
    "_id" : ObjectId("5303d1a2d6194c0f27000001"),
    "name" : "darko",
    "description" : "darko",
    "subdomain" : "darko",
    "domain" : "mango.dev",
    "created" : ISODate("2014-02-18T21:33:22.115Z"),
    "category" : "Brains",
    "owner" : "52b1d74dd6194c0646000002",
    "members" : [ 
        "52b1d74dd6194c0646000002"
    ],
    "categories" : [ 
        {
            "_id" : ObjectId("5303d1a2d6194c0f27000003"),
            "name" : "Admin and Moderator Area",
            "slug" : "admin-and-moderator-area",
            "adminonly" : true,
            "membersonly" : false,
            "forums" : [ 
                {
                    "_id" : ObjectId("5303d1a2d6194c0f27000005"),
                    "name" : "Admin Discussion",
                    "slug" : "admin-discussion",
                    "text" : "This is the main forum for administrative topics."
                }
            ]
        }, 
        {
            "_id" : ObjectId("5303d1a2d6194c0f27000002"),
            "name" : "General",
            "slug" : "general",
            "adminonly" : false,
            "membersonly" : false,
            "forums" : [ 
                {
                    "_id" : ObjectId("5303d1a2d6194c0f27000004"),
                    "name" : "General Discussion",
                    "slug" : "general-discussion",
                    "text" : "Talk about everything and anything here in this general discussion forum"
                }
            ]
        }
    ]
}

or in go:

Community struct {
	Id          bson.ObjectId `bson:"_id,omitempty" json:"id"`
	Name        string        `json:"name"`
	Description string        `bson:",omitempty" json:"description"`
	Subdomain   string        `bson:",omitempty" json:"subdomain"`
	Domain      string        `json:"domain"`
	Created     time.Time     `json:"created"`
	Category    string        `json:"category"`
	Owner       interface{}   `json:"owner"`                         //userid
	Members     []interface{} `json:"members"`                       //userid
	Moderators  []interface{} `bson:",omitempty" json:"moderators"`  //userid
	Logo        string        `bson:",omitempty" json:"logo"`        // relative path to file
	Stylesheets []string      `bson:",omitempty" json:"stylesheets"` // absolute path to files
	Javascripts []string      `bson:",omitempty" json:"javascripts"` // absolute path to files
	Categories  []*Category   `json:"categories"`
}

Category struct {
	Id          bson.ObjectId `bson:"_id,omitempty" json:"id"`
	Name        string        `json:"name"`
	Slug        string        `json:"slug"`
	AdminOnly   bool          `json:"-"`
	MembersOnly bool          `json:"-"`
	Forums      []*Forum      `json:"forums"`
}

Forum struct {
	Id         bson.ObjectId `bson:"_id,omitempty" json:"id"`
	Name       string        `json:"name"`
	Slug       string        `json:"slug"`
	Text       string        `json:"text"`
	Moderators []interface{} `bson:",omitempty" json:"moderators"` //userid
}

答案1

得分: 13

1.

目前,MongoDB没有内置的方法可以返回子文档,你只能返回文档的投影。也就是说,你仍然可以通过使用投影来找到你要查找的数据。

MongoDB手册给出了一个非常类似的例子。你应该执行的查询是:

db.coll.find({}, {categories:{ $elemMatch: {"slug":"general"}}})

2.

使用mgo,可以使用Select来处理投影部分。mgo文档中说明了如下内容:

>func (q *Query) Select(selector interface{}) *Query
>
>Select允许选择应该为找到的结果检索哪些字段。

尽管我没有尝试过,但它应该类似于以下代码:

err := collection.Find(nil).Select(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&result)

为了获取嵌套的Category对象,你可以让result成为一个包含结构体:

type CategoryContainer struct {
    Categories []Category{} // 在这种情况下甚至可以是[1]Category{}
}

然后简单地获取category:

category := result.Categories[0]

3.

关于更新子文档,已经有一些很好的帖子,比如:

https://stackoverflow.com/questions/5646798/mongodb-updating-subdocument

英文:

1.

Currently, there is no built in way for MongoDB to return subdocument, you can only return a projection of a document. That being said, you can still find the data you are looking for by using a projection.

The MongoDB manual gives you a very similar example. The query you should make is:

db.coll.find({}, {categories:{ $elemMatch: {"slug":"general"}}})

2.

With mgo, the projection part is handled using Select. The mgo documentation states:

>func (q *Query) Select(selector interface{}) *Query
>
>Select enables selecting which fields should be retrieved for the results found.

<s>Without having tried it</s>, it should look something like this:

err := collection.Find(nil).Select(bson.M{&quot;categories&quot;: bson.M{&quot;$elemMatch&quot;: bson.M{&quot;slug&quot;: &quot;general&quot;}}}).One(&amp;result)

In order to get the nestled Category object, you can let result be a containing struct:

type CategoryContainer struct {
    Categories []Category{} // Or even [1]Category{} in this case
}

and then fetch the category simply:

category := result.Categories[0]

3.

For updating subdocuments, there are already good posts about that, such as:

https://stackoverflow.com/questions/5646798/mongodb-updating-subdocument

答案2

得分: 0

虽然(如上所述)这个代码可以工作:

<!-- language: lang-go -->
err := collection.Find(nil).Select(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&result)

但我认为这将查询集合中的所有文档,然后选择正确的文档。因此,我认为下面的解决方案更有效:

<!-- language: lang-go -->
err := collection.Find(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&result)
英文:

Although (as mentioned above) this works:
<pre>
<!-- language: lang-go -->
err := collection.Find(nil).Select(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&result)
</pre>

but I think this will query all docments inside the collection, and then it choose the right one. Hence I think the solution that comes below is more efficent.

<pre>
<!-- language: lang-go -->
err := collection.Find(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&result)
</pre>

huangapple
  • 本文由 发表于 2014年3月19日 21:29:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/22507446.html
匿名

发表评论

匿名网友

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

确定