Toolmingo
Guides24 min read

50 MongoDB Interview Questions (With Answers)

Top MongoDB interview questions with clear answers and examples — covering CRUD, aggregation pipeline, indexes, schema design, replication, sharding, and performance tuning.

MongoDB interviews test your understanding of document modelling, the aggregation pipeline, indexing, replication, sharding, and performance tuning. This guide covers the 50 most common questions — with concise answers and shell/code examples.

Quick reference

Topic Most asked questions
Core concepts Document model, BSON, collections
CRUD insertOne/Many, find, update operators, delete
Aggregation $match, $group, $lookup, $unwind, $project
Indexes Single, compound, text, TTL, explain()
Schema design Embedding vs referencing, 1:N, M:N patterns
Replication Replica sets, elections, oplog
Sharding Shard keys, chunks, mongos, balancer
Transactions Multi-document ACID, sessions
Performance Query plans, index hints, profiler
Security Authentication, RBAC, TLS

Core concepts

1. What is MongoDB and how does it differ from relational databases?

MongoDB is a document-oriented NoSQL database. Instead of rows in tables, it stores BSON documents in collections.

Feature MongoDB Relational DB
Data unit Document (BSON) Row (typed columns)
Schema Flexible (schemaless) Fixed schema
Joins $lookup aggregation Native JOINs
Transactions Multi-doc ACID (v4.0+) Native ACID
Scaling Horizontal (sharding) Vertical (primarily)
Query language MQL / Aggregation Pipeline SQL
Relationships Embed or reference Foreign keys

Use MongoDB when your data is document-shaped, schema evolves frequently, or horizontal scaling is required.


2. What is BSON?

BSON (Binary JSON) is MongoDB's internal serialisation format. It extends JSON with additional types:

BSON type Example use
ObjectId _id field
Date Timestamps (ISODate)
Int32 / Int64 Numeric fields
Decimal128 Financial calculations
Binary File chunks, UUIDs
Regex Pattern fields
Array Ordered lists
Embedded document Nested objects

BSON is more efficient than JSON for encoding/decoding and supports types JSON doesn't (e.g. dates, binary data).


3. What is a document in MongoDB?

A document is a BSON object — analogous to a JSON object or a table row:

{
  _id: ObjectId("64a1b2c3d4e5f6a7b8c9d0e1"),
  name: "Alice",
  age: 30,
  address: { city: "London", postcode: "EC1A" },
  tags: ["developer", "mongodb"]
}
  • Maximum document size: 16 MB
  • _id is mandatory and unique per collection; auto-generated as ObjectId if omitted

4. What is a collection? Does it enforce a schema?

A collection is a group of documents — analogous to a table. By default MongoDB is schemaless: documents in the same collection can have different fields.

You can enforce a schema using JSON Schema validation:

db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email"],
      properties: {
        name:  { bsonType: "string" },
        email: { bsonType: "string", pattern: "^.+@.+$" }
      }
    }
  }
})

5. What is ObjectId? How is it structured?

ObjectId is MongoDB's default _id type — a 12-byte value:

Bytes Content
4 Unix timestamp (seconds)
5 Random value (machine + process)
3 Incrementing counter

This makes ObjectIds globally unique and roughly sortable by insertion time without a central counter.

const id = new ObjectId()
id.getTimestamp()  // Date when the id was created

CRUD operations

6. How do you insert documents?

// Insert one
db.users.insertOne({ name: "Bob", age: 25 })

// Insert many (ordered by default — stops on first error)
db.users.insertMany([
  { name: "Carol", age: 28 },
  { name: "Dave",  age: 35 }
], { ordered: false })  // ordered:false continues past errors

insertMany with ordered: false is faster in bulk loads because individual failures don't abort the batch.


7. How do you query documents?

// All documents
db.users.find()

// Equality filter
db.users.find({ age: 30 })

// Comparison operators
db.users.find({ age: { $gte: 25, $lt: 40 } })

// Logical operators
db.users.find({ $or: [{ city: "London" }, { city: "Paris" }] })

// Field projection (1 = include, 0 = exclude)
db.users.find({ age: { $gt: 20 } }, { name: 1, _id: 0 })

// Nested field
db.users.find({ "address.city": "London" })

// Array element match
db.users.find({ tags: "developer" })

// findOne returns first match only
db.users.findOne({ name: "Alice" })

8. What are the main update operators?

// $set — set field values
db.users.updateOne({ name: "Alice" }, { $set: { age: 31 } })

// $unset — remove a field
db.users.updateOne({ name: "Alice" }, { $unset: { age: "" } })

// $inc — increment / decrement
db.users.updateOne({ name: "Alice" }, { $inc: { loginCount: 1 } })

// $push — append to array
db.users.updateOne({ name: "Alice" }, { $push: { tags: "admin" } })

// $addToSet — push only if not already present
db.users.updateOne({ name: "Alice" }, { $addToSet: { tags: "admin" } })

// $pull — remove matching array elements
db.users.updateOne({ name: "Alice" }, { $pull: { tags: "developer" } })

// $rename — rename a field
db.users.updateMany({}, { $rename: { "oldName": "newName" } })

// upsert — insert if not found
db.users.updateOne(
  { email: "new@example.com" },
  { $set: { name: "New User" } },
  { upsert: true }
)

9. What is the difference between updateOne, updateMany, and replaceOne?

Method Behaviour
updateOne Modifies first matching document using update operators
updateMany Modifies all matching documents using update operators
replaceOne Replaces entire document (except _id) with new document

replaceOne does not use $set — the entire document body is swapped:

db.users.replaceOne({ name: "Alice" }, { name: "Alice", age: 32, city: "Berlin" })
// All fields not in the replacement document are removed

10. How do you delete documents?

// Delete first matching document
db.users.deleteOne({ name: "Alice" })

// Delete all matching documents
db.users.deleteMany({ age: { $lt: 18 } })

// Delete all documents in collection (keeps collection + indexes)
db.users.deleteMany({})

// Drop entire collection (removes collection, indexes, metadata)
db.users.drop()

Aggregation pipeline

11. What is the aggregation pipeline?

The aggregation pipeline processes documents through a series of stages, each transforming the data:

Collection → [$stage1] → [$stage2] → ... → result

Common stages:

Stage Purpose
$match Filter documents (like find)
$group Group and accumulate values
$project Reshape documents (include/exclude/compute fields)
$sort Sort results
$limit / $skip Paginate
$lookup Left outer join with another collection
$unwind Deconstruct array field into separate documents
$addFields Add computed fields
$count Count documents
$facet Multiple sub-pipelines on same input
$bucket Range-based grouping
$out / $merge Write results to a collection

12. Write an aggregation to find the total sales per category.

db.orders.aggregate([
  { $match: { status: "completed" } },          // filter first (use indexes)
  { $group: {
      _id: "$category",                           // group by category
      totalSales:  { $sum: "$amount" },
      orderCount:  { $sum: 1 },
      avgAmount:   { $avg: "$amount" }
  }},
  { $sort: { totalSales: -1 } },                 // highest first
  { $limit: 10 }
])

Always put $match early to reduce the document set before expensive stages.


13. How does $lookup work?

$lookup performs a left outer join between two collections:

db.orders.aggregate([
  {
    $lookup: {
      from:         "products",      // collection to join
      localField:   "productId",     // field in orders
      foreignField: "_id",           // field in products
      as:           "productInfo"    // output array field
    }
  },
  { $unwind: "$productInfo" }        // flatten the array (converts to object)
])

For correlated sub-queries use the pipeline form:

{
  $lookup: {
    from: "inventory",
    let: { ordProd: "$productId" },
    pipeline: [
      { $match: { $expr: { $eq: ["$_id", "$$ordProd"] } } },
      { $project: { stock: 1 } }
    ],
    as: "stock"
  }
}

14. What does $unwind do and when do you use it?

$unwind deconstructs an array field — it outputs one document per array element:

// Input:  { _id: 1, tags: ["a", "b", "c"] }
// $unwind: "$tags" produces:
// { _id: 1, tags: "a" }
// { _id: 1, tags: "b" }
// { _id: 1, tags: "c" }

Use cases:

  • Group by individual array elements ($group after $unwind)
  • Join array elements to another collection via $lookup

Options: preserveNullAndEmptyArrays: true keeps documents where the field is missing/empty.


15. How do you use $expr in a query?

$expr lets you use aggregation expressions inside $match or find:

// Find documents where spent > budget (comparing two fields)
db.campaigns.find({
  $expr: { $gt: ["$spent", "$budget"] }
})

// In aggregation
db.orders.aggregate([
  { $match: { $expr: { $gte: ["$amount", "$minAmount"] } } }
])

Without $expr, you can only compare a field to a literal value, not to another field.


Indexes

16. What types of indexes does MongoDB support?

Index type Use case
Single field { age: 1 } — ascending; { age: -1 } — descending
Compound { lastName: 1, firstName: 1 } — multi-field queries
Multikey Automatic when field is an array
Text Full-text search ({ content: "text" })
Geospatial 2dsphere for GeoJSON, 2d for flat coordinates
Hashed { _id: "hashed" } for hash-based sharding
Wildcard { "$**": 1 } — indexes all fields dynamically
TTL Expires documents automatically (expireAfterSeconds)
Sparse Only indexes documents that have the field
Partial Only indexes documents matching a filter expression

17. What is the ESR rule for compound indexes?

When building compound indexes, order fields as Equality → Sort → Range:

// Query: find users in London aged 25-35, sorted by name
// ESR index:
db.users.createIndex({ city: 1, name: 1, age: 1 })
//                     ^Equality  ^Sort   ^Range

This order allows MongoDB to:

  1. Jump directly to the equality value (city)
  2. Read documents in sort order (no in-memory sort needed)
  3. Apply the range filter on the remaining documents

18. What is a covered query?

A query is covered when all fields the query needs are in the index — MongoDB never touches the actual documents:

db.users.createIndex({ email: 1, name: 1 })

// This query is covered (only email and name accessed, _id excluded)
db.users.find({ email: "a@b.com" }, { name: 1, _id: 0 })

Covered queries are the fastest possible — they read only the index B-tree.


19. How do you analyse a query's performance?

db.users.find({ age: { $gt: 25 } }).explain("executionStats")

Key fields in the output:

Field What to look for
winningPlan.stage IXSCAN (index) is good; COLLSCAN (full scan) is bad
executionStats.totalDocsExamined Should equal totalDocsReturned ideally
executionStats.executionTimeMillis Actual execution time
indexBounds Which part of the index was used

If you see COLLSCAN on a high-volume collection → add an index.


20. What is a TTL index?

A TTL (Time-To-Live) index automatically deletes documents after a specified time:

// Delete documents 30 days after their createdAt date
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 2592000 })

Requirements:

  • Field must be a BSON Date type
  • A background thread checks and deletes expired documents every 60 seconds (approximate)
  • Does not work on capped collections
  • Only one TTL index per collection

Schema design

21. When should you embed vs reference documents?

Criterion Embed Reference
Access pattern Always loaded together Loaded independently
Relationship 1:few, 1:1 1:many, M:N
Document size concern Small sub-documents Avoids 16 MB limit
Write pattern Update atomically Update independently
Data duplication Acceptable for reads Avoid duplication

Rule of thumb: "Embed if you can, reference if you must."


22. How do you model a one-to-many relationship?

Option 1 — Embed (one-to-few): blog post with comments (small, bounded list)

{
  _id: ObjectId("..."),
  title: "MongoDB tips",
  comments: [
    { author: "Alice", text: "Great post!", date: ISODate("...") },
    { author: "Bob",   text: "Very helpful", date: ISODate("...") }
  ]
}

Option 2 — Reference (one-to-many): user with thousands of orders

// users collection
{ _id: ObjectId("u1"), name: "Alice" }

// orders collection
{ _id: ObjectId("o1"), userId: ObjectId("u1"), amount: 99 }

Option 3 — Bucket pattern (time-series / IoT): group readings into buckets of N measurements to avoid unbounded arrays.


23. How do you model a many-to-many relationship?

// students and courses

// students collection
{ _id: ObjectId("s1"), name: "Alice", courseIds: [ObjectId("c1"), ObjectId("c2")] }

// courses collection
{ _id: ObjectId("c1"), title: "MongoDB 101", studentIds: [ObjectId("s1"), ObjectId("s3")] }

For very large M:N sets, store the relationship in a junction collection instead of arrays:

// enrollments collection
{ studentId: ObjectId("s1"), courseId: ObjectId("c1"), enrolledAt: ISODate("...") }

24. What is the Bucket pattern?

The Bucket pattern groups time-series or sequential data into documents that hold N measurements:

// Instead of one document per sensor reading:
{ deviceId: "sensor1", timestamp: ISODate("..."), temp: 22.5 }

// Bucket into hourly batches:
{
  deviceId: "sensor1",
  date:     ISODate("2026-07-15T14:00:00Z"),
  count:    60,
  readings: [
    { t: ISODate("2026-07-15T14:00:10Z"), temp: 22.5 },
    // ... up to 59 more
  ],
  minTemp: 22.1,
  maxTemp: 23.0
}

Benefits: fewer documents → smaller index → faster range queries. MongoDB's time series collections (v5.0+) implement this automatically.


25. What is the Outlier pattern?

When most documents have small arrays but a few have very large ones:

// Normal document
{ _id: ObjectId("b1"), title: "Normal book", reviewIds: [ObjectId("r1"), ObjectId("r2")] }

// Outlier (thousands of reviews — add overflow flag)
{ _id: ObjectId("b2"), title: "Bestseller", reviewIds: [...first 1000...], hasOverflow: true }

// Overflow documents
{ _id: ObjectId("bk_b2_1"), bookId: ObjectId("b2"), reviewIds: [...next 1000...] }

Application code checks hasOverflow and fetches additional pages if needed.


Replication

26. What is a replica set?

A replica set is a group of MongoDB instances that maintain the same dataset:

  • Primary — receives all write operations
  • Secondaries (1+) — replicate from the primary's oplog asynchronously
  • Arbiter (optional) — participates in elections but holds no data
Primary ──(oplog)──► Secondary 1
         └──(oplog)──► Secondary 2

If the primary fails, an election chooses a new primary from the secondaries. Minimum recommended size: 3 members (odd number avoids split-brain).


27. What is the oplog?

The oplog (operations log) is a capped collection (local.oplog.rs) on each replica set member that records every write operation in an idempotent, replayable format. Secondaries continuously tail the oplog to stay in sync.

Key points:

  • Capped — oldest entries are overwritten (configurable size)
  • Oplog window matters: if a secondary falls too far behind, it needs a resync
  • Change Streams are built on top of the oplog

28. What are read preferences?

Read preference controls which replica set member satisfies read operations:

Mode Description
primary (default) All reads from primary (strongly consistent)
primaryPreferred Primary if available, else secondary
secondary Always from a secondary (may read stale data)
secondaryPreferred Secondary if available, else primary
nearest Lowest network latency member
db.users.find().readPref("secondaryPreferred")

Use secondary read preference for reporting/analytics to offload the primary.


29. What is write concern?

Write concern specifies how many replica set members must acknowledge a write before MongoDB considers it successful:

db.orders.insertOne(
  { item: "widget", qty: 10 },
  { writeConcern: { w: "majority", j: true, wtimeout: 5000 } }
)
Option Description
w: 0 Fire and forget (no acknowledgement)
w: 1 Primary only
w: "majority" Majority of voting members (safe for most production use)
j: true Wait for the journal to be written to disk
wtimeout Max milliseconds to wait

w: "majority" + j: true is the safest combination.


30. What causes a replica set election and how long does it take?

Elections occur when:

  • Primary becomes unreachable (heartbeat fails after ~10 s)
  • rs.stepDown() is called explicitly
  • Priority change forces re-election

Election duration: typically 10–30 seconds during which no writes are accepted. The node with the highest priority (and most up-to-date oplog) wins.


Sharding

31. What is sharding and why is it used?

Sharding horizontally partitions data across multiple servers (shards) to:

  • Scale writes beyond a single server's capacity
  • Store datasets larger than a single machine's disk

Architecture:

Client → mongos (query router) → Config servers (metadata)
                              ↓
                  Shard 1 | Shard 2 | Shard 3 (each is a replica set)

32. What is a shard key and how do you choose one?

The shard key determines how data is distributed across shards. Criteria for a good shard key:

Criterion Why it matters
High cardinality Allows many distinct chunks
Even write distribution Avoids hot shards
Query isolation Most queries include the shard key → single shard
Not monotonically increasing Avoids write hot spot on last chunk

Bad: { createdAt: 1 } — all new inserts go to the last chunk (monotonic hot spot).
Good: { userId: "hashed" } — hashed key distributes writes evenly.
Best for range queries: compound key { region: 1, userId: 1 } — isolates queries by region.


33. What are ranged vs hashed sharding?

Strategy How it works Best for
Ranged Documents with adjacent shard key values are on the same chunk/shard Range queries on shard key
Hashed Shard key is hashed; documents distributed uniformly High write throughput; avoids hot spots
// Ranged sharding
sh.shardCollection("mydb.orders", { customerId: 1 })

// Hashed sharding
sh.shardCollection("mydb.orders", { customerId: "hashed" })

Transactions

34. Does MongoDB support ACID transactions?

Yes — since v4.0 for replica sets and v4.2 for sharded clusters, MongoDB supports multi-document ACID transactions:

const session = client.startSession()
session.startTransaction({
  readConcern:  { level: "snapshot" },
  writeConcern: { w: "majority" }
})

try {
  db.accounts.updateOne(
    { _id: "A" }, { $inc: { balance: -100 } }, { session }
  )
  db.accounts.updateOne(
    { _id: "B" }, { $inc: { balance:  100 } }, { session }
  )
  await session.commitTransaction()
} catch (err) {
  await session.abortTransaction()
} finally {
  session.endSession()
}

Design preference: MongoDB's document model lets you atomically update a single document (including arrays) — avoid multi-document transactions when possible for better performance.


35. What is the difference between read concern local, majority, and snapshot?

Read concern Guarantee
local (default) Returns the most recent data on the node — may be rolled back
available Like local but does not guarantee causal consistency on shards
majority Returns data acknowledged by a majority — won't be rolled back
snapshot Consistent snapshot of data at a point in time (required for transactions)
linearizable Most recent majority-committed data, waits for in-flight writes (single node)

Performance

36. What is the MongoDB profiler?

The database profiler logs slow operations to system.profile:

// Enable: log operations slower than 100 ms
db.setProfilingLevel(1, { slowms: 100 })

// Enable: log all operations (heavy — for debugging only)
db.setProfilingLevel(2)

// Query the profile collection
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty()

// Disable
db.setProfilingLevel(0)

Key fields: op, ns, millis, keysExamined, docsExamined, planSummary.


37. How do you force MongoDB to use a specific index?

db.users.find({ age: { $gt: 25 } }).hint({ age: 1 })

// Use natural order (no index — full collection scan)
db.users.find({ age: { $gt: 25 } }).hint({ $natural: 1 })

hint() overrides the query planner. Use it when the planner picks a suboptimal index, or when benchmarking.


38. What is the WiredTiger cache and why does it matter?

WiredTiger (default storage engine since v3.2) uses an in-memory cache:

  • Default: 50% of available RAM - 1 GB (min 256 MB)
  • Hot data (frequently accessed documents + index pages) stays in cache
  • Reads from cache are orders of magnitude faster than disk reads

Tune with --wiredTigerCacheSizeGB or storage.wiredTiger.engineConfig.cacheSizeGB in mongod.conf. For dedicated MongoDB servers, set to ~60–70% of RAM.


Security

39. What authentication mechanisms does MongoDB support?

Mechanism Description
SCRAM-SHA-256 (default) Challenge-response; recommended
SCRAM-SHA-1 Legacy; avoid for new deployments
x.509 certificates Client and member authentication
LDAP / Kerberos Enterprise feature
AWS IAM Atlas / Enterprise on AWS

Always enable authentication (security.authorization: enabled in mongod.conf). By default MongoDB binds to localhost only — change bindIp carefully.


40. How does MongoDB RBAC work?

MongoDB uses Role-Based Access Control. A user is assigned roles; roles grant privileges on resources:

// Create a read-only user on one database
db.createUser({
  user: "reportUser",
  pwd:  "securePassword",
  roles: [{ role: "read", db: "analytics" }]
})

// Built-in roles:
// read, readWrite, dbAdmin, userAdmin, clusterAdmin, root

Custom roles:

db.createRole({
  role: "reportRole",
  privileges: [
    { resource: { db: "analytics", collection: "" }, actions: ["find"] }
  ],
  roles: []
})

Advanced topics

41. What are Change Streams?

Change Streams allow applications to subscribe to real-time notifications of data changes:

const changeStream = db.orders.watch([
  { $match: { operationType: { $in: ["insert", "update"] } } }
])

changeStream.on("change", (change) => {
  console.log("Changed:", change.fullDocument)
})

Built on the oplog — requires a replica set or sharded cluster. Use cases: event-driven architecture, cache invalidation, audit logs, real-time dashboards.


42. What is the difference between $match early vs late in a pipeline?

Position Effect
Early $match (before $group, $lookup) Reduces input document count; can use indexes
Late $match (after $group) Filters computed/grouped results; cannot use collection indexes

Always place $match as early as possible to limit data flowing through subsequent stages.

// Good: index on status is used
db.orders.aggregate([
  { $match: { status: "shipped" } },   // ← early
  { $group: { _id: "$customerId", total: { $sum: "$amount" } } }
])

// Bad: full collection scanned before grouping
db.orders.aggregate([
  { $group: { _id: "$customerId", total: { $sum: "$amount" } } },
  { $match: { total: { $gt: 1000 } } }  // ← this is fine (on computed field)
])

43. What are capped collections?

Capped collections are fixed-size, circular collections — oldest documents are overwritten when the size limit is reached:

db.createCollection("logs", { capped: true, size: 10485760, max: 10000 })
// size: bytes; max: maximum document count (both limits enforced)
  • Guaranteed insertion order
  • Very fast writes (no index on _id by default)
  • Cannot delete or resize individual documents
  • Ideal for: log rotation, audit trails, oplog-style ring buffers

44. What are Atlas Search and text indexes?

Text indexes provide basic full-text search:

db.articles.createIndex({ title: "text", body: "text" })

db.articles.find({ $text: { $search: "mongodb performance" } },
                 { score: { $meta: "textScore" } })
            .sort({ score: { $meta: "textScore" } })

Atlas Search (MongoDB Atlas) is a richer, Lucene-based full-text search:

  • Fuzzy matching, autocomplete, synonyms, facets, highlighting
  • Separate search nodes — does not impact primary cluster performance
  • Defined with $search aggregation stage

45. How does the MongoDB aggregation pipeline handle memory limits?

By default a single aggregation pipeline stage cannot exceed 100 MB of RAM. For larger datasets:

db.bigCollection.aggregate(pipeline, { allowDiskUse: true })

allowDiskUse: true spills intermediate data to disk (slower but avoids memory errors).

In MongoDB 5.0+ the 100 MB limit was raised to 100 MB per stage with disk use allowed automatically in some contexts; Atlas has configurable limits.


46. What is the $facet stage?

$facet runs multiple sub-pipelines on the same input in a single pass — ideal for faceted search results:

db.products.aggregate([
  { $match: { category: "electronics" } },
  {
    $facet: {
      byBrand:       [{ $group: { _id: "$brand", count: { $sum: 1 } } }],
      priceRanges:   [{ $bucket: { groupBy: "$price", boundaries: [0, 100, 500, 1000], default: "1000+" } }],
      totalCount:    [{ $count: "total" }]
    }
  }
])

Returns a single document with one field per sub-pipeline.


47. How do you paginate efficiently in MongoDB?

Skip/limit (simple but slow for large offsets):

db.posts.find().sort({ _id: 1 }).skip(page * pageSize).limit(pageSize)
// Skip scans all preceding documents — O(n) cost

Keyset / cursor pagination (recommended):

// First page
const firstPage = db.posts.find().sort({ _id: 1 }).limit(10).toArray()
const lastId = firstPage[firstPage.length - 1]._id

// Next page — no skip needed
db.posts.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(10)

Keyset pagination is O(log n) and consistent regardless of offset depth.


48. What is MongoDB Atlas?

MongoDB Atlas is MongoDB's fully managed cloud service:

Feature Description
Managed clusters Automated provisioning, backups, patching
Multi-cloud AWS, GCP, Azure
Atlas Search Lucene-based full-text search
Atlas Vector Search Approximate nearest-neighbour for AI/ML
Data API / GraphQL HTTP API without drivers
Charts Built-in visualisation
Atlas App Services Serverless functions, triggers, sync
Free tier M0 — 512 MB storage forever

49. What is the difference between MongoDB and Mongoose?

MongoDB (driver) Mongoose
What it is Official Node.js driver ODM (Object-Document Mapper) for Node.js
Schema None (schemaless) Defines schemas, validation, type casting
Models Raw collection access Model classes with methods
Middleware None Pre/post hooks on save, find, remove
Overhead Low Slightly higher (schema validation, casting)
When to use Microservices, low latency, flexible schema Applications needing structure and validation

50. What are common MongoDB anti-patterns?

Anti-pattern Problem Solution
Unbounded arrays Document grows past 16 MB; slow updates Reference pattern or Bucket pattern
Bloated documents Loading unneeded fields on every query Projection; separate collections
$where / eval Server-side JS execution; slow, insecure Use native MQL operators
Index on every field Write overhead; RAM pressure Index only queried fields
No index on query fields COLLSCAN on large collections Add indexes; use explain()
Wrong shard key (monotonic) Write hot spot on one shard Hashed shard key
Multi-document txn for everything Higher latency, coordination overhead Embed related data; single-document atomicity
Ignoring write concern Silent data loss on failure Use w: "majority" in production

Common mistakes

Mistake What goes wrong Fix
Using _id as ObjectId string vs ObjectId type Query { _id: "abc" } misses { _id: ObjectId("abc") } Always cast to correct BSON type
Querying array as scalar { tags: ["a","b"] } matches exact array, not elements Use { tags: "a" } or $elemMatch
Missing $ in update { age: 31 } replaces document instead of setting field Use { $set: { age: 31 } }
$match too late in pipeline Misses index; scans entire collection Place $match first
Forgetting new: true in findOneAndUpdate Returns old document Pass { returnDocument: "after" }
Case-sensitive regex without i flag Misses differently-cased values /pattern/i or $regex with $options: "i"
Large in-memory sort without index Hits 32 MB sort limit, aborts pipeline Index sort fields
deleteMany({}) without thinking Deletes every document Always add a filter

MongoDB vs other databases

Feature MongoDB PostgreSQL DynamoDB Redis
Data model Document Relational Key-Value/Document Key-Value/Structures
Schema Flexible Strict Flexible None
ACID transactions Yes (v4.0+) Yes Yes (limited) Limited
Full-text search Text index / Atlas Search tsvector No (external) RediSearch
Horizontal scaling Sharding (native) Citus / partitioning Native Redis Cluster
Best for Flexible docs, hierarchical data Complex relations, reporting Massive scale key-value Caching, sessions, pub/sub

FAQ

Q: Is MongoDB suitable for financial applications?
A: Yes — with w: "majority" write concern, j: true journaling, and multi-document transactions. Use Decimal128 type for monetary values to avoid floating-point errors.

Q: How do I handle schema migrations in MongoDB?
A: Three strategies: (1) Lazy migration — update documents when they're next read/written; (2) Background migration script to update all documents in batches; (3) Schema versioning — add a schemaVersion field and handle multiple versions in application code.

Q: What is the $elemMatch operator?
A: Matches documents where an array field contains at least one element that satisfies all specified conditions — important when filtering on multiple fields of the same array element:

// Find products with a review where rating >= 4 AND author = "Alice"
db.products.find({ reviews: { $elemMatch: { rating: { $gte: 4 }, author: "Alice" } } })

Q: When should I use MongoDB time series collections?
A: When ingesting sensor data, metrics, or any time-stamped measurements. Time series collections (v5.0+) automatically use the Bucket pattern internally, providing better compression and faster range queries than regular collections.

Q: How does MongoDB handle null vs missing fields?
A: { field: null } matches documents where the field is null or where the field does not exist. To match only documents where the field explicitly exists, use { field: { $exists: true, $eq: null } }.

Q: What is the difference between find() cursor and aggregate() cursor?
A: Both return cursors, but find() can only filter and project — no computed fields, grouping, or joins. The aggregation pipeline is more powerful but slightly higher overhead. Prefer find() for simple queries; aggregate() for anything involving $group, $lookup, $unwind, or computed fields.

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools