Filter concepts by levelShowing all levels.

MongoDB · $unwind and Array Processing

$unwind and Array Processing

Exploding arrays into per-element documents, its options, its cost, and when to avoid it entirely

  • 1 doc → N docsone output document per array element
  • Empty arrays vanishunless preserveNullAndEmptyArrays: true
  • Explosion compoundsevery later stage pays the multiplied document count
  • Filter/map firstskip $unwind entirely for a same-shape array transform

1.$unwind explodes arrays into documents

$unwind takes a document with an array field and outputs one copy of that document per array element, with the array field replaced by a single element each time. A document with a 5-element array becomes 5 pipeline documents.

Key features

  • $unwind: "$field" (or the object form { path: "$field", ... }) is the syntax
  • A document with an N-element array becomes N documents; each keeps every other field unchanged
  • A document with a 0-length array or a missing field is dropped entirely by default
  • The unwound field holds one element, not an array, in each output document
  • $unwind is almost always followed by $match, $group, or $sort operating on the now-scalar field
One document with a 3-element array becomes three

Input

{ _id: 1, tags: ["a", "b", "c"] } — one document

$unwind: "$tags"

the array field is exploded

Output

{ _id: 1, tags: "a" }, { _id: 1, tags: "b" }, { _id: 1, tags: "c" } — three documents

  1. Input — { _id: 1, tags: ["a", "b", "c"] } — one document
  2. $unwind: "$tags" — the array field is exploded
  3. Output — { _id: 1, tags: "a" }, { _id: 1, tags: "b" }, { _id: 1, tags: "c" } — three documents

Command

text
{ $unwind: "$field" } or { $unwind: { path: "$field", preserveNullAndEmptyArrays, includeArrayIndex } }

$unwind turns one document with an N-element array into N documents, each holding one element — and silently drops documents whose array is empty or missing, unless preserveNullAndEmptyArrays is set.

2.preserveNullAndEmptyArrays and includeArrayIndex

These are two options of the object form of $unwind. preserveNullAndEmptyArrays: true keeps a document even when its array is missing or empty, instead of dropping it. includeArrayIndex: "<name>" adds a field recording each output document's original position in the array.

Key features

  • Object form: { $unwind: { path: "$field", preserveNullAndEmptyArrays: true, includeArrayIndex: "idx" } }
  • preserveNullAndEmptyArrays: true outputs one document with the field set to null, instead of zero documents, for a missing/empty array
  • includeArrayIndex adds a new field holding the zero-based index of each element in the original array
  • For a missing array, includeArrayIndex is set to null rather than an index
  • Both options are independent — you can use either alone or both together

Command

text
{ $unwind: { path: "$field", preserveNullAndEmptyArrays: true, includeArrayIndex: "fieldIdx" } }

preserveNullAndEmptyArrays keeps documents that $unwind would otherwise drop; includeArrayIndex records each element's original position — both live only in the object form of $unwind.

3.The document-explosion trade-off

$unwind makes per-element logic easy to write, at the cost of multiplying the document count by array length at every downstream stage — a collection of 1 million documents averaging 20 array elements each becomes 20 million pipeline documents to process.

Key features

  • Document count after $unwind = sum of each surviving document's array length
  • A large or highly variable array length makes the multiplier unpredictable and hard to capacity-plan for
  • Filtering with $match before $unwind reduces the base document count the multiplier applies to
  • Filtering with $match after $unwind on the array field itself is often necessary and fine — the cost is in doing it before $unwind was possible
  • If the end goal is a scalar (a sum, a count) rather than per-element documents, $map/$filter/$reduce avoid the multiplication entirely

Command

text
// Push any $match that does not depend on the unwound field to before the $unwind stage

The explosion multiplier applies to every stage after $unwind, not just $unwind itself — filter down to the documents you need first, and skip $unwind entirely when a scalar result is all you actually want.

4.$filter/$map instead of full $unwind

When the goal is still "one document per input document" — just with a transformed or narrowed array field — reach for $filter or $map instead of $unwind. Only use $unwind when the goal genuinely is one output document per array element.

Key features

  • $filter and $map operate on the array in place — same document count in and out
  • $unwind is required only when a later stage ($group, $sort, $match) needs to treat each array element as its own document
  • Chaining $unwind then immediately $group back to the original _id is a strong signal that $filter/$map/$reduce would have skipped the round trip
  • A $lookup joining on an array field is one of the few cases $unwind is genuinely required for, to line up each element with its own joined document
  • $filter can also take a limit to cap how many matching elements are kept

Command

text
$filter: { input, as?, cond, limit? } · $map: { input, as?, in }

If the result is still "the same documents, with a transformed array field," use $filter/$map — reach for $unwind only when the result genuinely needs to be one document per array element.

Remember

  • $unwind turns one N-element-array document into N documents — a document-count multiplier.
  • A missing or empty array drops the document entirely, unless preserveNullAndEmptyArrays is set.
  • Filter down to the documents you need before $unwind, not after.
  • If the result should stay one document per input, use $filter/$map instead of unwind-then-regroup.

Pro tip

  • An $unwind immediately followed by a $group back to the original _id is almost always replaceable by $filter/$map.
  • includeArrayIndex is useful whenever downstream code needs to know an element's original position.

Next up

  • $lookup and Join-Like Operations
  • Window Functions and Advanced Analytics
  • Transactions

Sources

  • $unwind — mongodb.com/docs/manual/reference/operator/aggregation/unwind/
  • $filter — mongodb.com/docs/manual/reference/operator/aggregation/filter/
  • Checked 2026-09-10

Spotted a mistake on this page? Tell us.