MongoDB $unset Stage

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

What You’ll Learn

The $unset stage removes fields from pipeline documents—strip secrets, drop temporary helpers, and trim nested paths while keeping everything else on the document.

01

Remove fields

Array syntax.

02

Dot notation

Nested paths.

03

vs $set

Opposite.

04

vs $project

Drop vs shape.

05

Temp fields

After $set.

06

API cleanup

Safe output.

Definition and Usage

$unset is an aggregation stage that deletes specified fields from each document in the pipeline stream. Unlike $project, which defines the full output shape, $unset is subtractive—you name what to remove and keep the rest.

💡
Beginner tip

$set = add or overwrite a field.
$unset = delete a field.
$project: { password: 0 } also hides fields—but $unset reads clearly when removing several known fields by name.

Common uses: remove password hashes before API responses, drop internal _internalId fields, delete temporary sort keys added mid-pipeline, and strip nested paths like metadata.debug.

📝 Syntax

$unset takes an array of field name strings to remove:

mongosh
{
  $unset: [
    <field1>,
    <field2>,
    ...
  ]
}

// field names are plain strings — use quotes
// dot notation for nested: "address.zipCode"

Syntax Rules

  • Array of strings — each entry is a field path to remove: [ "password", "ssn" ].
  • Single field — use a one-element array: { $unset: [ "temp" ] }.
  • Nested paths — dot notation removes one nested key: "profile.internalNote".
  • Non-destructive to collection — only affects pipeline documents, not stored data (unless using $out / $merge).
  • Missing fields — unsetting a field that does not exist is safe—no error.
  • MongoDB 4.2+ — introduced alongside $set as a readable field-transform stage.
mongosh
db.users.aggregate([
  { $unset: [ "password", "internalId" ] }
])

⚡ Quick Reference

QuestionAnswer
What it doesRemoves listed fields from each document
Syntax{ $unset: [ "field1", "field2" ] }
Nested field"parent.child" in the array
Opposite$set / $addFields
vs $project$unset drops named fields; $project reshapes all output
Missing fieldNo error—stage continues normally
One field
{ $unset: [
  "password"
]}

Single removal

Many fields
{ $unset: [
  "a", "b", "c"
]}

Batch cleanup

Nested
{ $unset: [
  "meta.debug"
]}

Dot path

Pattern
$set →
use field →
$unset

Temp helper

🧰 $unset Field Removal Patterns

Each string in the array identifies a field path to delete from the document:

Top-level field Common

Remove a root-level key—password, token, legacy column—without touching siblings.

{ $unset: [ "passwordHash" ] }
Multiple fields Batch

One stage can drop many fields—cleaner than chaining multiple unset stages.

{ $unset: [
  "ssn", "internalId", "legacyFlag"
]}
Nested dot path Nested

Removes only the nested key; other keys inside the parent object remain.

{ $unset: [
  "address.geo", "audit.trace"
]}
After $set Pattern

Add a temporary computed field, use it downstream, then unset before returning.

{ $set: { _sortKey: "$score" } },
{ $sort: { _sortKey: -1 } },
{ $unset: [ "_sortKey" ] }

Examples Gallery

Users and orders collections—remove sensitive fields, nested paths, temporary helpers after $set, batch cleanup, and API-safe output shaping.

📚 Getting Started

Users and orders with fields to strip before display.

Example 1 — Users and orders collections

mongosh
db.users.drop()
db.orders.drop()

db.users.insertMany([
  {
    username: "ana_r",
    email: "ana@example.com",
    passwordHash: "scrypt$abc123",
    internalId: "USR-9001",
    profile: { displayName: "Ana Rivera", tier: "gold" },
    metadata: { source: "web", debug: { traceId: "t-001" } }
  },
  {
    username: "ben_c",
    email: "ben@example.com",
    passwordHash: "scrypt$def456",
    internalId: "USR-9002",
    profile: { displayName: "Ben Cho", tier: "silver" },
    metadata: { source: "mobile", debug: { traceId: "t-002" } }
  }
])

db.orders.insertMany([
  {
    orderId: "O-1001",
    userId: "USR-9001",
    product: "Widget",
    amount: 49,
    costPrice: 22,
    margin: 27
  },
  {
    orderId: "O-1002",
    userId: "USR-9002",
    product: "Gadget",
    amount: 129,
    costPrice: 80,
    margin: 49
  }
])

How It Works

Users carry sensitive and internal fields; orders include cost data you may want hidden from customer-facing APIs.

Example 2 — Remove password and internal ID (basic $unset)

mongosh
db.users.aggregate([
  { $unset: [ "passwordHash", "internalId" ] }
])

// passwordHash and internalId gone from output
// username, email, profile, metadata remain
// Original collection unchanged — pipeline-only transform

How It Works

The most common $unset use case: never expose credentials or internal identifiers in API responses—even if they exist in the stored document.

Example 3 — Remove nested debug path (dot notation)

mongosh
db.users.aggregate([
  { $unset: [ "metadata.debug" ] },
  {
    $project: {
      _id: 0,
      username: 1,
      email: 1,
      metadata: 1
    }
  }
])

// metadata.debug removed
// metadata.source still present
// profile and other top-level fields unchanged

How It Works

Dot notation in the unset array targets one nested key without deleting the entire metadata object—sibling nested fields stay intact.

📈 Practical Patterns

Temporary helpers and full API response cleanup.

Example 4 — Drop temporary sort key after $set

mongosh
// Rank orders by margin, then remove internal cost fields
db.orders.aggregate([
  {
    $set: {
      marginPct: {
        $multiply: [
          { $divide: ["$margin", "$amount"] },
          100
        ]
      }
    }
  },
  { $sort: { marginPct: -1 } },
  { $unset: [ "costPrice", "margin", "marginPct" ] },
  {
    $project: {
      _id: 0,
      orderId: 1,
      product: 1,
      amount: 1
    }
  }
])

// marginPct used for sorting only — unset before client
// costPrice and margin also stripped (confidential)

How It Works

Classic $set → use field → $unset workflow. Compute helpers mid-pipeline, then strip them so clients never see internal calculations.

Example 5 — Full API response cleanup (batch $unset)

mongosh
// Public user profile endpoint pipeline
db.users.aggregate([
  { $match: { username: "ana_r" } },
  {
    $unset: [
      "passwordHash",
      "internalId",
      "metadata.debug",
      "metadata.source"
    ]
  },
  {
    $set: {
      publicProfile: {
        name: "$profile.displayName",
        tier: "$profile.tier"
      }
    }
  },
  {
    $unset: [ "profile", "metadata" ]
  },
  { $project: { _id: 0, username: 1, email: 1, publicProfile: 1 } }
])

// Two $unset stages: first targeted nested keys,
// second drops whole objects replaced by publicProfile

How It Works

Production APIs often chain $unset, $set, and $project—unset removes sensitive paths, set builds the public shape, project caps the final field list.

🧠 How $unset Works

1

Document enters

Full document from prior stages—possibly enriched by $set or joined data.

Input
2

Fields matched

$unset walks the array of field paths and marks each for removal from the document copy.

Match
3

Keys deleted

Matched top-level or nested paths are removed; all other fields pass through unchanged.

Remove
=

Trimmed document

Downstream stages or the client receive a document without the unset fields—safer and leaner output.

📝 Notes

  • $unset argument is an array of strings—field paths to remove.
  • Use dot notation for nested keys: "metadata.debug".
  • Pipeline $unset does not change stored documents unless you use $out or $merge.
  • Unset a missing field safely—no error is raised.
  • Pair with $set: add temp fields, use them, then unset before returning.
  • Previous topic: $sortByCount. Next: $unwind.

Conclusion

$unset subtracts fields from pipeline documents: list paths in an array, use dot notation for nested keys, and strip secrets or temporary helpers before results reach the client.

For strict allow-list APIs, combine with $project. Next: $unwind to expand array fields into separate documents.

💡 Best Practices

✅ Do

  • Unset passwords, tokens, and internal IDs in every public-facing pipeline
  • Remove temporary $set fields after sorting or filtering on them
  • Batch multiple removals in one $unset array
  • Use dot notation to drop nested debug or audit paths only
  • Place $unset late in the pipeline—just before client output

❌ Don’t

  • Assume $unset deletes data from the collection permanently
  • Expose fields you forgot to list in $unset—review API pipelines carefully
  • Use $unset when you only need two fields—$project inclusion may be clearer
  • Unset _id if downstream stages still need it—unset last or use $project
  • Chain ten single-field unset stages when one array works

Key Takeaways

Knowledge Unlocked

Five things to remember about $unset

Use these points when cleaning pipeline document shape.

5
Core concepts
🔗 02

Dot paths

Nested keys.

Nested
🔄 03

vs $set

Opposite.

Pair
🛡️ 04

Secrets

Strip early.

Security
05

Temp fields

Unset last.

Pattern

❓ Frequently Asked Questions

$unset removes one or more fields from every document passing through the aggregation pipeline. All other fields stay intact. Use it to strip passwords, internal IDs, temporary computed values, or nested paths before returning results to clients.
{ $unset: [ "field1", "field2" ] } — an array of field name strings. For nested paths use dot notation: "address.zipCode". A single field can be passed as a one-element array: [ "password" ].
$unset removes specific named fields; everything else remains. $project reshapes output—you include fields to keep or exclude with 0. Use $unset when dropping a few known fields; use $project when building a slim allow-list for API responses.
Yes. $set adds or overwrites fields; $unset removes them. Common pattern: $set to compute a helper field, use it in $sort or $match, then $unset to drop it before the client sees the document.
No. Like all aggregation stages, $unset only affects documents in the pipeline stream. Original collection data is unchanged unless you write results back with $out or $merge.
Nothing harmful—MongoDB simply omits a field that is not there. The stage succeeds and the document passes through with other fields unchanged.
Did you know?

MongoDB added $set and $unset together in version 4.2—mirroring the update operators of the same names so pipeline field edits feel familiar. The aggregation $unset stage always takes an array of field paths, even when removing just one field. See the official $unset docs.

Continue the Stages Series

Clean documents with $unset, then expand arrays with $unwind.

Next: $unwind →

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