Many time we want run query with in query to get desirable result. MongoDB provide a aggregation operator to achieve result like this. Lets assume we have a collection with documents similar in structure to below : (this example from StackOverflow)
_id: xyz Movieid: 123 MovieName: Titanic ReleaseDate: 2000-01-01 _id: uvw Movieid: 456 MovieName: Titanic II ReleaseDate: 2018-01-01 _id: pqr Movieid: 789 MovieName: Titanic III ReleaseDate:
I would like to achieve the output as counts for total movies, movies with the release date, and movies without a release date in 3 separate columns as below:
Total | Released | UnReleased 3 | 2 | 1
To achieve the above result we need to run multiple aggregation pipelines within the same query. To do this MongoDB provide us an operator named $facet
. This can be used to run multiple pipelines with the same data.
$facet (3.4+):
Processes multiple aggregation pipelines within a single stage on the same set of input documents. Each sub-pipeline has its own field in the output document where its results are stored as an array of documents.
{ $facet:
{
<outputField1>: [ <stage1>, <stage2>, ... ],
<outputField2>: [ <stage1>, <stage2>, ... ],
...
}
}
The
$facet
stage, and its sub-pipelines, cannot make use of indexes, even if its sub-pipelines use$match
or if$facet
is the first stage in the pipeline. The$facet
stage will always perform aCOLLSCAN
during execution.
Input documents are passed to the $facet
stage only once. $facet
enables various aggregations on the same set of input documents, without needing to retrieve the input documents multiple times.
Solution:
db.collection.aggregate([ { "$facet": { "Total": [ { "$match" : { "ReleaseDate": { "$exists": true }}}, { "$count": "Total" }, ], "Released": [ { "$match" : {"ReleaseDate": { "$exists": true, "$nin": [""] }}}, { "$count": "Released" } ], "Unreleased": [ { "$match" : {"ReleaseDate": { "$exists": true, "$in": [""] }}}, { "$count": "Unreleased" } ] }}, { "$project": { "Total": { "$arrayElemAt": ["$Total.Total", 0] }, "Released": { "$arrayElemAt": ["$Released.Released", 0] }, "Unreleased": { "$arrayElemAt": ["$Unreleased.Unreleased", 0] } }} ]) // output [{ "Total": 3, "Released": 2, "Unreleased": 1 }]
In each pipeline we can use all aggregation related operator to achieve desire result.