MongoDBProjection

Projection

Projection controls which fields are returned in query results. By default, find() returns all fields. Projection lets you include only the fields you need — reducing network transfer and improving performance.

Inclusion Projection

Set fields to 1 to include them. Only those fields are returned (plus _id by default).

Inclusion projection

JS
// Return only 'name' and 'email' fields (plus _id)
db.users.find({}, { name: 1, email: 1 })

// Result shape:
// { _id: ObjectId('...'), name: 'Alice', email: 'alice@example.com' }

// With a filter
db.users.find({ active: true }, { name: 1, email: 1 })
Exclusion Projection

Set fields to 0 to exclude them. All other fields are returned.

Exclusion projection

JS
// Return everything EXCEPT 'password' and '__v'
db.users.find({}, { password: 0, __v: 0 })

// Result shape:
// { _id: ObjectId('...'), name: 'Alice', email: 'alice@example.com', createdAt: ... }

// With a filter
db.users.find({ active: true }, { password: 0, refreshToken: 0 })
Excluding _id

Remove _id from results

JS
// Include name and email, but suppress _id
db.users.find({}, { _id: 0, name: 1, email: 1 })

// Result shape:
// { name: 'Alice', email: 'alice@example.com' }
// No _id field at all
Note
You cannot mix inclusion and exclusion in a single projection (except for excluding _id). Doing so throws a MongoDB error.
Projecting Nested Fields

Nested field projection

JS
// Return only specific nested fields using dot notation
db.users.find(
  {},
  { 'address.city': 1, 'address.zip': 1 }
)

// Result shape:
// { _id: ObjectId('...'), address: { city: 'New York', zip: '10001' } }
// Other address fields (street, country) are excluded

// Works for deeply nested fields too
db.orders.find(
  {},
  { 'shipping.address.city': 1, 'shipping.carrier': 1 }
)
$slice — Limit Array Elements

$slice limits the number of elements returned from an array field.

$slice projection

JS
// Return FIRST 3 tags
db.posts.find({}, { tags: { $slice: 3 } })

// Return LAST 2 comments
db.posts.find({}, { comments: { $slice: -2 } })

// Skip 5 elements, then return the next 3  [skip, limit]
db.posts.find({}, { comments: { $slice: [5, 3] } })
$elemMatch Projection — First Matching Array Element

$elemMatch in projection

JS
// Return only the FIRST order item where price > 100
db.orders.find(
  { 'items.price': { $gt: 100 } },
  { items: { $elemMatch: { price: { $gt: 100 } } } }
)

// Result shape:
// { _id: ObjectId('...'), items: [{ product: 'Laptop', price: 999 }] }
// Only the first matching item is returned, not all items
The Positional $ in Projection

Positional projection

JS
// The $ in projection returns ONLY the matched array element from the query
db.students.find(
  { grades: { $elemMatch: { subject: 'Math', score: { $gt: 90 } } } },
  { 'grades.$': 1 }
)

// The query must reference the same array field used in the $ projection
// Result: only the first element that matched the query condition is returned
Projection in Practice — Performance Impact

Always project only the fields you need in production. Fetching full documents when you only need 2 fields wastes network bandwidth and memory.

Scenario

Without Projection

With Projection

1,000 users, full doc (5 KB each)

5 MB

50 KB (name + email only)

10,000 orders full

50 MB

500 KB (id + total only)

Tip
Combine projection with indexes to create covered queries — where MongoDB can satisfy the entire query from the index without reading documents at all.
Warning
Never return passwords, tokens, or sensitive fields in API responses. Always use exclusion projection on sensitive fields.