Anubhav
Anubhav

Reputation: 1623

Aggregate Query in Mongodb returns specific field

Document Sample:

{
    "_id" : ObjectId("53329dfgg43771e49538b4567"),
    "u" : {
        "_id" : ObjectId("532a435gs4c771edb168c1bd7"),
        "n" : "Salman khan",
        "e" : "[email protected]"
    },
    "ps" : 0,
    "os" : 1,
    "rs" : 0,
    "cd" : 1395685800,
    "ud" : 0
}

Query:

db.collectiontmp.aggregate([
            {$match: {os:1}},
            {$project : { name:{$toUpper:"$u.e"} , _id:0 } },
            {$group: { _id: "$u._id",total: {$sum:1} }},
            {$sort: {total: -1}}, { $limit: 10 }
             ]);

I need following things from the above query:

  1. Group by u._id
  2. Returns total number of records and email from the record, as shown below:

    { "result": [ { "email": "", "total": "" }, { "email": "", "total": "" } ], "ok": 1 }

Upvotes: 17

Views: 41577

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151170

The first thing you are doing wrong here is not understanding how $project is intended to work. Pipeline stages such as $project and $group will only output the fields that are "explicitly" identified. So only the fields you say to output will be available to the following pipeline stages.

Specifically here you "project" only part of the "u" field in your document and you therefore removed the other data from being available. The only present field here now is "name", which is the one you "projected".

Perhaps it was really your intention to do something like this:

db.collectiontmp.aggregate([
    { "$group": {
        "_id": {
           "_id": "$u._id",
           "email": { "$toUpper": "$u.e" }
        },
        "total": { "$sum": 1 },
    }},
    { "$project": {
        "_id": 0,
        "email": "$_id.email",
        "total": 1
    }},
    { "$sort": { "total": -1 } },
    { "$limit": 10 }
])

Or even:

db.collectiontmp.aggregate([
    { "$group": {
        "_id": "$u._id",
        "email": { "$first": { "$toUpper": "$u.e" } }
        "total": { "$sum": 1 },
    }},
    { "$project": {
        "_id": 0,
        "email": 1,
        "total": 1
    }},
    { "$sort": { "total": -1 } },
    { "$limit": 10 }
])

That gives you the sort of output you are looking for.

Remember that as this is a "pipeline", then only the "output" from a prior stage is available to the "next" stage. There is no "global" concept of the document as this is not a declarative statement such as in SQL, but a "pipeline".

So think Unix pipe "|" command, or otherwise look that up. Then your thinking will fall into place.

Upvotes: 27

Related Questions