The $unwind operator in MongoDB breaks down an array field into multiple documents, where each document contains one element from the original array. The array field is replaced with one element from the array for each new document.
Syntax:
{
$unwind: <field path>
}
Examples of Using $unwind Operator in MongoDB
To understand How to Use $unwind Operator in MongoDB collection we need a collection and some documents on which we will perform various operations and queries.
Sample Collection: employees
[
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": ["C", "C++", "PHP", "Java", ".Net"]
},
{
"_id": ObjectId("..."),
"name": "John",
"age": 28,
"phone_no": 1234567890,
"company": "techguy",
"skills": ["JavaScript", "Python", "Go"]
},
{
"_id": ObjectId("..."),
"name": "Alice",
"age": 26,
"phone_no": 9876543210,
"company": "webmasters",
"skills": []
},
{
"_id": ObjectId("..."),
"name": "David",
"age": 40,
"phone_no": 5647382910,
"company": "devteam",
"skills": ["Ruby", "PHP", "Node.js"]
}
]
Example 1: Using MongoDB $unwind on a Simple Array
When working with array fields, the $unwind operator helps convert each array element into a separate document. This makes it easier to query, filter, and analyze individual elements within the array. Let's use the $unwind operator to break down the skills array into individual documents.
Query:
db.employee.aggregate([
{
$unwind: "$skills"
}
]);
Output:
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": "C"
}
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": "C++"
}
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": "PHP"
}
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": "Java"
}
{
"_id": ObjectId("..."),
"name": "Mikky",
"age": 31,
"phone_no": 8654793212,
"company": "javatpoint",
"skills": ".Net"
}
{
"_id": ObjectId("..."),
"name": "John",
"age": 28,
"phone_no": 1234567890,
"company": "techguy",
"skills": "JavaScript"
}
{
"_id": ObjectId("..."),
"name": "John",
"age": 28,
"phone_no": 1234567890,
"company": "techguy",
"skills": "Python"
}
{
"_id": ObjectId("..."),
"name": "John",
"age": 28,
"phone_no": 1234567890,
"company": "techguy",
"skills": "Go"
}
{
"_id": ObjectId("..."),
"name": "David",
"age": 40,
"phone_no": 5647382910,
"company": "devteam",
"skills": "Ruby"
}
{
"_id": ObjectId("..."),
"name": "David",
"age": 40,
"phone_no": 5647382910,
"company": "devteam",
"skills": "PHP"
}
{
"_id": ObjectId("..."),
"name": "David",
"age": 40,
"phone_no": 5647382910,
"company": "devteam",
"skills": "Node.js"
}
Explanation:
- Each
skillsentry is now an individual document. - Documents with empty arrays (
Alice) are omitted from the results
Example 2: Using MongoDB $unwind on an Embedded Array
In many cases, documents contain nested arrays within objects, making it necessary to extract and analyze individual elements. The $unwind operator can be applied to embedded arrays, breaking them down into separate documents while preserving other fields.
Next, let’s create a new collection called products and populate it with some documents that include embedded arrays. We'll then apply $unwind to deconstruct these arrays for better queryability.
Sample Collection: products
[
{
"_id": "1",
"items": [
{
"name": "copy",
"work": ["write", "office"],
"cost": 10,
"total_quantity": 5
},
{
"name": "pencil",
"work": ["write", "school"],
"cost": 2,
"total_quantity": 5
}
]
},
{
"_id": "2",
"items": [
{
"name": "monitor",
"work": ["college", "office"],
"cost": 5000,
"total_quantity": 1
},
{
"name": "mouse",
"work": ["laptop", "CPU"],
"cost": 300,
"total_quantity": 5
}
]
}
]
Query: Unwinding items array
db.products.aggregate([
{
$unwind: "$items"
}
]);
Output:
{
"_id": "1",
"items": {
"name": "copy",
"work": ["write", "office"],
"cost": 10,
"total_quantity": 5
}
}
{
"_id": "1",
"items": {
"name": "pencil",
"work": ["write", "school"],
"cost": 2,
"total_quantity": 5
}
}
{
"_id": "2",
"items": {
"name": "monitor",
"work": ["college", "office"],
"cost": 5000,
"total_quantity": 1
}
}
{
"_id": "2",
"items": {
"name": "mouse",
"work": ["laptop", "CPU"],
"cost": 300,
"total_quantity": 5
}
}
Explanation:
- The products collection has documents with an array field items.
- Using $unwind: "$items" splits each element of the items array into a separate document.
- Other fields (like _id) are preserved in each new document
$unwind with non-array field
The $unwind stage normally splits array fields into multiple documents.
- Field Exists but Is Not an Array: If the field exists and contains a single value (not an array), $unwind treats the value as a single-element array. The document appears once in the output with the field unchanged.
- Field Does Not Exist: By default, documents where the field does not exist are excluded from the output.This behavior can be changed using the option preserveNullAndEmptyArrays: true, which retains documents with missing or empty fields.
Example:
Collection students:
{ "_id": 1, "name": "Alice", "scores": 90 }
{ "_id": 2, "name": "Bob", "scores": [85, 88] }
{ "_id": 3, "name": "Charlie" }
Aggregation:
db.students.aggregate([
{ $unwind: "$scores" }
])
Output:
{ "_id" : 1, "name" : "Alice", "scores" : 90 }
{ "_id" : 2, "name" : "Bob", "scores" : 85 }
{ "_id" : 2, "name" : "Bob", "scores" : 88 }
Explanation:
- Alice’s scores is a single value → appears once.
- Bob’s scores array [85, 88] → split into two documents.
- Charlie has no scores field → excluded by default.
Using preserveNullAndEmptyArrays
The preserveNullAndEmptyArrays option ensures that documents with missing or null fields are kept in the output when using $unwind.
Example:
{ "_id": 1, "name": "Alice", "scores": 90 }
{ "_id": 2, "name": "Bob", "scores": [85, 88] }
{ "_id": 3, "name": "Charlie" }
Aggregation:
db.students.aggregate([
{ $unwind: { path: "$scores", preserveNullAndEmptyArrays: true } }
])
Output:
{ "_id" : 1, "name" : "Alice", "scores" : 90 }
{ "_id" : 2, "name" : "Bob", "scores" : 85 }
{ "_id" : 2, "name" : "Bob", "scores" : 88 }
Explanation:
- preserveNullAndEmptyArrays: true keeps documents where the field is missing or null.
- Alice’s single value → appears once.
- Bob’s array → split into two documents.
- Charlie has no scores → included in the output.
$unwind with includeArrayIndex
The $unwind operator splits array fields into separate documents. The includeArrayIndex option adds a new field that stores the index position of each element in the original array. This helps in grouping or ordering elements based on their position.
Example:
{ "_id": 1, "name": "Alice", "scores": [90, 85, 88] }Query:
db.students.aggregate([
{ $unwind: { path: "$scores", includeArrayIndex: "scoreIndex" } }
])
Output:
{ "_id" : 1, "name" : "Alice", "scores" : 90, "scoreIndex" : 0 }
{ "_id" : 1, "name" : "Alice", "scores" : 85, "scoreIndex" : 1 }
{ "_id" : 1, "name" : "Alice", "scores" : 88, "scoreIndex" : 2 }
Explanation:
- The scores array is split into separate documents.
- scoreIndex shows the original position of each element in the array.
- This is useful for grouping, ordering, or referencing array elements by their position during aggregation.