MongoDB $replaceWith Stage

Beginner
⏱️ 11 min read
📚 Updated: Jul 2026
🎯 5 Examples
Aggregation

What You’ll Learn

The $replaceWith stage replaces each document entirely with a new document—same behavior as $replaceRoot, but with shorter, cleaner syntax. It is ideal for promoting nested objects, flattening unwound arrays, and building computed output documents.

01

Shorthand

No newRoot.

02

Promote nested

"$field".

03

Build new doc

Expressions.

04

After $unwind

Flat rows.

05

$ifNull

Safe guard.

06

vs $replaceRoot

Same result.

Definition and Usage

$replaceWith evaluates an expression for each input document and outputs that expression as the new document—discarding all original top-level fields unless they appear in the replacement.

💡
Beginner tip

These two are equivalent:
{ $replaceRoot: { newRoot: "$profile" } }
{ $replaceWith: "$profile" }
$replaceWith saves the newRoot wrapper—prefer it when pipelines get long.

Use after $lookup, $unwind, or $group when the shape you need is a nested subdocument or a computed object—not a trimmed version of the current root.

📝 Syntax

$replaceWith takes a single expression that must resolve to a document:

mongosh
{ $replaceWith: <expression> }

Syntax Rules

  • Document only — the expression must evaluate to a document. Strings, numbers, or bare arrays cause an error.
  • Full replacement — the input document is discarded; output is exactly the evaluated expression.
  • Field path{ $replaceWith: "$nestedField" } promotes that subdocument to root.
  • Literal object{ $replaceWith: { key: "$field", computed: { $add: [1, 2] } } } builds a new shape.
  • Missing field — if the expression resolves to missing/null, the stage errors. Use $ifNull or $mergeObjects.
  • Equivalent to{ $replaceRoot: { newRoot: expr } } (MongoDB 4.2+).
mongosh
db.developers.aggregate([
  {
    $replaceWith: {
      $mergeObjects: [
        { _id: "$_id" },
        "$name"
      ]
    }
  }
])

⚡ Quick Reference

QuestionAnswer
What it doesReplaces entire document with the expression result
Syntax{ $replaceWith: expression }
Promote nested{ $replaceWith: "$embedded" }
vs $replaceRootIdentical behavior; $replaceWith is shorter
Missing nested fieldUse $ifNull, $mergeObjects, or $match
After $unwind{ $replaceWith: "$arrayField" } for flat rows
Promote
$replaceWith:
  "$profile"

Lift subdoc

Build
$replaceWith: {
  total: { $sum: ... }
}

New shape

Guard
$ifNull: [
  "$x", {}
]

No error

= replaceRoot
newRoot: expr
≡ $replaceWith: expr

Equivalent

🧰 Replacement Expression Patterns

The single argument to $replaceWith determines the output document. Common patterns:

Field path string Promote

Shortest form—replace the document with an embedded subdocument at the given path.

{ $replaceWith: "$grades" }
{ $replaceWith: "$imdb" }
Computed object Build

Construct a new document from field paths, literals, and aggregation expressions including $$NOW.

{ $replaceWith: {
  _id: "$_id",
  amount: { $multiply: ["$price", "$qty"] },
  asof: "$$NOW"
}}
$mergeObjects Defaults

Merge defaults with a nested field or $$ROOT—prevents errors and fills missing keys.

{ $replaceWith: {
  $mergeObjects: [
    { dogs: 0, cats: 0 },
    { $ifNull: ["$pets", {}] }
  ]
}}
$arrayToObject Pivot

Convert k/v array pairs into a document—advanced pattern for pivot-style reports after $group.

{ $replaceWith: {
  $arrayToObject: "$items2"
}}

Examples Gallery

Developers, sales, and orders—promote name subdocuments, build completion reports, flatten line items, normalize contacts, and compare syntax with $replaceRoot.

📚 Getting Started

Developers, sales, and orders for replaceWith labs.

Example 1 — Developers, sales, and orders

mongosh
db.developers.drop()
db.sales.drop()
db.orders.drop()

db.developers.insertMany([
  { _id: 1, name: { first: "Grace", last: "Hopper"  }, lang: "COBOL" },
  { _id: 2, name: { first: "John",  last: "Backus"  }, lang: "Fortran" },
  { _id: 3, lang: "Python", note: "Missing name object" }
])

db.sales.insertMany([
  { _id: 1, item: "butter",  price: 10, quantity: 2, status: "C", date: ISODate("2025-03-01T08:00:00Z") },
  { _id: 2, item: "cream",   price: 20, quantity: 1, status: "A", date: ISODate("2025-03-01T09:00:00Z") },
  { _id: 3, item: "jam",     price: 5,  quantity: 10, status: "C", date: ISODate("2025-03-15T09:00:00Z") },
  { _id: 4, item: "muffins", price: 5,  quantity: 10, status: "C", date: ISODate("2025-03-15T09:00:00Z") }
])

db.orders.insertMany([
  {
    _id: 1,
    customer: "Acme Corp",
    items: [
      { sku: "WGT-01", qty: 3, price: 29.99 },
      { sku: "WGT-02", qty: 1, price: 49.99 }
    ]
  },
  {
    _id: 2,
    customer: "Beta LLC",
    items: [
      { sku: "LMP-01", qty: 2, price: 19.99 }
    ]
  }
])

How It Works

Three collections: embedded name objects (one doc missing name), sales with status codes, and orders with items arrays—covering promote, compute, and unwind patterns.

Example 2 — Promote name subdocument (with $ifNull guard)

mongosh
db.developers.aggregate([
  {
    $replaceWith: {
      $ifNull: [
        {
          $mergeObjects: [
            { _id: "$_id", lang: "$lang" },
            "$name"
          ]
        },
        { _id: "$_id", lang: "$lang", first: "", last: "", missingName: true }
      ]
    }
  },
  { $sort: { last: 1 } }
])

// Grace Hopper → { _id: 1, lang: "COBOL", first: "Grace", last: "Hopper" }
// Doc 3 (no name) → fallback with missingName: true — no pipeline error
// Shorter than: $replaceRoot: { newRoot: { ... } }

How It Works

Bare $replaceWith: "$name" fails on doc 3. $ifNull plus $mergeObjects preserves _id and lang while lifting first and last to root.

Example 3 — Completed sales report with $$NOW

mongosh
db.sales.aggregate([
  { $match: { status: "C" } },
  {
    $replaceWith: {
      _id: "$_id",
      item: "$item",
      amount: { $multiply: ["$price", "$quantity"] },
      status: "Complete",
      asofDate: "$$NOW"
    }
  },
  { $sort: { amount: -1 } }
])

// status "C" → completed; "A" excluded by $match
// amount = price × quantity (replaces separate price/qty fields)
// asofDate stamped at pipeline run time

How It Works

When there is no nested object to promote, build the replacement document inline. Only fields listed in the object appear in output—ideal for ETL snapshots before $out.

📈 Practical Patterns

Unwind arrays, normalize schemas, and compare syntax.

Example 4 — Flatten order line items after $unwind

mongosh
db.orders.aggregate([
  { $unwind: "$items" },
  {
    $replaceWith: {
      $mergeObjects: [
        "$items",
        {
          orderId: "$_id",
          customer: "$customer",
          lineTotal: {
            $multiply: ["$items.qty", "$items.price"]
          }
        }
      ]
    }
  },
  { $sort: { orderId: 1, sku: 1 } }
])

// Each line item becomes its own top-level document
// orderId + customer merged from parent (lost on bare "$items")
// lineTotal computed at replace time

How It Works

$unwind plus $replaceWith is a common e-commerce pattern: one row per line item for analytics, invoicing, or export. Merge parent context so promotion does not drop order metadata.

Example 5 — $replaceWith vs $replaceRoot (equivalent pipelines)

mongosh
// Same result — $replaceWith (shorter)
db.developers.aggregate([
  { $match: { "name.first": { $exists: true } } },
  {
    $replaceWith: {
      $mergeObjects: ["$name", { _id: "$_id", language: "$lang" }]
    }
  }
])

// Same result — $replaceRoot (explicit newRoot)
db.developers.aggregate([
  { $match: { "name.first": { $exists: true } } },
  {
    $replaceRoot: {
      newRoot: {
        $mergeObjects: ["$name", { _id: "$_id", language: "$lang" }]
      }
    }
  }
])

// Prefer $replaceWith in new pipelines
// Use $match first when some docs lack the nested field

How It Works

MongoDB 4.2 introduced $replaceWith as syntactic sugar. Teams often standardize on one form—many prefer $replaceWith for brevity. See also $replaceRoot for the wrapped variant.

🧠 How $replaceWith Works

1

Document enters

Each document from the prior stage arrives with its current nested or flat shape.

Input
2

Expression evaluated

MongoDB evaluates the $replaceWith argument—path, object literal, $mergeObjects, or $ifNull.

Evaluate
3

Document swapped

The input is discarded; the evaluated document becomes the new root passed downstream.

Replace
=

New document stream

Downstream stages see promoted, flattened, or recomputed documents ready for sort, merge, or export.

📝 Notes

  • $replaceWith and $replaceRoot produce identical results—only syntax differs.
  • The expression must resolve to a document—not a scalar or array alone.
  • Missing nested fields cause a pipeline error unless guarded with $ifNull or filtered with $match.
  • Original _id is dropped unless explicitly included in the replacement expression.
  • After $unwind, merge parent fields into the replacement when you still need order or customer context.
  • Previous topic: $replaceRoot. Next: $sample.

Conclusion

$replaceWith is the concise way to swap an entire document: promote nested objects, build computed reports, and flatten unwound arrays—with one expression instead of a newRoot wrapper.

Guard missing fields, preserve _id when needed, and pair with $unwind for line-item exports. Next: $sample to randomly select documents from a pipeline.

💡 Best Practices

✅ Do

  • Prefer $replaceWith over $replaceRoot in new pipelines for readability
  • Use $ifNull or $mergeObjects when nested fields may be absent
  • Include _id: "$_id" when the replacement must keep the original identifier
  • Merge parent context after $unwind before replacing with array elements
  • Follow with $project only if you need a final trim after replacement

❌ Don’t

  • Use bare $replaceWith: "$field" without verifying the field exists on all docs
  • Expect original top-level fields to survive—they are fully replaced
  • Use $replaceWith when $project suffices for simple field selection
  • Pass non-document values as the replacement expression
  • Mix up $replaceWith and $set$set adds fields without replacing the root

Key Takeaways

Knowledge Unlocked

Five things to remember about $replaceWith

Use these points whenever you replace documents in aggregation pipelines.

5
Core concepts
🔃 02

= replaceRoot

Same behavior.

Equiv
📦 03

Promote

"$nested".

Pattern
📈 04

+ $unwind

Flat rows.

Arrays
🛠 05

$ifNull

Safe guard.

Errors

❓ Frequently Asked Questions

$replaceWith replaces the entire input document with a new document specified by a single expression—identical in behavior to $replaceRoot. Use it to promote nested subdocuments, flatten after $unwind, or output a freshly computed document shape.
Same operation, different syntax. $replaceRoot wraps the expression: { $replaceRoot: { newRoot: expression } }. $replaceWith is shorter: { $replaceWith: expression }. Pick whichever reads better in your pipeline.
The original _id is replaced unless you include it in the replacement expression—e.g. $replaceWith: "$profile" drops _id if profile has none. Set _id: "$_id" explicitly or use $mergeObjects to preserve it.
The stage errors and fails. Guard with $ifNull, $mergeObjects with defaults, or $match with $exists before $replaceWith to skip documents without the nested field.
$replaceWith was added in MongoDB 4.2 as a shorthand for $replaceRoot. Both stages remain fully supported—use $replaceWith for concise pipelines and $replaceRoot when the newRoot wrapper makes intent clearer.
$project trims or reshapes fields at the current document level. $replaceWith discards the entire input document and substitutes the expression result as the new root. Use $replaceWith to flatten nested objects; use $project for simple field selection.
Did you know?

MongoDB added $replaceWith and $set together in version 4.2 as part of a broader push for cleaner update-style syntax in aggregation. The Node.js driver accepts .replaceWith(expr) as a pipeline builder method—mirroring the stage name directly. See the official $replaceWith docs.

Continue the Stages Series

Reshape documents with $replaceWith, then sample randomly with $sample.

Next: $sample →

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.

5 people found this page helpful