For AI agents: a documentation index is available at https://www.mongodb.com/docs/llms.txt — markdown versions of all pages are available by appending .md to any URL path.
Docs Menu

$unwind (aggregation stage)

$unwind

Deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.

You can use $unwind for deployments hosted in the following environments:

  • MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud

Pass a field path operand or a document operand to unwind an array field.

You can pass the array field path to $unwind. With 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.

You can pass a document to $unwind to specify 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 $ and enclose in quotes.

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.

  • If true, if path is missing or is an empty array, $unwind omits the output field from the output document. If the value is null, the field remains null.

  • If false, if path is null, missing, or an empty array, $unwind does not output a document.

The default value is false.

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, $unwind outputs a single document using the value as-is.

  • If includeArrayIndex is specified, the index is 0 for array inputs and null for non-array inputs. Documents later in the list have an index greater than 0.

  • If the value is missing, null, or an empty array, $unwind follows the preserveNullAndEmptyArrays option. When includeArrayIndex is specified and the document is preserved, the index is null.

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.

The examples on this page use data from the sample_mflix 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 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.

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" and "Inception" documents, genres is a populated array. $unwind returns a document for each element.

  • The "La porta del cielo" and "Neecha Nagar" documents do not have a genres field, so $unwind does 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>" } } ]
)

The preserveNullAndEmptyArrays and includeArrayIndex examples use documents from the sample_mflix.movies collection.

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'
}
]

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')
}
]

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
}
]

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
}
]

For related stages and expressions, see $group, $sum, $sort, and $multiply.

For a full example, see the Unwind Arrays and Group Data tutorial.