$unwind (aggregation)
Definition
$unwind
- Deconstructs an array field from the input documents to output adocument for each element. Each output document is the inputdocument with the value of the array field replaced by the element.
Syntax
You can pass a field path operand or a document operand to unwind anarray field.
Field Path Operand
You can pass the array field path to $unwind
. When usingthis syntax, $unwind
does not output a document if the fieldvalue is null, missing, or an empty array.
- { $unwind: <field path> }
When you specify the field path, prefix thefield name with a dollar sign $
and enclose in quotes.
Document Operand with Options
New in version 3.2.
You can pass a document to $unwind
to specify variousbehavior options.
- {
- $unwind:
- {
- path: <field path>,
- includeArrayIndex: <string>,
- preserveNullAndEmptyArrays: <boolean>
- }
- }
Field | Type | Description |
---|---|---|
path | string | Field path to an array field. To specify a field path, prefixthe field name with a dollar sign $ and enclose in quotes. |
includeArrayIndex | string | Optional. The name of a new field to hold the array index of theelement. The name cannot start with a dollar sign $ . |
preserveNullAndEmptyArrays | boolean | Optional.- If true , if the path is null, missing, or an emptyarray, $unwind outputs the document.- If false , if path is null, missing, or an emptyarray, $unwind does not output a document.The default value is false . |
Behaviors
Non-Array Field Path
Changed in version 3.2: $unwind
stage no longer errors on non-array operands. Ifthe operand does not resolve to an array but is not missing, null,or an empty array, $unwind
treats the operand as asingle element array. If the operand is null, missing, or an emptyarray, the behavior of $unwind
depends on the value ofthe preserveNullAndEmptyArraysoption.
Previously, if a value in the field specified by the field path isnot an array, db.collection.aggregate()
generates anerror.
Missing Field
If you specify a path for a field that does not exist in an inputdocument or the field is an empty array, $unwind
, bydefault, ignores the input document and will not output documents forthat input document.
New in version 3.2: To output documents where the array field is missing, null or anempty array, use thepreserveNullAndEmptyArraysoption.
Examples
Unwind Array
From the mongo
shell, create a sample collection namedinventory
with the following document:
- db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })
The following aggregation uses the $unwind
stage to outputa document for each element in the sizes
array:
- db.inventory.aggregate( [ { $unwind : "$sizes" } ] )
The operation returns the following results:
- { "_id" : 1, "item" : "ABC1", "sizes" : "S" }
- { "_id" : 1, "item" : "ABC1", "sizes" : "M" }
- { "_id" : 1, "item" : "ABC1", "sizes" : "L" }
Each document is identical to the input document except for the valueof the sizes
field which now holds a value from the originalsizes
array.
includeArrayIndex and preserveNullAndEmptyArrays
New in version 3.2.
From the mongo
shell, create a sample collection namedinventory2
with the following documents:
- db.inventory2.insertMany([
- { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },
- { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },
- { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },
- { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },
- { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
- ])
The following $unwind
operations are equivalent and returna document for each element in the sizes
field. If the sizes
field does not resolve to an array but is not missing, null, or anempty array, $unwind
treats the non-array operand as asingle element array.
- db.inventory2.aggregate( [ { $unwind: "$sizes" } ] )
- db.inventory2.aggregate( [ { $unwind: { path: "$sizes" } } ] )
The operation returns the following documents:
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
- { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
includeArrayIndex
The following $unwind
operation uses theincludeArrayIndex option to includethe array index in the output.
- db.inventory2.aggregate( [
- {
- $unwind:
- {
- path: "$sizes",
- includeArrayIndex: "arrayIndex"
- }
- }])
The operation unwinds the sizes
array and includes the array indexof the array index in the new arrayIndex
field. If the sizes
field does not resolve to an array but is not missing, null, or anempty array, the arrayIndex
field is null
.
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S", "arrayIndex" : NumberLong(0) }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M", "arrayIndex" : NumberLong(1) }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L", "arrayIndex" : NumberLong(2) }
- { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M", "arrayIndex" : null }
preserveNullAndEmptyArrays
The following $unwind
operation uses thepreserveNullAndEmptyArraysoption to include documents whose sizes
field is null, missing,or an empty array.
- db.inventory2.aggregate( [
- { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }
- ] )
The output includes those documents where the sizes
field isnull, missing, or an empty array:
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
- { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }
- { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
- { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
- { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
Group by Unwound Values
From the mongo
shell, create a sample collection namedinventory2
with the following documents:
- db.inventory2.insertMany([
- { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },
- { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },
- { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },
- { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },
- { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
- ])
The following pipeline unwinds the sizes
array and groups theresulting documents by the unwound size values:
- db.inventory2.aggregate( [
- // First Stage
- {
- $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true }
- },
- // Second Stage
- {
- $group:
- {
- _id: "$sizes",
- averagePrice: { $avg: "$price" }
- }
- },
- // Third Stage
- {
- $sort: { "averagePrice": -1 }
- }
- ] )
- First Stage:
- The
$unwind
stage outputs a new document for each elementin thesizes
array. The stage uses thepreserveNullAndEmptyArraysoption to include in the output those documents wheresizes
fieldis missing, null or an empty array. This stage passes the followingdocuments to the next stage:
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
- { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
- { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }
- { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
- { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
- { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
- Second Stage:
- The
$group
stage groups the documents bysizes
and calculates the average price of each size. This stagepasses the following documents to the next stage:
- { "_id" : "S", "averagePrice" : NumberDecimal("80") }
- { "_id" : "L", "averagePrice" : NumberDecimal("80") }
- { "_id" : "M", "averagePrice" : NumberDecimal("120") }
- { "_id" : null, "averagePrice" : NumberDecimal("45.25") }
- Third Stage:
- The
$sort
stage sorts the documents byaveragePrice
indescending order. The operation returns the following result:
- { "_id" : "M", "averagePrice" : NumberDecimal("120") }
- { "_id" : "L", "averagePrice" : NumberDecimal("80") }
- { "_id" : "S", "averagePrice" : NumberDecimal("80") }
- { "_id" : null, "averagePrice" : NumberDecimal("45.25") }
See also
Unwind Embedded Arrays
From the mongo
shell, create a sample collection namedsales
with the following documents:
- db.sales.insertMany([
- {
- _id: "1",
- "items" : [
- {
- "name" : "pens",
- "tags" : [ "writing", "office", "school", "stationary" ],
- "price" : NumberDecimal("12.00"),
- "quantity" : NumberInt("5")
- },
- {
- "name" : "envelopes",
- "tags" : [ "stationary", "office" ],
- "price" : NumberDecimal("1.95"),
- "quantity" : NumberInt("8")
- }
- ]
- },
- {
- _id: "2",
- "items" : [
- {
- "name" : "laptop",
- "tags" : [ "office", "electronics" ],
- "price" : NumberDecimal("800.00"),
- "quantity" : NumberInt("1")
- },
- {
- "name" : "notepad",
- "tags" : [ "stationary", "school" ],
- "price" : NumberDecimal("14.95"),
- "quantity" : NumberInt("3")
- }
- ]
- }
- ])
The following operation groups the items sold by their tags andcalculates the total sales amount per each tag.
- db.sales.aggregate([
- // First Stage
- { $unwind: "$items" },
- // Second Stage
- { $unwind: "$items.tags" },
- // Third Stage
- {
- $group:
- {
- _id: "$items.tags",
- totalSalesAmount:
- {
- $sum: { $multiply: [ "$items.price", "$items.quantity" ] }
- }
- }
- }
- ])
- First Stage
- The first
$unwind
stage outputs a new document foreach element in theitems
array:
- { "_id" : "1", "items" : { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : 5 } }
- { "_id" : "1", "items" : { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : 8 } }
- { "_id" : "2", "items" : { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : 1 } }
- { "_id" : "2", "items" : { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : 3 } }
- Second Stage
- The second
$unwind
stage outputs a new document for eachelement in theitems.tags
arrays:
- { "_id" : "1", "items" : { "name" : "pens", "tags" : "writing", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
- { "_id" : "1", "items" : { "name" : "pens", "tags" : "office", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
- { "_id" : "1", "items" : { "name" : "pens", "tags" : "school", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
- { "_id" : "1", "items" : { "name" : "pens", "tags" : "stationary", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
- { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "stationary", "price" : NumberDecimal("19.95"), "quantity" : 8 } }
- { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "office", "price" : NumberDecimal("19.95"), "quantity" : 8 } }
- { "_id" : "2", "items" : { "name" : "laptop", "tags" : "office", "price" : NumberDecimal("800.00"), "quantity" : 1 } }
- { "_id" : "2", "items" : { "name" : "laptop", "tags" : "electronics", "price" : NumberDecimal("800.00"), "quantity" : 1 } }
- { "_id" : "2", "items" : { "name" : "notepad", "tags" : "stationary", "price" : NumberDecimal("14.95"), "quantity" : 3 } }
- { "_id" : "2", "items" : { "name" : "notepad", "tags" : "school", "price" : NumberDecimal("14.95"), "quantity" : 3 } }
- Third Stage
- The
$group
stage groups the documents by the tag andcalculates the total sales amount of items with each tag:
- { "_id" : "writing", "totalSalesAmount" : NumberDecimal("60.00") }
- { "_id" : "stationary", "totalSalesAmount" : NumberDecimal("264.45") }
- { "_id" : "electronics", "totalSalesAmount" : NumberDecimal("800.00") }
- { "_id" : "school", "totalSalesAmount" : NumberDecimal("104.85") }
- { "_id" : "office", "totalSalesAmount" : NumberDecimal("1019.60") }
See also