Definition
$facetProcesses 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.
The
$facetstage allows you to create multi-faceted aggregations which characterize data across multiple dimensions, or facets, within a single aggregation stage. Multi-faceted aggregations provide multiple filters and categorizations to guide data browsing and analysis. Retailers commonly use faceting to narrow search results by creating filters on product price, manufacturer, size, etc.Input documents are passed to the
$facetstage only once.$facetenables various aggregations on the same set of input documents, without needing to retrieve the input documents multiple times.
Compatibility
You can use $facet for deployments hosted in the following
environments:
MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud
MongoDB Enterprise: The subscription-based, self-managed version of MongoDB
MongoDB Community: The source-available, free-to-use, and self-managed version of MongoDB
Syntax
The $facet stage has the following form:
{ $facet: { <outputField1>: [ <stage1>, <stage2>, ... ], <outputField2>: [ <stage1>, <stage2>, ... ], ... } }
Specify the output field name for each specified pipeline.
Considerations
As each stage in a $facet executes, the resulting document is
limited to 100 megabytes. Note the allowDiskUse flag doesn't affect the 100 megabyte size
limit, since $facet can't spill to disk.
The final output document is subject to the 16 mebibyte BSON document size limit. If it exceeds 16 mebibytes, the aggregation produces an error.
Behavior
Facet-related aggregation stages categorize and group incoming
documents. Specify any of the following facet-related stages within
different $facet sub-pipeline's <stage> to perform a
multi-faceted aggregation:
Other
aggregation stages can
also be used with $facet with the following exceptions:
Each sub-pipeline within $facet is passed the exact same set of
input documents. These sub-pipelines are completely independent of one
another and the document array output by each is stored in separate
fields in the output document. The output of one sub-pipeline can not
be used as the input for a different sub-pipeline within the same
$facet stage. If further aggregations are required, add additional
stages after $facet and specify the field name, <outputField>,
of the desired sub-pipeline output.
Index Use
Pipeline order determines how the $facet stage uses indexes.
If the
$facetstage is the first stage in a pipeline, the stage will perform aCOLLSCAN. The$facetstage does not make use of indexes if it is the first stage in the pipeline.If the
$facetstage comes later in the pipeline and earlier stages have used indexes,$facetwill not trigger aCOLLSCANduring execution.
For example, $match or $sort stages that come
before a $facet stage can make use of indexes and the $facet
will not trigger a COLLSCAN.
For optimization suggestions, see: Aggregation Pipeline Optimization.
Examples
The examples on this page use data from the sample_mflix sample dataset. For details on how to load this dataset into your self-managed MongoDB deployment, see Load the sample dataset. If you made any modifications to the sample databases, you may need to drop and recreate the databases to run the examples on this page.
The following aggregation pipeline uses the movies collection to
categorize films across multiple dimensions. A $match stage
limits the input to movies with a runtime greater than 1,000 minutes. The
$facet stage runs three sub-pipelines in parallel using
$sortByCount, $bucket, and
$bucketAuto. MongoDB fetches input documents from the
movies collection only once, at the beginning of the operation:
db.movies.aggregate( [ { $match: { runtime: { $gt: 1000 } } }, { $facet: { "categorizedByGenres": [ { $unwind: "$genres" }, { $sortByCount: "$genres" } ], "categorizedByRuntime": [ { $bucket: { groupBy: "$runtime", boundaries: [ 1000, 1200, 1400 ], default: "Other", output: { "count": { $sum: 1 }, "titles": { $push: "$title" } } } } ], "categorizedByYear(Auto)": [ { $bucketAuto: { groupBy: "$year", buckets: 4 } } ] } } ] )
[ { categorizedByGenres: [ { _id: 'Sport', count: 1 }, { _id: 'Documentary', count: 1 }, { _id: 'Action', count: 1 }, { _id: 'Drama', count: 1 }, { _id: 'Adventure', count: 1 }, { _id: 'History', count: 1 } ], categorizedByRuntime: [ { _id: 1000, count: 1, titles: [ 'Baseball' ] }, { _id: 1200, count: 1, titles: [ 'Centennial' ] } ], 'categorizedByYear(Auto)': [ { _id: { min: 1978, max: 1994 }, count: 1 }, { _id: { min: 1994, max: 1994 }, count: 1 } ] } ]
The C# examples on this page use the sample_mflix database
from the Atlas sample datasets. To learn how to create a
free MongoDB Atlas cluster and load the sample datasets, see
Get Started in the MongoDB .NET/C#
Driver documentation.
The following Movie class models the documents in the sample_mflix.movies
collection:
public class Movie { public ObjectId Id { get; set; } public int Runtime { get; set; } public string Title { get; set; } public string Rated { get; set; } public List<string> Genres { get; set; } public string Plot { get; set; } public ImdbData Imdb { get; set; } public int Year { get; set; } public int Index { get; set; } public string[] Comments { get; set; } [] public DateTime LastUpdated { get; set; } }
Note
ConventionPack for Pascal Case
The C# classes on this page use Pascal case for their property names, but the
field names in the MongoDB collection use camel case. To account for this difference,
you can use the following code to register a ConventionPack when your
application starts:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
To use the MongoDB .NET/C# driver to add a $facet stage to an aggregation
pipeline, call the Facet() method on a PipelineDefinition object.
The following example creates a pipeline stage that executes two parallel aggregations. The first aggregation distributes incoming
documents into five groups by the value of their Runtime field. The second
aggregation counts each value in the Rated field and returns the
count for each value, limited to the top five values.
var bucketPipeline = new EmptyPipelineDefinition<Movie>() .BucketAuto( groupBy: m => m.Runtime, buckets: 5); var bucketFacet = AggregateFacet.Create( name: "Runtimes", pipeline: bucketPipeline); var countLimitPipeline = new EmptyPipelineDefinition<Movie>() .SortByCount(m => m.Rated) .Limit(5); var countFacet = AggregateFacet.Create( "Ratings", countLimitPipeline); var pipeline = new EmptyPipelineDefinition<Movie>() .Facet(bucketFacet, countFacet);
The Node.js examples on this page use the sample_mflix database from the
Atlas sample datasets. To learn how to create a free
MongoDB Atlas cluster and load the sample datasets, see Get Started in the MongoDB Node.js driver documentation.
To use the MongoDB Node.js driver to add a $facet stage to an aggregation
pipeline, use the $facet operator in a pipeline object.
The following example creates a pipeline stage that executes two parallel aggregations. The first aggregation distributes
incoming documents into five groups by the value of their runtime
field by using the $bucketAuto stage. The second
aggregation counts each value in the rated field and returns the
count of the top five values by using the $sortByCount
and $limit stages. The
example then runs the aggregation pipeline:
const pipeline = [ { $facet: { bucketPipeline: [ { $bucketAuto: { groupBy: "$runtime", buckets: 5 } } ], countLimit: [ { $sortByCount: "$rated" }, { $limit: 5 } ] } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Learn More
To learn more about related pipeline stages, see the $bucketAuto,
$sortByCount, and $limit guides.