0

This is Collection Structure

[{
   "_id"              : "....",
   "name"             : "aaaa",
   "level_max_leaves" : [
                           {
                              level      : "ObjectIdString 1",
                              max_leaves : 4,
                           }
                        ]
},
{
   "_id"              : "....",
   "name"             : "bbbb",
   "level_max_leaves" : [
                           {
                              level      : "ObjectIdString 2",
                              max_leaves : 2,
                           }
                        ]
}]

I need to find the subdocument value of level_max_leaves.level filter when its matching with given input value.

And this how I tried,

For example,

var empLevelId = 'ObjectIdString 1' ;

MyModel.aggregate(
    {$unwind: "$level_max_leaves"},
    {$match: {"$level_max_leaves.level": empLevelId } },
    {$group: { "_id": "$level_max_leaves.level", 
               "total": { "$sum": "$level_max_leaves.max_leaves" }}},
    function (err, res) {
        console.log(res);
});

But here the $match filter is not working. I can't find out exact results of ObjectIdString 1

If I filter with name field, its working fine. like this,

{$match: {"$name": "aaaa" } },

But in subdocument level its returns 0.

{$match: {"$level_max_leaves.level": "ObjectIdString 1"} },

My expected result was,

{
   "_id"              : "ObjectIdString 1",
   "total"            : 4,
}
Ranjith
  • 2,779
  • 3
  • 22
  • 41
  • You supplied value does not match anything on the element you are specifying to match. You might actually get what you want by clearly stating what you are trying to do. The best way is to show an expected result. – Neil Lunn Aug 21 '14 at 09:44
  • I think you should be doing unwind: "$level_max_leaves" – Lalit Agarwal Aug 21 '14 at 09:46
  • @LalitAgarwal : Yeah. Typo. Correct it now. – Ranjith Aug 21 '14 at 10:01
  • @NeilLunn : You already knew from the previous question you calculate the sum of `max_leaves` for all `levels`. For example, `[{_id:1, max:5}, {_id:2, max:7}, {_id:3, max:6}]`. If I pass `_id: 3` in that query. In the same scenario, now i need to get leaves particular `level` alone not to all. Got it? The result would be `{_id: 3, total: 6 }` For that I add `$match { ... }` filter option. But I won't be expected. – Ranjith Aug 21 '14 at 10:07
  • @NeilLunn: I updated my question too. – Ranjith Aug 21 '14 at 10:17

1 Answers1

0

You have typed the $match incorrectly. Fields with $ prefixes are either for the implemented operators or for "variable" references to field content. So you just type the field name:

MyModel.aggregate(
    [
        { "$match": { "level_max_leaves.level": "ObjectIdString 1" } },
        { "$unwind": "$level_max_leaves" },
        { "$match": { "level_max_leaves.level": "ObjectIdString 1" } },
        { "$group": { 
            "_id": "$level_max_leaves.level", 
           "total": { "$sum": "$level_max_leaves.max_leaves" }
        }}
    ],
    function (err, res) {
        console.log(res); 
    }
);

Which on the sample you provide produces:

{ "_id" : "ObjectIdString 1", "total" : 4 }

It is also good practice to $match first in your pipeline. That is in fact the only time an index can be used. But not only for that, as without the initial $match statement, your aggregation pipeline would perform an $unwind operation on every document in the collection, whether it met the conditions or not.

So generally what you want to do here is

  1. Match the documents that contain the required elements in the array
  2. Unwind the array of the matching documents
  3. Match the required array content excluding all others
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • Thanks for your reply :). I removed that `$` prefix in `$match` condition. Either ways it returns nothing. – Ranjith Aug 22 '14 at 05:20
  • @Ranjith To make this clear, people do this every day so it is not a discovered bug, but you are doing something wrong here. `empLevelId` is a variable. Try logging the content to see what it contains before your query is issued. Then check that you actually have a document that matches the value you are supplying. – Neil Lunn Aug 22 '14 at 05:25
  • Actually the `level_max_leaves.level` is a subdocument referenced by the `ObjectId` parent of `level` collection. So in dynamically `empLevelId` is one of the `ObjectId` of `Level`. In above Collection which is asked in the question, it contains one or more `document` holds different `level` id with `max_leaves`. Now I wish is to calculate the all `max_leaves` based on my `empLevelId` which is referred `LevelId`. No issues in `empLevelId`. It holds `Level` id correctly. – Ranjith Aug 22 '14 at 05:42
  • @Ranjith There is something you are not clearly understanding here. I have edited the query to show what should be happening and the result I get when running against your data as shown. – Neil Lunn Aug 22 '14 at 05:59