Bird
Raised Fist0
MongoDBquery~5 mins

$unwind for flattening arrays in MongoDB

Choose your learning style10 modes available
Introduction

$unwind helps to take a list inside a document and make each item in that list its own document. This makes it easier to work with each item separately.

You have a list of hobbies inside a person's record and want to see each hobby on its own line.
You want to count how many times each item appears in a list inside documents.
You need to filter or sort based on items inside a list.
You want to join or match documents based on items inside an array.
You want to create reports that show each element of an array as a separate entry.
Syntax
MongoDB
db.collection.aggregate([
  { $unwind: "$arrayField" }
])

Replace collection with your collection name.

Replace arrayField with the name of the array you want to flatten.

Examples
This takes each hobby from the hobbies array and makes a separate document for it.
MongoDB
db.people.aggregate([
  { $unwind: "$hobbies" }
])
Flattens the items array in orders so each item is its own document.
MongoDB
db.orders.aggregate([
  { $unwind: "$items" }
])
If tags array is empty or missing, $unwind will remove that document by default.
MongoDB
db.products.aggregate([
  { $unwind: "$tags" }
])
This keeps documents even if the tags array is empty or missing.
MongoDB
db.products.aggregate([
  { $unwind: { path: "$tags", preserveNullAndEmptyArrays: true } }
])
Sample Program

This example inserts four people. Some have hobbies, some have empty or missing hobbies. The $unwind stage flattens hobbies so each hobby is a separate document. The option preserveNullAndEmptyArrays: true keeps people with no hobbies in the result.

MongoDB
db.people.insertMany([
  { _id: 1, name: "Alice", hobbies: ["reading", "swimming"] },
  { _id: 2, name: "Bob", hobbies: ["cycling"] },
  { _id: 3, name: "Carol", hobbies: [] },
  { _id: 4, name: "Dave" }
])

// Use $unwind to flatten hobbies

const result = db.people.aggregate([
  { $unwind: { path: "$hobbies", preserveNullAndEmptyArrays: true } }
]).toArray()

printjson(result)
OutputSuccess
Important Notes

Time complexity depends on the number of documents and the size of the arrays inside them.

Space complexity increases because one document with an array becomes many documents.

A common mistake is forgetting preserveNullAndEmptyArrays and losing documents with empty or missing arrays.

Use $unwind when you want to work with each array item separately. If you want to keep arrays as they are, do not use $unwind.

Summary

$unwind turns each item in an array into its own document.

It helps to analyze or filter array items easily.

Remember to use preserveNullAndEmptyArrays if you want to keep documents without array items.

Practice

(1/5)
1. What does the $unwind stage do in a MongoDB aggregation pipeline?
easy
A. It turns each element of an array field into a separate document.
B. It groups documents by a specified field.
C. It sorts documents in ascending order.
D. It removes duplicate documents from the collection.

Solution

  1. Step 1: Understand the purpose of $unwind

    $unwind takes an array field and creates a new document for each element in that array.
  2. Step 2: Compare with other pipeline stages

    Grouping, sorting, and removing duplicates are done by other stages like $group, $sort, and $match, not $unwind.
  3. Final Answer:

    It turns each element of an array field into a separate document. -> Option A
  4. Quick Check:

    $unwind flattens arrays = B [OK]
Hint: Remember: $unwind splits arrays into separate documents [OK]
Common Mistakes:
  • Confusing $unwind with $group or $sort
  • Thinking $unwind removes duplicates
  • Assuming $unwind sorts documents
2. Which of the following is the correct syntax to use $unwind on the field items in an aggregation pipeline?
easy
A. { $unwind: "$items" }
B. { $unwind: { path: "$items" } }
C. { $unwind: items }
D. { $unwind: { path: "items" } }

Solution

  1. Step 1: Recall correct $unwind syntax

    The $unwind stage requires the field path as a string with a $ prefix inside an object with key path.
  2. Step 2: Evaluate each option

    { $unwind: "$items" } misses the path key. { $unwind: items } lacks quotes and $ prefix. { $unwind: { path: "items" } } misses the $ prefix. Only { $unwind: { path: "$items" } } correctly uses { path: "$items" }.
  3. Final Answer:

    { $unwind: { path: "$items" } } -> Option B
  4. Quick Check:

    Use { path: "$field" } for $unwind [OK]
Hint: Use { path: "$field" } syntax for $unwind [OK]
Common Mistakes:
  • Omitting quotes or $ in field path
  • Using string without path key
  • Passing field name without $ prefix
3. Given the collection documents:
{ "_id": 1, "tags": ["red", "blue"] }
{ "_id": 2, "tags": ["green"] }

What will be the output documents after applying this pipeline?
[ { $unwind: "$tags" } ]
medium
A. [{ "_id": 1, "tags": "blue" }, { "_id": 2, "tags": "green" }]
B. [{ "_id": 1, "tags": ["red", "blue"] }, { "_id": 2, "tags": ["green"] }]
C. [{ "_id": 1, "tags": "red" }, { "_id": 2, "tags": "green" }]
D. [{ "_id": 1, "tags": "red" }, { "_id": 1, "tags": "blue" }, { "_id": 2, "tags": "green" }]

Solution

  1. Step 1: Understand $unwind effect on arrays

    $unwind creates one document per array element, duplicating other fields.
  2. Step 2: Apply $unwind to each document

    Document 1 with tags ["red", "blue"] becomes two documents with tags "red" and "blue". Document 2 with tags ["green"] becomes one document with tag "green".
  3. Final Answer:

    [{ "_id": 1, "tags": "red" }, { "_id": 1, "tags": "blue" }, { "_id": 2, "tags": "green" }] -> Option D
  4. Quick Check:

    Each array element becomes a document = A [OK]
Hint: Each array item becomes a separate document after $unwind [OK]
Common Mistakes:
  • Expecting original array to remain intact
  • Missing duplicated documents for multiple array items
  • Confusing $unwind with $group
4. You run this aggregation pipeline:
[ { $unwind: "$comments" } ]

But some documents have no comments field or have it as an empty array. What problem occurs and how to fix it?
medium
A. Documents without comments are dropped; fix by adding preserveNullAndEmptyArrays: true in $unwind.
B. Syntax error occurs; fix by quoting the field name.
C. Documents are duplicated; fix by removing $unwind.
D. No problem occurs; $unwind handles missing fields automatically.

Solution

  1. Step 1: Identify behavior of $unwind on missing or empty arrays

    By default, $unwind removes documents where the array field is missing or empty.
  2. Step 2: Use preserveNullAndEmptyArrays option to keep those documents

    Adding preserveNullAndEmptyArrays: true keeps documents without the array or with empty arrays in the output.
  3. Final Answer:

    Documents without comments are dropped; fix by adding preserveNullAndEmptyArrays: true in $unwind. -> Option A
  4. Quick Check:

    Use preserveNullAndEmptyArrays to keep empty/missing arrays [OK]
Hint: Add preserveNullAndEmptyArrays:true to keep missing arrays [OK]
Common Mistakes:
  • Ignoring dropped documents without array
  • Assuming $unwind never drops documents
  • Confusing syntax errors with missing field issues
5. You have documents with a field orders which is an array of objects:
{ "_id": 1, "orders": [{ "item": "apple", "qty": 5 }, { "item": "banana", "qty": 3 }] }

You want to create a list of all items ordered with their quantities as separate documents, but also keep documents that have no orders. Which aggregation pipeline correctly does this?
hard
A. [ { $unwind: { path: "$orders" } }, { $match: { orders: { $exists: true } } } ]
B. [ { $unwind: "$orders" }, { $project: { item: "$orders.item", qty: "$orders.qty" } } ]
C. [ { $unwind: { path: "$orders", preserveNullAndEmptyArrays: true } }, { $project: { item: "$orders.item", qty: "$orders.qty" } } ]
D. [ { $project: { item: "$orders.item", qty: "$orders.qty" } } ]

Solution

  1. Step 1: Use $unwind with preserveNullAndEmptyArrays to keep documents without orders

    [ { $unwind: { path: "$orders", preserveNullAndEmptyArrays: true } }, { $project: { item: "$orders.item", qty: "$orders.qty" } } ] uses preserveNullAndEmptyArrays: true to keep documents that have no orders.
  2. Step 2: Project only the needed fields from the unwound orders array

    The $project stage extracts item and qty from each order object.
  3. Final Answer:

    [ { $unwind: { path: "$orders", preserveNullAndEmptyArrays: true } }, { $project: { item: "$orders.item", qty: "$orders.qty" } } ] -> Option C
  4. Quick Check:

    Unwind with preserveNullAndEmptyArrays + project fields = D [OK]
Hint: Use preserveNullAndEmptyArrays:true to keep docs without array [OK]
Common Mistakes:
  • Omitting preserveNullAndEmptyArrays and losing documents
  • Projecting fields before unwind causing errors
  • Using $match to filter instead of preserveNullAndEmptyArrays
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard