MongoDB $out Stage

Beginner
5 min read
Updated: Oct 2026
4 examples
Live preview

What Is $out?

$out is an aggregation output stage that writes all pipeline results into a collection, replacing that collection’s contents on each run—ideal for snapshots and materialized views.

Remember
$out = "save the full pipeline result as a collection"

{ $out: "sales_by_region" }

// or another database (4.4+):
{ $out: { db: "analytics", coll: "daily_report" } }

MUST be the LAST stage
re-run → target collection is REPLACED
cannot $out into the same collection you read from

full snapshot → $out
incremental upsert → $merge
UsePrefer
Full snapshot / replace collection$out
Incremental upsert (keep other rows)$merge
Filter before writing$match then $out

How to Use $out in MongoDB

Shape your result set, then end the pipeline with $out.

  1. Filter/shape with $match, $group, $project.
  2. End with { $out: "targetName" } (must be last).
  3. Use a different collection than the one you read from.
  4. Optional: { $out: { db: "…", coll: "…" } } for another database.
FormMeaning
{ $out: "name" }Write to collection name in the current DB
{ $out: { db, coll } }Write to another database (4.4+)
PositionMust be the last stage
Re-runTarget contents are replaced

Pseudocode

Pseudocode
assert: $out is the last stage
assert: target collection ≠ input collection

results = all documents from prior stages

write results into target collection
  (atomically replace prior contents)

pipeline ends — no cursor returned to client
read the target with find() afterward

Cheat Sheet

Keep these facts handy for snapshot exports.

TopicDetail
TypeTerminal aggregation output stage
PositionMust be last
SemanticsFull replace of target contents
Same collectionNot allowed as read + write target
Cross-DB{ db, coll } form (4.4+)
vs $mergeSnapshot replace vs row upsert
Client resultNo documents returned; check target with find()

Live Preview

Compare a full snapshot write vs keeping old rows (teaching model—not a live MongoDB write).

Target starts with east + west. Pipeline result is only east + north.

Target after write
east   total: 120
north  total: 40
(west removed — full replace)

Worked Walkthrough — Sales by Region Snapshot

Goal: group orders by region and save the full result as sales_by_region.

Calculation
orders:
  { region: "east",  amount: 50 }
  { region: "east",  amount: 70 }
  { region: "north", amount: 40 }

$group by region →
  { _id: "east",  total: 120 }
  { _id: "north", total: 40 }

$out: "sales_by_region"
  → collection becomes exactly those two docs

re-run tomorrow with different totals
  → whole collection replaced again
  → old regions not in the new result disappear

MongoDB Examples

We use four sample order lines in orders. Insert them once in Example 1, then run each $out pipeline in Examples 2–4. Copy blocks into mongosh.

Example 1 — Sample Data

Four order lines with region and amount. We load them into a collection named orders for grouping and $out demos.

mongosh
db.orders.drop()

db.orders.insertMany([
  { _id: 1, region: "east",  amount: 50 },
  { _id: 2, region: "east",  amount: 70 },
  { _id: 3, region: "north", amount: 40 },
  { _id: 4, region: "west",  amount: 25 }
])

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps four documents in orders. Run find() to see them:

mongosh
db.orders.find()

Each order line uses these fields:

Field in MongoDBExamplePlain English
_id1Document id for this lesson
region"east"Sales region to group by
amount50Line amount to sum in $group
Picture
orders (raw lines):

  _id   region   amount
  ---   ------   ------
    1   east         50
    2   east         70   → east total 120
    3   north        40
    4   west         25

Examples 2–4 group these rows and $out the snapshot.

How It Works

  1. Reset once. drop() clears old rows; insertMany loads four order lines.
  2. Two fields matter. region is the group key; amount is summed.
  3. find() confirms storage. You see the same rows the pipeline reads.
  4. Reuse. Examples 2–4 assume this data is already in orders.

Example 2 — Basic $out

Goal: group sales by region from Example 1 and save the full result into sales_by_region.

mongosh
db.orders.aggregate([
  {
    $group: {
      _id: "$region",
      total: { $sum: "$amount" }
    }
  },
  {
    $project: {
      _id: 0,
      region: "$_id",
      total: 1
    }
  },
  { $out: "sales_by_region" }
])

db.sales_by_region.find().sort({ region: 1 })

How It Works

  1. Example 1 orders. Four lines with east (50+70), north (40), west (25).
  2. Group. Sum amount per region.
  3. Project. Rename _id → region for a clean report shape.
  4. $out last. Writes every result doc into sales_by_region; verify with find().

Example 3 — Re-run Replaces

Goal: run $out again on the same Example 1 orders data with a tighter filter—the whole target is replaced and old regions vanish.

mongosh
// Filter to east + north only, then overwrite the snapshot
db.orders.aggregate([
  { $match: { region: { $in: ["east", "north"] } } },
  {
    $group: {
      _id: "$region",
      total: { $sum: "$amount" }
    }
  },
  {
    $project: {
      _id: 0,
      region: "$_id",
      total: 1
    }
  },
  { $out: "sales_by_region" }
])

db.sales_by_region.find().sort({ region: 1 })
// west is gone — full replace, not an upsert

How It Works

  1. Same Example 1 rows. No re-insert; still four order documents.
  2. $match first. Pipeline result has only east + north totals.
  3. $out replaces. sales_by_region becomes exactly that result set.
  4. west disappears. Unmatched old snapshot rows are not kept—use $merge to keep them.

Example 4 — Cross-Database

Goal: group the Example 1 orders and write the snapshot into another database (MongoDB 4.4+ syntax).

mongosh
db.orders.aggregate([
  {
    $group: {
      _id: "$region",
      total: { $sum: "$amount" }
    }
  },
  {
    $project: {
      _id: 0,
      region: "$_id",
      total: 1
    }
  },
  {
    $out: {
      db: "analytics",
      coll: "sales_by_region"
    }
  }
])

db.getSiblingDB("analytics").sales_by_region.find()
// needs privileges on the analytics database

How It Works

  1. Example 1 source. Reads the same four orders documents.
  2. Same group/project. Region totals match Example 2’s pipeline shape.
  3. Document form. db + coll pick the target database.
  4. Inspect. getSiblingDB("analytics") then find() (needs write privileges).

Edge Cases & Pitfalls

Check these before writing production snapshots.

last

Must be the last stage

Nothing may follow $out. Put filters and projections before it.

same

Cannot out into the input collection

Write to a different name. For in-place refresh, write to a temp collection then rename.

replace

Re-run wipes previous target docs

Anything not in the new pipeline result disappears. Use $merge to keep unmatched rows.

cursor

No documents returned to the client

After $out, read the target with find(). The aggregate call does not stream the written docs back.

Performance Notes

ApproachCostBest for
$match before $outSmaller snapshotFiltered exports
Full collection $outRewrite whole targetNightly materialized views
Frequent tiny updates via $outHeavy rewritesPrefer $merge
Indexes on targetRecreate after replace if neededPlan index strategy for snapshots

Use $out when the whole result is the source of truth. For small incremental changes, $merge is usually cheaper.

Key Takeaways

  • Role: write the full pipeline result into a collection.
  • Rules: must be last; cannot target the input collection.
  • Semantics: each run replaces the target contents.
  • vs $merge: full snapshot vs row-by-row upsert.

One line: $out answers “how do I save this whole aggregation result as a collection snapshot?”

Frequently Asked Questions

$out writes all documents from the aggregation pipeline into a target collection. On each run, the target is replaced with the new pipeline output—ideal for full snapshots and materialized views.
$out replaces the entire target collection. $merge upserts row-by-row and can keep unmatched rows. Use $out for a complete refresh; use $merge for incremental updates.
Yes. $out is a terminal output stage—nothing can follow it. The pipeline finishes by writing to the target instead of returning a result cursor to the client.
No. You cannot $out into the collection the pipeline reads from. Write to a different collection name, or use a temp collection then rename.
Yes (MongoDB 4.4+): { $out: { db: "analytics", coll: "daily_report" } }. You need insert privileges on the target.
The user needs privileges to create/replace documents in the target collection. Test on staging before writing production report collections.

Next: $planCacheStats

Inspect the plan cache to understand how MongoDB executes queries on a collection.

$planCacheStats tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful