iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Projection

A projection picks which fields a query returns. { field: 1 } includes, { field: 0 } excludes. Smaller payloads, fewer index scans, less bandwidth.

Include, exclude, nested, array

EXAMPLE
// 1) Include specific fields (and _id by default)
await db.users.find({}, { projection: { name: 1, email: 1 } }).toArray();
// → { _id, name, email }

// 2) Exclude _id explicitly
await db.users.find({}, { projection: { name: 1, email: 1, _id: 0 } }).toArray();
// → { name, email }

// 3) Exclude a heavy field
await db.posts.find({}, { projection: { body: 0 } }).toArray();
// → all fields except body

// 4) Mixing include + exclude is generally forbidden
//   { name: 1, body: 0 }   ← error
//   Exception: excluding only _id alongside includes is fine.

// 5) Nested fields — dot path
await db.users.find({}, { projection: { 'address.city': 1 } }).toArray();
// → { _id, address: { city } }

// 6) Array projection
// Whole array
await db.users.findOne({ _id }, { projection: { tags: 1 } });

// First matching element
await db.orders.findOne(
    { _id, 'items.sku': 'A-100' },
    { projection: { 'items.$': 1 } },
);
// → { items: [<first match>] }

// First N elements
await db.feeds.findOne({ _id }, { projection: { events: { $slice: 10 } } });

// Slice with skip
await db.feeds.findOne({ _id }, { projection: { events: { $slice: [20, 10] } } });

// 7) Conditional projection with $elemMatch — array element matching ALL conditions
await db.orders.findOne(
    { _id },
    { projection: { items: { $elemMatch: { sku: 'A-100', qty: { $gte: 5 } } } } },
);

// 8) Combined with aggregation $project — full transformations
await db.orders.aggregate([
    { $match:   { user_id: u } },
    { $project: {
        _id:       0,
        id:        '$_id',
        total:     1,
        itemCount: { $size: '$items' },
        firstSku:  { $arrayElemAt: ['$items.sku', 0] },
    } },
]).toArray();

// 9) Projection in updates — findOneAndUpdate / findOneAndReplace
await db.users.findOneAndUpdate(
    { _id },
    { $set: { lastSeen: new Date() } },
    {
        projection:     { name: 1, lastSeen: 1, _id: 0 },
        returnDocument: 'after',
    },
);

// 10) Cursor-side projection
await db.posts
    .find({})
    .project({ title: 1, slug: 1, _id: 0 })
    .toArray();

// 11) Performance — covered query
// If the projection AND the query are entirely served by a single index,
// MongoDB never touches the documents. Massive speedup on large collections.
await db.posts.createIndex({ status: 1, createdAt: -1 });
await db.posts
    .find({ status: 'published' }, { projection: { _id: 0, status: 1, createdAt: 1 } })
    .sort({ createdAt: -1 })
    .toArray();
// Run explain() and look for 'totalDocsExamined': 0 (covered)

// 12) GUI tools
// MongoDB Compass — pick projections via UI; copy as code.
// mongosh — db.posts.find({}, { projection: { title: 1 } }).pretty()

Why it matters

Smaller projections = faster queries, smaller payloads, less RAM. Pair frequent projection patterns with a matching index to land in “covered query” territory and skip document fetches entirely.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Include / exclude fields
db.customers.find({}, { name: 1, email: 1, _id: 0 });
Try it Yourself »

Discussion

Loading…