Definition
Compatibility
You can use $unwind 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
You can pass a field path operand or a document operand to unwind an array field.
Field Path Operand
You can pass the array field path to $unwind. When using
this syntax, $unwind does not output a document if the field
value is null, missing, or an empty array.
{ $unwind: <field path> }
When you specify the field path, prefix the
field name with a dollar sign $ and enclose in quotes.
Document Operand with Options
You can pass a document to $unwind to specify various
behavior options.
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
Field | Type | Description |
|---|---|---|
string | Field path to an array field. To specify a field path, prefix
the field name with a dollar sign | |
string | Optional. The name of a new field to hold the array index of the
element. The name cannot start with a dollar sign | |
boolean | Optional.
The default value is |
Behaviors
Non-Array Field Path
When the value at path does not resolve to an array, $unwind
behaves as follows:
If the value is not missing, not
null, and not an empty array,$unwindoutputs a single document using the value as-is.If
includeArrayIndexis specified, the index is0for array inputs andnullfor non-array inputs. Documents later in the list have an index greater than0.If the value is missing,
null, or an empty array,$unwindfollows the preserveNullAndEmptyArrays option. WhenincludeArrayIndexis specified and the document is preserved, the index isnull.
Missing Field
If you specify a path for a field that does not exist in an input
document or the field is an empty array, $unwind, by
default, ignores the input document and will not output documents for
that input document.
To output documents where the array field is missing, null or an empty array, use the preserveNullAndEmptyArrays option.
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.
Unwind Array
The following aggregation uses the $unwind stage to
output a document for each element in the genres array of the
Inception movie document:
db.movies.aggregate( [ { $match: { title: "Inception" } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: "$genres" } ] )
[ { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' } ]
Each output document is identical to the input document except for
the value of the genres field, which now holds a single element
from the original genres array.
Missing or Non-array Values
The following aggregation uses the $unwind stage with
four movie documents. Two of the documents ("La porta del cielo"
and "Neecha Nagar") do not have a genres field:
db.movies.aggregate( [ { $match: { title: { $in: [ "Inception", "Brave", "La porta del cielo", "Neecha Nagar" ] } } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres" } } ] )
[ { genres: 'Animation', title: 'Brave' }, { genres: 'Adventure', title: 'Brave' }, { genres: 'Comedy', title: 'Brave' }, { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' } ]
In the
"Brave"document,genresis a populated array.$unwindreturns a document for each element in thegenresfield.In the
"Inception"document,genresis also a populated array.$unwindreturns a document for each element.The
"La porta del cielo"and"Neecha Nagar"documents do not have agenresfield, so$unwinddoes not return any documents for them.
Note
The { path: <FIELD> } syntax is optional. The following
$unwind operations are equivalent.
db.<COLLECTION>.aggregate( [ { $unwind: "<FIELD>" } ] )
db.<COLLECTION>.aggregate( [ { $unwind: { path: "<FIELD>" } } ] )
preserveNullAndEmptyArrays and includeArrayIndex
The preserveNullAndEmptyArrays and includeArrayIndex examples
use documents from the sample_mflix.movies collection.
preserveNullAndEmptyArrays
The following $unwind operation uses the
preserveNullAndEmptyArrays
option to include documents whose genres field is missing.
db.movies.aggregate( [ { $match: { title: { $in: [ "Inception", "Brave", "La porta del cielo", "Neecha Nagar" ] } } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true } } ] )
[ { title: 'La porta del cielo' }, { title: 'Neecha Nagar' }, { genres: 'Animation', title: 'Brave' }, { genres: 'Adventure', title: 'Brave' }, { genres: 'Comedy', title: 'Brave' }, { genres: 'Action', title: 'Inception' }, { genres: 'Mystery', title: 'Inception' }, { genres: 'Sci-Fi', title: 'Inception' } ]
includeArrayIndex
The following $unwind operation uses the
includeArrayIndex option to include
the array index in the output.
db.movies.aggregate( [ { $match: { title: "Inception" } }, { $project: { _id: 0, title: 1, genres: 1 } }, { $unwind: { path: "$genres", includeArrayIndex: "genreIndex" } } ] )
[ { genres: 'Action', title: 'Inception', genreIndex: Long('0') }, { genres: 'Mystery', title: 'Inception', genreIndex: Long('1') }, { genres: 'Sci-Fi', title: 'Inception', genreIndex: Long('2') } ]
Group by Unwound Values
The following pipeline unwinds the genres array and groups the
resulting documents by genre to count the number of movies in each
genre:
db.movies.aggregate( [ // First Stage { $match: { title: { $in: [ "The Dark Knight", "Inception", "Interstellar", "Brave" ] } } }, // Second Stage { $project: { _id: 0, title: 1, genres: 1 } }, // Third Stage { $unwind: "$genres" }, // Fourth Stage { $group: { _id: "$genres", movieCount: { $sum: 1 } } }, // Fifth Stage { $sort: { movieCount: -1 } } ] )
[ { _id: 'Adventure', movieCount: 2 }, { _id: 'Action', movieCount: 2 }, { _id: 'Drama', movieCount: 2 }, { _id: 'Sci-Fi', movieCount: 2 }, { _id: 'Crime', movieCount: 1 }, { _id: 'Animation', movieCount: 1 }, { _id: 'Comedy', movieCount: 1 }, { _id: 'Mystery', movieCount: 1 } ]
Unwind Multiple Array Fields
You can apply $unwind multiple times in a single
pipeline to expand documents that contain several array fields.
The following operation unwinds the genres array and then
the cast array to produce a flat document for every
genre-actor combination, then groups by genre to count the total
cast appearances in each genre:
db.movies.aggregate( [ // First Stage { $match: { title: { $in: [ "Inception", "The Dark Knight", "Interstellar" ] } } }, // Second Stage { $project: { _id: 0, title: 1, genres: 1, cast: 1 } }, // Third Stage { $unwind: "$genres" }, // Fourth Stage { $unwind: "$cast" }, // Fifth Stage { $group: { _id: "$genres", castAppearances: { $sum: 1 } } } ] )
[ { _id: 'Adventure', castAppearances: 4 }, { _id: 'Crime', castAppearances: 4 }, { _id: 'Action', castAppearances: 8 }, { _id: 'Drama', castAppearances: 8 }, { _id: 'Mystery', castAppearances: 4 }, { _id: 'Sci-Fi', castAppearances: 8 } ]
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 $unwind stage to an aggregation
pipeline, call the Unwind() method on a PipelineDefinition object.
The following example creates a pipeline stage that iterates over the Genres field in each input Movie document. For each
value in the Genres field, the stage creates a new Movie document and
populates its Genres field with the Genres value from the input document.
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres);
You can use an AggregateUnwindOptions
object to customize the behavior of the Unwind() method.
The following example performs the same operation as the previous
example, but also includes the following options:
PreserveNullAndEmptyArraysensures that documents that contain an empty array in theGenresfield are included in the output.The
IncludeArrayIndexoption adds a new field namedIndexto each output document. The value of this field is the array index of theGenresfield's value in the input document'sGenresarray.
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres, new AggregateUnwindOptions<Movie>() { PreserveNullAndEmptyArrays = true, IncludeArrayIndex = new ExpressionFieldDefinition<Movie, int>( m => m.Index) });
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 $unwind stage to an aggregation
pipeline, use the $unwind operator in a pipeline object.
The following example creates a pipeline stage that iterates over the genres field in each input movie document. For each
value in the genres field, the stage creates a new movie document and
populates its genres field with the genres value from the input document. The
example then runs the aggregation pipeline:
const pipeline = [{ $unwind: "$genres" }]; const cursor = collection.aggregate(pipeline); return cursor;
You can customize the behavior of the $unwind method.
The following example performs the same operation as the previous
example, but also includes the following options:
preserveNullAndEmptyArraysensures that documents that contain an empty array in thegenresfield are included in the output.includeArrayIndexadds a new field namedindexto each output document. The field contains the array index of thegenresvalue in the input document'sgenresfield.
const pipeline = [ { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true, includeArrayIndex: "index" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
Learn More
To learn more about related methods, see the $group, $sum,
$sort, and $multiply guides.
To see how to use $unwind in a full example, see the
Unwind Arrays and Group Data tutorial.