MongoDB $project Stage

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

What Is $project?

$project is a MongoDB aggregation stage that reshapes each document. You can keep fields, drop fields, rename them, or compute new values—controlling the exact shape that flows to the next stage or the client.

Remember
$project = pick / drop / compute fields

{ $project: { <specs> } }

include  → field: 1
exclude  → field: 0   (or use $unset)
compute  → field: <expression>
_id      → included by default; set _id: 0 to hide

Do not mix 1 and 0 on regular fields
(only _id: 0 is allowed with inclusion)

vs $addFields → reshape vs add columns
UsePrefer
Slim / reshape output$project
Add fields, keep the rest$addFields / $set
Drop a few fields only$unset (or exclusion $project)

How to Use $project in MongoDB

Decide inclusion or exclusion, list the specs, then place the stage where you need that shape.

  1. Start (or continue) an aggregation pipeline with aggregate([...]).
  2. Choose inclusion (field: 1) or exclusion (field: 0)—not both on regular fields.
  3. Optional: add computed fields with expressions such as $multiply.
  4. Optional: hide _id with _id: 0 in inclusion mode.
PieceRequired?Role
Specification mapYesNon-empty object of field rules
1 / trueNoInclude an existing field
0 / falseNoExclude a field (exclusion mode)
ExpressionNoCompute or rename a field

Pseudocode

Pseudocode
for each document:
    if $project specs are empty:
        error
    if inclusion mode:
        out = { _id: document._id }   // unless _id: 0
        for each field marked 1 or expression:
            out[field] = value or evaluate(expression)
    else if exclusion mode:
        out = copy(document)
        remove listed fields
    emit out

Cheat Sheet

Keep these facts handy while reshaping pipeline documents.

TopicDetail
TypeAggregation stage (not a find filter)
Syntax{ $project: { <field>: <1|0|expression> } }
Inclusion{ name: 1, price: 1 } (+ _id by default)
Exclusion{ notes: 0 } keeps everything else
Hide _id{ _id: 0, name: 1 }
Compute{ total: { $multiply: ["$price", "$qty"] } }
Empty specs{ $project: {} } → error

Live Preview

Pick a projection style and see how one order document is reshaped.

Simulates $project on { _id: 1, name: "Keyboard", price: 45, qty: 2, notes: "promo" }.

Result
$project: { name: 1, price: 1 }

output:
  { _id: 1, name: "Keyboard", price: 45 }

Worked Walkthrough — Slim Order Output

An order has extra notes. Goal: return only name and a computed total, without _id.

Calculation
Document in:
  { _id: 1, name: "Keyboard", price: 45, qty: 2, notes: "promo" }

$project:
  _id: 0
  name: 1
  total: { $multiply: ["$price", "$qty"] }

45 * 2 = 90

Document out:
  { name: "Keyboard", total: 90 }

MongoDB Examples

Insert sample products, include selected fields, then compute a slim total. Run these in mongosh in order.

Example 1 — Sample Data

Two products with name, price, quantity, and an internal notes field we may want to hide later.

mongosh
db.products.insertMany([
  {
    _id: 1,
    name: "Keyboard",
    price: 45,
    qty: 2,
    notes: "promo"
  },
  {
    _id: 2,
    name: "Mouse",
    price: 25,
    qty: 1,
    notes: "clearance"
  }
]);

How the Data Is Stored in MongoDB

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

mongosh
db.products.find()

Read each document like this:

FieldExamplePlain English
name"Keyboard"Product name
price45Unit price
qty2Units
notes"promo"Internal note (often hidden)

How It Works

  1. Insert once. insertMany adds two product documents.
  2. All fields are present. Including notes, which we may omit in API output.
  3. Ready to reshape. The next examples use $project.

Example 2 — Include Fields

Goal: keep only name, price, and qty (_id stays by default; notes is dropped).

mongosh
db.products.aggregate([
  {
    $project: {
      name: 1,
      price: 1,
      qty: 1
    }
  }
]);

How It Works

  1. Inclusion mode. Fields set to 1 are kept; others (except _id) are omitted.
  2. _id stays. MongoDB includes _id unless you set _id: 0.
  3. notes is gone. It was not listed, so it does not appear in the output.
  4. Shape is smaller. Later stages or clients only see the projected fields.

Example 3 — Compute Total

Goal: return name and total = price × qty, without _id or other fields.

mongosh
db.products.aggregate([
  {
    $project: {
      _id: 0,
      name: 1,
      total: { $multiply: ["$price", "$qty"] }
    }
  }
]);

How It Works

  1. Hide _id. _id: 0 is allowed together with inclusion specs.
  2. Keep name. name: 1 copies the existing string.
  3. Compute total. $multiply does 45 × 2 = 90 and 25 × 1 = 25.
  4. Everything else is dropped. Including raw price, qty, and notes.

Edge Cases & Pitfalls

Check these before using $project in production pipelines.

mix

Do not mix 1 and 0 on regular fields

Inclusion and exclusion modes conflict. Exception: _id: 0 with other inclusions is allowed.

empty

Empty $project errors

{ $project: {} } fails. Always specify at least one field rule.

vs add

Need to keep everything? Use $addFields

$addFields appends columns without forcing you to re-list every field.

literal

Numeric literals need $literal

Writing { flag: 1 } means “include field flag”, not “set flag to 1”. Use $literal for literal numbers/booleans.

Performance Notes

ApproachCostBest for
Final $projectPer-document reshapeAPI / export response shape
Early $projectOften little gainMongoDB may already drop unused fields
$match then $projectFilter firstFewer docs to reshape

Prefer $match early to cut document count. Use $project to control the final shape; do not assume early projection always speeds the pipeline.

Key Takeaways

  • Role: reshape documents—include, exclude, or compute fields.
  • Modes: inclusion (1) or exclusion (0), not both.
  • _id: included by default; hide with _id: 0.
  • vs $addFields: reshape vs add columns.

One line: $project answers “exactly which fields should leave this stage?”

Frequently Asked Questions

$project reshapes each document in an aggregation pipeline. You can include fields, exclude fields, rename fields, and compute new values with expressions.
Generally no—except for _id. Use either inclusion mode (list fields with 1) or exclusion mode (list fields with 0). Mixing 1 and 0 on regular fields causes an error. You can set _id: 0 in inclusion mode.
$project replaces the document shape—you choose what survives. $addFields keeps existing fields and adds or overwrites specific ones. Use $project to slim output; use $addFields to append columns.
Yes. Output documents include _id unless you set _id: 0 in the $project specification.
Yes. Any field value can be an expression, such as { total: { $multiply: ["$price", "$qty"] } } or a rename like { title: "$name" }.
Often near the end to shape what clients receive. MongoDB already optimizes unused fields in many cases, so early $project is not always a performance win.

Next: $redact

Restrict fields and subdocuments based on access conditions.

$redact 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