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
- Insert and Import Export — sample
postsdata - BSON and Document Basics
Seed Sample Data (Optional)
find and findOne
// 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:
// 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)_idis included by default unless_id: 0
Sort, Skip, Limit (Pagination)
// 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
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.
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:
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
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
| SQL | MongoDB |
|---|---|
SELECT * FROM posts WHERE published = 1 | find({ published: true }) |
SELECT title, slug FROM posts | projection { 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
| Mistake | Symptom | Fix |
|---|---|---|
find() without filter in prod | Slow, memory pressure | Always filter + index |
Large skip | Timeouts | Keyset pagination |
| Wrong type in filter | Empty results | Match BSON type (boolean vs string "true") |
| Projection mix 1 and 0 | Error | Include or exclude, not both (except _id) |
Post-Chapter Checklist
-
find/findOnewith equality filters - Projection reduces returned fields
-
.sort(),.limit(),.skip()for pagination -
countDocumentsfor totals - You treat
findresult 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.
Case-insensitive search?
$regex with i option—prefer text index for search — Indexes.
Limit default?
No default limit—always limit() in exploratory queries on production data.