6

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
}

2 Answers2

13

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.

Without having tried it, it should look something like this:

err := collection.Find(nil).Select(bson.M{"categories": bson.M{"$elemMatch": bson.M{"slug": "general"}}}).One(&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:

MongoDB: Updating subdocument

Community
  • 1
  • 1
ANisus
  • 74,460
  • 29
  • 162
  • 158
  • mhm. thank you. the result is a stripped version of community. After testing around with the Setter and Getter type I came to the conclusion that every independently editable document should be in its own collection. Sorting, querying, filtering should all be the DB's job, it's written to it, it should not be done in the application. Only embed a document if you expect to get the root document and don't nest too deeply. The advantage of nosql is the flexible schema, nothing else, and that is also arguable with sql migrations. Treat the models like sql models and avoid a headache. –  Mar 20 '14 at 13:51
  • Yes, I think that sounds like a correct conclusion. Working with MongoDB/mgo, it tends to be easiest to map each struct to a collection. Nested structs gets their own collection unless they are only used in the nested context and never as single objects. You will keep having relations between documents and multiple queries, but it is still very nice to get away from the limitations of a flat table. – ANisus Mar 21 '14 at 00:19
0

Although (as mentioned above) this works:

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

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.

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