MongoDB $set Stage

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

What You’ll Learn

The $set stage adds or overwrites fields on each document passing through the pipeline—computed totals, formatted labels, flags, and nested updates—without dropping the rest of the document.

01

Add fields

New columns.

02

Overwrite

Replace values.

03

Expressions

$multiply etc.

04

$cond

If/else fields.

05

Nested

Dot notation.

06

= $addFields

Same stage.

Definition and Usage

$set is an aggregation stage that assigns one or more field values on every input document. Existing fields remain unless you assign to the same name—then the new expression replaces the old value. Think of it as “set these properties on each document as it flows through the pipeline.”

💡
Beginner tip

$project = choose which fields to show (reshape output).
$set = keep everything and add/update a few fields.
Since MongoDB 4.2, $set and $addFields are the same stage with different names.

Use $set after $match or $lookup when you need derived columns—line totals, full names, status labels, or audit timestamps—before $sort, $group, or returning results to the client.

📝 Syntax

$set accepts an object of field names mapped to values or aggregation expressions:

mongosh
{
  $set: {
    <field1>: <expression>,
    <field2>: <expression>,
    ...
  }
}

Syntax Rules

  • Field keys — plain names (total) or dot notation ("address.city") for nested paths.
  • Values — literals, field paths ("$price"), or any aggregation expression ($multiply, $concat, $cond, etc.).
  • Overwrite — assigning to an existing field name replaces its value for documents in this stage.
  • Preserves fields — unlike $project, fields you do not mention stay on the document.
  • Alias$set and $addFields are interchangeable (MongoDB 4.2+).
  • Multiple assignments — one $set stage can set many fields at once.
mongosh
db.orders.aggregate([
  {
    $set: {
      lineTotal: { $multiply: ["$price", "$quantity"] }
    }
  }
])

⚡ Quick Reference

QuestionAnswer
What it doesAdds or overwrites fields; keeps other fields intact
Same as$addFields (MongoDB 4.2+)
vs $project$set additive; $project reshapes output
Opposite$unset removes fields
Nested updateDot notation key: "meta.processed": true
Common placementAfter $match / $lookup, before $sort
Compute
{ $set: {
  total: {
    $multiply: [
      "$price", "$qty"
    ]
  }
}}

Math expression

Concat
fullName: {
  $concat: [
    "$first", " ",
    "$last"
  ]
}

String field

Conditional
label: {
  $cond: [ cond, then, else ]
}

If/else value

Timestamp
processedAt: "$$NOW"

System variable

🧰 $set Field Assignment Patterns

Each key in the $set object becomes a field assignment. These patterns cover most beginner use cases:

New field Add

Add a field that does not exist yet. All original fields remain on the document.

{ $set: {
  discount: 0.1,
  currency: "USD"
}}
Overwrite Replace

Assign to an existing field name to replace its value—useful for normalizing or flagging records.

{ $set: {
  status: "archived",
  active: false
}}
Dot notation Nested

Update one nested path without rebuilding the entire sub-document.

{ $set: {
  "shipping.trackingId": "$tracking"
}}
Expression Compute

Values can be any aggregation expression—arithmetic, strings, dates, arrays, or $cond.

{ $set: {
  tier: {
    $cond: [
      { $gte: ["$spend", 1000] },
      "gold", "silver"
    ]
  }
}}

Examples Gallery

Orders and customers collections—computed line totals, concatenated names, conditional labels, nested shipping updates, and enriching grouped results after $group.

📚 Getting Started

Sample orders and customers for $set labs.

Example 1 — Orders and customers collections

mongosh
db.orders.drop()
db.customers.drop()

db.orders.insertMany([
  { orderId: "O-1001", product: "Widget",  price: 25,  quantity: 4, status: "shipped" },
  { orderId: "O-1002", product: "Gadget",  price: 80,  quantity: 1, status: "pending" },
  { orderId: "O-1003", product: "Gizmo",   price: 15,  quantity: 10, status: "shipped" },
  { orderId: "O-1004", product: "Tool",    price: 120, quantity: 2, status: "cancelled" },
  { orderId: "O-1005", product: "Part",    price: 9,   quantity: 6, status: "shipped" }
])

db.customers.insertMany([
  { customerId: "C-01", first: "Ana",  last: "Rivera", spend: 850,  active: true  },
  { customerId: "C-02", first: "Ben",  last: "Cho",    spend: 1200, active: true  },
  { customerId: "C-03", first: "Cara", last: "Singh",  spend: 400,  active: false },
  { customerId: "C-04", first: "Dan",  last: "Wu",     spend: 2100, active: true  }
])

How It Works

Five orders and four customers give enough data for arithmetic, string concat, conditionals, and grouped enrichment examples below.

Example 2 — Compute lineTotal ($multiply)

mongosh
db.orders.aggregate([
  { $match: { status: "shipped" } },
  {
    $set: {
      lineTotal: { $multiply: ["$price", "$quantity"] }
    }
  },
  {
    $project: {
      _id: 0,
      orderId: 1,
      product: 1,
      price: 1,
      quantity: 1,
      lineTotal: 1
    }
  }
])

// O-1001: 25 × 4  = 100
// O-1003: 15 × 10 = 150
// O-1005: 9  × 6  = 54
// All original fields still exist until $project trims output

How It Works

The most common $set pattern: add a derived numeric field from existing columns. Filter first with $match, compute with $set, then optionally $project for API response shape.

Example 3 — Build fullName and overwrite display label

mongosh
db.customers.aggregate([
  {
    $set: {
      fullName: {
        $concat: ["$first", " ", "$last"]
      },
      label: {
        $concat: ["$last", ", ", "$first"]
      }
    }
  },
  {
    $project: {
      _id: 0,
      customerId: 1,
      fullName: 1,
      label: 1,
      spend: 1
    }
  }
])

// fullName → "Ana Rivera" (new field)
// label    → "Rivera, Ana" (new field)
// first and last still on document until excluded

How It Works

$concat inside $set builds display strings without changing stored data in the collection—transformations happen at query time in the pipeline.

📈 Practical Patterns

Conditionals, nested paths, and post-group enrichment.

Example 4 — Conditional tier label ($cond)

mongosh
db.customers.aggregate([
  {
    $set: {
      tier: {
        $cond: [
          { $gte: ["$spend", 2000] },
          "platinum",
          {
            $cond: [
              { $gte: ["$spend", 1000] },
              "gold",
              "silver"
            ]
          }
        ]
      },
      isVip: { $gte: ["$spend", 1000] }
    }
  },
  { $match: { active: true } },
  { $sort: { spend: -1 } }
])

// C-04 spend 2100 → tier: platinum, isVip: true
// C-02 spend 1200 → tier: gold,     isVip: true
// C-01 spend 850  → tier: silver,   isVip: false
// C-03 excluded by $match (inactive)

How It Works

$set with nested $cond implements business rules inline—tier bands, VIP flags, or status labels—before sorting or grouping.

Example 5 — Nested $set after $group (enrich summary rows)

mongosh
// Summarize shipped revenue by product, then enrich with labels
db.orders.aggregate([
  { $match: { status: "shipped" } },
  {
    $set: {
      lineTotal: { $multiply: ["$price", "$quantity"] }
    }
  },
  {
    $group: {
      _id: "$product",
      orderCount: { $sum: 1 },
      revenue: { $sum: "$lineTotal" }
    }
  },
  {
    $set: {
      product: "$_id",
      avgOrderValue: {
        $divide: ["$revenue", "$orderCount"]
      },
      "report.generatedAt": "$$NOW",
      "report.source": { $literal: "orders_pipeline" }
    }
  },
  {
    $project: {
      _id: 0,
      product: 1,
      orderCount: 1,
      revenue: 1,
      avgOrderValue: 1,
      report: 1
    }
  },
  { $sort: { revenue: -1 } }
])

// After $group, documents have _id, orderCount, revenue
// $set adds product name, avgOrderValue, nested report fields

How It Works

$set is not only for raw collection documents—use it after $group to flatten _id into readable fields, compute averages, and attach nested audit metadata with dot notation.

🧠 How $set Works

1

Document enters

Each document from the prior stage arrives with its current field set—possibly filtered or joined data.

Input
2

Expressions evaluated

MongoDB evaluates every value in the $set object—literals, field paths, or nested expressions like $multiply.

Evaluate
3

Fields merged

New keys are added; existing keys are overwritten. Unmentioned fields pass through unchanged.

Merge
=

Enriched document

The document exits with added or updated fields—ready for $sort, $group, or client output.

📝 Notes

  • $set and $addFields are aliases—same syntax since MongoDB 4.2.
  • Assigning to an existing field overwrites its value for pipeline documents—not a permanent collection update unless you use $merge or $out.
  • Use dot notation keys to update nested paths without replacing entire sub-documents.
  • $set does not remove fields—use $unset or $project exclusion for that.
  • Multiple $set stages in one pipeline are fine; each merges its assignments in order.
  • Previous topic: $search. Next: $skip.

Conclusion

$set enriches pipeline documents: add computed fields, overwrite values, set nested paths, and attach metadata—while keeping the rest of each document intact.

Reach for $project when reshaping output; use $set when you only need a few new or updated fields. Next: $skip to offset documents for pagination.

💡 Best Practices

✅ Do

  • Use $set for derived columns instead of repeating math in $project
  • Place $set after filters ($match) so expressions run on fewer documents
  • Use dot notation for single nested updates
  • Combine $set + $unset to add temp fields then clean up
  • Flatten $group output with $set: { product: "$_id" }

❌ Don’t

  • Use $set when you only need a slim API response—use $project
  • Assume pipeline $set updates the collection permanently
  • Replace entire nested objects when dot notation suffices
  • Forget that overwriting a field removes the old value in pipeline output
  • Chain ten separate $set stages when one stage with multiple keys works

Key Takeaways

Knowledge Unlocked

Five things to remember about $set

Use these points whenever you enrich documents mid-pipeline.

5
Core concepts
📈 02

Expressions

Compute values.

Syntax
🔗 03

= $addFields

Same stage.

Alias
📁 04

Dot notation

Nested paths.

Nested
📊 05

After $group

Enrich summaries.

Pattern

❓ Frequently Asked Questions

$set adds new fields to documents or overwrites existing field values using aggregation expressions. All other fields stay on the document unless you assign to the same name—making $set ideal when you want to enrich data without reshaping the whole document.
Yes. Since MongoDB 4.2, $set is an alias for $addFields—they accept identical syntax and behave the same way. Use whichever name reads clearer in your pipeline; many teams prefer $set when updating fields and $addFields when adding computed columns.
$project controls which fields appear in output—you include or exclude explicitly. $set is additive: every existing field remains unless you overwrite it. Use $set when adding a few computed fields; use $project when trimming or renaming the output shape.
Yes. If you set a field name that already exists, the new expression replaces the old value. Example: { $set: { status: "reviewed" } } overwrites status for every document passing through the stage.
Yes. Use keys like "address.city": "$cityName" to merge into nested objects, or assign whole nested objects: { profile: { displayName: "$name" } }. Dot notation updates one nested path without removing sibling nested fields.
$unset removes fields from documents. Pairs naturally with $set: add computed values with $set, then drop temporary or sensitive fields with $unset before returning results.
Did you know?

MongoDB introduced $set and $unset in version 4.2 as readable companions to $addFields—mirroring update operator names ($set / $unset) so pipeline field changes feel familiar if you already know updateOne syntax. See the official $set docs.

Continue the Stages Series

Enrich documents with $set, then paginate with $skip.

Next: $skip →

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