Find Queries and Projection

Introduction

Find operations return documents matching a filter—the MongoDB equivalent of SQL SELECT ... WHERE. This chapter covers find, findOne, projections, sorting, pagination, and cursors. You will query the posts collection from earlier chapters without yet diving into every operator (chapter 6).

Prerequisites

Seed Sample Data (Optional)

find and findOne

javascript
// All documents (avoid on huge collections in prod)
db.posts.find()
 
// Equality filter on top-level field
db.posts.find({ published: true })
 
// Single document or null
db.posts.findOne({ slug: "mongodb-basics" })

findOne returns a document object; find returns a cursor.

Projection: Include and Exclude Fields

Return only needed fields—less network and memory:

javascript
// Include title and slug; exclude _id
db.posts.find(
  { published: true },
  { title: 1, slug: 1, _id: 0 }
)
 
// Exclude heavy field only
db.posts.find({}, { body: 0 })

Rules:

  • 1 = include, 0 = exclude (cannot mix except _id)
  • _id is included by default unless _id: 0

Sort, Skip, Limit (Pagination)

javascript
// Newest first
db.posts.find({ published: true })
  .sort({ createdAt: -1 })
  .limit(10)
 
// Page 2 (page size 10) — skip grows expensive on large offsets
db.posts.find({ published: true })
  .sort({ createdAt: -1 })
  .skip(10)
  .limit(10)

Tip

Better Pagination

For large collections, use range queries on _id or createdAt instead of huge skip values—cursor-based pagination in apps.

Count Documents

javascript
db.posts.countDocuments({ published: true })
 
db.posts.countDocuments({ viewCount: { $gte: 50 } })

Avoid deprecated count()—use countDocuments or estimatedDocumentCount() for empty filter totals.

Cursors

find returns a cursor—not all rows at once.

javascript
const cursor = db.posts.find({ published: true }).batchSize(50)
 
cursor.forEach((doc) => {
  print(doc.title)
})
 
// Materialize array (fine for small result sets)
db.posts.find().limit(5).toArray()

In application code (PyMongo), iterate the cursor or call list() with care on size.

Query on Nested Fields

Dot notation matches embedded documents:

javascript
db.users.insertOne({
  name: "Ada",
  address: { city: "London", country: "UK" }
})
 
db.users.find({ "address.city": "London" })

Array element match (exact array or position)—advanced operators in chapter 6.

Pretty Output and explain Preview

javascript
db.posts.find({ published: true }).limit(3).pretty()
 
db.posts.find({ published: true }).explain("executionStats")

COLLSCAN means full collection scan—add indexes in chapter 11.

SQL Comparison

SQLMongoDB
SELECT * FROM posts WHERE published = 1find({ published: true })
SELECT title, slug FROM postsprojection { title: 1, slug: 1, _id: 0 }
ORDER BY created_at DESC LIMIT 10.sort({ createdAt: -1 }).limit(10)
COUNT(*)countDocuments({})

Deep SQL — MySQL SELECT track.

Common Mistakes

MistakeSymptomFix
find() without filter in prodSlow, memory pressureAlways filter + index
Large skipTimeoutsKeyset pagination
Wrong type in filterEmpty resultsMatch BSON type (boolean vs string "true")
Projection mix 1 and 0ErrorInclude or exclude, not both (except _id)

Post-Chapter Checklist

  • find / findOne with equality filters
  • Projection reduces returned fields
  • .sort(), .limit(), .skip() for pagination
  • countDocuments for totals
  • You treat find result as a cursor

FAQ

find returns nothing but data exists?

Wrong database (db.getName()), typo in field name, or type mismatch.

Difference between find and aggregate?

find is simple filter + sort. aggregate chains stages for grouping/joins — chapter 12.

Can I select computed fields in find?

Use $project in aggregation or map in application code. find projection only includes/excludes stored fields.

$regex with i option—prefer text index for search — Indexes.

Limit default?

No default limit—always limit() in exploratory queries on production data.