Tutorials

MongoDB Projections

Key Takeaways

  • A MongoDB projection controls which fields appear in returned documents, not which documents match the query.
  • Inclusion projections return the specified fields plus _id by default; exclusion projections return the other fields.
  • Don't mix ordinary inclusion and exclusion fields. You can exclude _id while including other fields.
  • Smaller results can reduce network transfer and client processing, but fewer server-side document reads depend on the query plan.

A MongoDB projection selects or omits fields from query results. In mongosh, pass it as the second argument to find(): db.collection.find({a: 3}, {b: 1, _id: 0}) returns only b from matching documents.

Use projections to avoid returning data your application doesn't need. They're a result-shaping tool—not a guarantee that MongoDB will read less data from the collection.

MongoDB Find Projection Basics

The examples below use mongosh syntax. In a disposable database, start with an empty collection named collection and insert these documents once:

db.collection.insertMany([
  { _id: 1, a: 3, b: "large payload", label: "example" },
  { _id: 2, a: "some string", b: "internal details", label: "editable" }
]);

insertMany() inserts the supplied array of documents. The explicit IDs make the results easy to follow.

Start with the original query:

db.collection.find({a:3});

The filter selects documents where a equals 3. Without a projection, the matching documents retain all their fields; technically, find() returns a cursor, which mongosh displays interactively.

Exclude Fields from Results

db.collection.find({a:3}, {b:0});

The second argument is an exclusion projection: {b: 0} removes b from the returned document and keeps the other fields. It doesn't delete b from storage.

Include Fields and Control the _id Default

// Return b and the default _id field.
db.collection.find({a: 3}, {b: 1});

// Return only b.
db.collection.find({a: 3}, {b: 1, _id: 0});

An inclusion projection uses 1 to select fields. Unmentioned fields are omitted, except for _id, which remains included unless you explicitly exclude it. The filter field doesn't have to appear in the returned document.

For the first sample document, the results look like this:

Stored document:
{ _id: 1, a: 3, b: "large payload", label: "example" }

Projection             Returned document
{ b: 0 }           ->  { _id: 1, a: 3, label: "example" }
{ b: 1 }           ->  { _id: 1, b: "large payload" }
{ b: 1, _id: 0 }   ->  { b: "large payload" }

Choose inclusion when you want a defined response shape. With exclusion, other fields—including fields added to documents later—remain eligible to appear in the results.

Don't Mix Ordinary Inclusion and Exclusion

A projection such as {a: 1, b: 0} is invalid because it mixes ordinary inclusion and exclusion fields. The important exception is _id: {a: 1, _id: 0} is valid.

Check Your Driver's Projection API

Shell syntax isn't universal application syntax. For example, the MongoDB Node.js driver exposes a cursor project() method:

// Fragment: collection is an existing Node.js driver Collection.
const cursor = collection.find({a: 3}).project({b: 1, _id: 0});

Apply project() before iterating the cursor. Check the documentation for your driver or library rather than copying mongosh arguments unchanged. For application-library context, see our comparison of Mongoose and the MongoDB native driver.

Shape findOneAndUpdate Return Values

findOneAndUpdate() accepts a projection for its returned document. The update argument changes stored data; the projection option selects returned fields; returnDocument chooses the before- or after-update document.

db.collection.findOneAndUpdate(
  {a: "some string"},
  {$set: {a: "another string"}},
  {
    projection: {b: 0},
    returnDocument: "after"
  }
);

Run once against the sample data, this returns:

{ _id: 2, a: "another string", label: "editable" }

The stored document still contains b. Here, $set changes only a; excluding b affects the response, not the update.

In mongosh, findOneAndUpdate() returns the original document by default. Set returnDocument: "after" for the updated document or returnDocument: "before" for the original. With no match and no upsert requested, it returns null.

Does a MongoDB Projection Improve Performance?

Returning fewer fields reduces unnecessary response data and can reduce network transfer and client-side decoding work. It doesn't necessarily reduce the documents MongoDB examines: the server may still fetch matching documents before applying the projection.

A covered query can satisfy the filter and return the requested fields from the same index without examining collection documents. Coverage has query and index restrictions; a narrow projection alone doesn't make a query covered.

Compare Execution Plans with explain()

Compare the full-document and projected queries:

db.collection.find({a: 3}).explain("executionStats");

db.collection.find(
  {a: 3},
  {b: 1, _id: 0}
).explain("executionStats");

The executionStats mode executes the winning plan to completion and reports its execution statistics. Use representative data for performance testing; the tiny fixture above is a syntax demonstration, not a benchmark.

For this scalar-field query, a compound index on a and b is a candidate covering index:

db.collection.createIndex({a: 1, b: 1});

db.collection.find(
  {a: 3},
  {b: 1, _id: 0}
).explain("executionStats");

The index contains the filter field a and returned field b. Excluding _id matters because this index doesn't contain it. Inspect the chosen plan rather than assuming MongoDB will select the new index. For the underlying concepts, read our introduction to MongoDB indexing.

In the explain output, compare:

  • nReturned: documents returned by the winning plan.
  • totalDocsExamined: documents examined during execution.
  • totalKeysExamined: index entries scanned.

For a query that returns matches, zero documents examined is useful evidence of coverage; also inspect the plan for document-fetch work. Don't judge application latency from executionTimeMillis alone: it excludes network transmission time and isn't necessarily representative of normal query latency.

Find Projections vs. Aggregation $project

A find projection is an argument to a query. Aggregation's $project is a pipeline stage that passes selected or computed fields to the next stage. It can select fields, rename them, and calculate new values.

db.collection.aggregate([
  {$match: {a: 3}},
  {$project: {_id: 0, selectedB: "$b"}}
]);

For the sample data, this returns {selectedB: "large payload"}. The "$b" expression reads b into an output field named selectedB; it doesn't rename the stored field.

Don't treat field selection and aggregation as completely separate capabilities: find projections can also accept aggregation expressions. Use the API that fits the operation, and use a pipeline when you need multiple processing stages. Our MongoDB aggregation pipeline tutorial explains how those stages fit together.

Common Mistakes

  • Assuming unmentioned fields are always included. That's true for exclusion projections, not inclusion projections.
  • Expecting fewer fields to mean fewer matches. The filter selects documents; projection selects returned fields.
  • Treating a projected result as a complete record. Keep response-shaping code separate from code that needs the full document.

FAQ

How do I return only selected fields in a MongoDB query?

In mongosh, pass an inclusion projection as the second argument to find(). For example, db.collection.find({a: 3}, {b: 1, _id: 0}) returns only b from matching documents.

Does MongoDB include _id in projections by default?

Yes. MongoDB includes _id by default, even when an inclusion projection doesn't mention it. Add _id: 0 to omit it.

Can a MongoDB projection mix included and excluded fields?

You generally can't mix ordinary inclusion and exclusion fields: {a: 1, b: 0} is invalid. You can exclude _id while including other fields, as in {a: 1, _id: 0}.

Do projections make MongoDB queries faster?

They can reduce network transfer and client processing, but don't necessarily reduce server-side document reads. Compare execution plans with explain("executionStats") and measure application latency; a covered query can avoid fetching collection documents.

How does projection work with findOneAndUpdate?

The projection option controls fields in the returned document, not fields changed by the update. In mongosh, the original document is returned by default; returnDocument: "after" requests the updated document.

Further Reading

Written by Sam Erickson

Software engineer. I write every article on StackChief myself. Author of Mastering the Tech Interview. More about me