RSRobince Studio

Top 50 MongoDB interview questions and answers

The 50 questions interviewers ask most about MongoDB, with short, clear answers and code where it helps. The first 25 are the most common; the rest go deeper. Free, no sign-up.

All 50 questions

  1. What is MongoDB and how does it differ from a relational database?
  2. What is a document and a collection in MongoDB?
  3. What is BSON and why does MongoDB use it?
  4. What is the `_id` field and what is an ObjectId?
  5. What are the basic CRUD operations in MongoDB?
  6. What is the difference between `find()` and `findOne()`?
  7. What are the common query operators in MongoDB?
  8. What is the difference between `$set`, `$unset`, `$inc` and `$push`?
  9. What is a projection in MongoDB?
  10. What are indexes in MongoDB and why are they important?
  11. What types of indexes does MongoDB support?
  12. How do you analyze and optimize a slow query in MongoDB?
  13. What is the aggregation pipeline in MongoDB?
  14. What does `$lookup` do and how is it like a SQL join?
  15. What is the difference between embedding and referencing in MongoDB?
  16. How do you design a schema in MongoDB?
  17. What is replication and what is a replica set in MongoDB?
  18. How does a replica set election work?
  19. What is sharding and when would you use it?
  20. How do you choose a good shard key?
  21. Does MongoDB support ACID transactions?
  22. What is write concern in MongoDB?
  23. What is read concern and read preference?
  24. What is the CAP theorem and where does MongoDB fit?
  25. When would you choose MongoDB over a SQL database?
  26. What is a compound index and why does field order matter?
  27. What is a covered query and how do you make one?
  28. What is the difference between `skip()`/`limit()` pagination and range-based pagination?
  29. What does this query return: `db.users.find({ tags: { $all: ["a", "b"] } })` compared with `{ tags: ["a", "b"] }`?
  30. When do you need `$elemMatch` instead of a normal dot-notation query?
  31. What are the common ways to update array elements, such as `$`, `$[]` and `$[<identifier>]`?
  32. What is an upsert and how can it cause duplicate documents?
  33. How do you use `$group` and what happens to memory with large pipelines?
  34. What does `$unwind` do and what happens to documents with empty or missing arrays?
  35. How would you find duplicate values in a collection?
  36. What is the difference between `$match` before `$group` and after `$group`?
  37. What are partial, sparse and TTL indexes, and when would you use each?
  38. What are the downsides of too many indexes, and how do you find unused ones?
  39. How does MongoDB's text search work, and when should you use Atlas Search instead?
  40. What is the bucket pattern and when would you use it?
  41. How do time series collections differ from regular collections?
  42. How do you enforce a schema in MongoDB?
  43. What are change streams and what are they used for?
  44. What causes replication lag and how do you reduce its impact?
  45. What happens to writes during a primary failover, and how do you handle it in an application?
  46. What is a jumbo chunk and how do you avoid it?
  47. What is the difference between a targeted query and a scatter-gather query in a sharded cluster?
  48. How would you secure a MongoDB deployment?
  49. How do you back up and restore a MongoDB database?
  50. What is the WiredTiger storage engine and what are its key features?

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

MongoDB is an open-source, document-oriented NoSQL database that stores data as flexible, JSON-like BSON documents instead of rows in fixed tables. Related data can be nested inside one document, schemas are not enforced by default, and the database scales horizontally through sharding. Relational databases use fixed schemas, normalized tables and joins, and traditionally scale vertically.

2. What is a document and a collection in MongoDB?

A document is a single record made of field-and-value pairs, similar to a JSON object, and a collection is a group of documents, similar to a table. Documents in the same collection can have different fields, though in practice they usually share a structure. Each document must have a unique _id field, and the maximum document size is 16 MB.

db.users.insertOne({ name: "Asha", age: 28, skills: ["js", "mongodb"] })

3. What is BSON and why does MongoDB use it?

BSON (Binary JSON) is the binary serialization format MongoDB uses to store documents and send them over the wire. It supports more types than JSON, such as Date, ObjectId, Decimal128, Int32, Int64 and binary data. It is also faster to traverse and encode, because fields carry length and type information.

4. What is the `_id` field and what is an ObjectId?

_id is the required primary key of every document, and it is unique within a collection and automatically indexed. If you do not supply one, MongoDB generates an ObjectId, a 12-byte value made of a 4-byte timestamp, a 5-byte random value and a 3-byte incrementing counter. Because it begins with a timestamp, ObjectIds are roughly sortable by creation time.

const id = new ObjectId()
id.getTimestamp()

5. What are the basic CRUD operations in MongoDB?

CRUD maps to insertOne/insertMany for create, find/findOne for read, updateOne/updateMany/replaceOne for update, and deleteOne/deleteMany for delete. Updates use operators such as $set and $inc to modify only specific fields. Each write operation is atomic at the single-document level.

db.users.insertOne({ name: "Ravi", age: 30 })
db.users.find({ age: { $gt: 25 } })
db.users.updateOne({ name: "Ravi" }, { $set: { age: 31 } })
db.users.deleteOne({ name: "Ravi" })

6. What is the difference between `find()` and `findOne()`?

find() returns a cursor to all documents matching the filter, while findOne() returns the first matching document itself, or null if there is none. find() is lazy and iterates in batches, so you can chain sort, skip and limit on it. Use findOne() when you expect a single result, such as a lookup by _id.

7. What are the common query operators in MongoDB?

Common operators include comparison ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin), logical ($and, $or, $not, $nor), element ($exists, $type) and array operators ($all, $elemMatch, $size). Multiple conditions in one filter object are combined with an implicit AND.

db.products.find({
  $or: [{ price: { $lt: 10 } }, { category: { $in: ["book", "toy"] } }]
})

8. What is the difference between `$set`, `$unset`, `$inc` and `$push`?

These are update operators that change only part of a document. $set sets or adds a field value, $unset removes a field, $inc increments a number atomically, and $push appends an element to an array. Using them avoids replacing the whole document and keeps updates atomic.

db.users.updateOne({ _id: 1 }, {
  $set: { city: "Pune" }, $inc: { logins: 1 }, $push: { tags: "new" }
})

9. What is a projection in MongoDB?

A projection is the second argument to find() that controls which fields are returned, reducing the data sent over the network. Use 1 to include fields or 0 to exclude them; you cannot mix inclusion and exclusion except for _id, which is returned by default unless set to 0.

db.users.find({ age: { $gt: 18 } }, { name: 1, email: 1, _id: 0 })

10. What are indexes in MongoDB and why are they important?

An index is a data structure, a B-tree, that lets MongoDB find documents without scanning the whole collection, which makes queries and sorts much faster. Without an index, a query performs a collection scan. The tradeoff is extra storage and slower writes, because every insert or update must also maintain the indexes. Every collection has a default unique index on _id.

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

11. What types of indexes does MongoDB support?

MongoDB supports single-field, compound, multikey (for arrays), text, geospatial (2dsphere), hashed, wildcard and TTL indexes, plus unique, sparse and partial options. Compound indexes follow the ESR guideline: put Equality fields first, then Sort fields, then Range fields. A TTL index automatically deletes documents after a set time.

db.orders.createIndex({ userId: 1, createdAt: -1 })
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

12. How do you analyze and optimize a slow query in MongoDB?

Use explain("executionStats") to see the winning plan, and check whether it uses an IXSCAN (index scan) or a COLLSCAN (full scan). Compare totalDocsExamined and totalKeysExamined to nReturned; a large gap means the query needs a better index. You can also use the database profiler, projections to return fewer fields, and covered queries that are served entirely from an index.

db.orders.find({ userId: 5 }).explain("executionStats")

13. What is the aggregation pipeline in MongoDB?

The aggregation pipeline is a framework that processes documents through an ordered series of stages, where each stage transforms the output of the previous one. Common stages are $match, $group, $project, $sort, $limit, $unwind, $lookup and $addFields. Put $match and $limit early so the pipeline processes fewer documents and can use indexes.

db.orders.aggregate([
  { $match: { status: "paid" } },
  { $group: { _id: "$customerId", total: { $sum: "$amount" } } },
  { $sort: { total: -1 } }
])

14. What does `$lookup` do and how is it like a SQL join?

$lookup is an aggregation stage that performs a left outer join with another collection in the same database, adding matching documents as an array field. It resembles a SQL LEFT JOIN, but the result is an array, and joins are costlier than in relational databases. If you rely on $lookup heavily, reconsider your schema and embed instead.

db.orders.aggregate([
  { $lookup: { from: "customers", localField: "customerId",
               foreignField: "_id", as: "customer" } }
])

15. What is the difference between embedding and referencing in MongoDB?

Embedding stores related data inside one document, while referencing stores it in separate documents linked by an _id. Embedding gives fast reads in a single query and atomic updates, and suits one-to-few relationships that are read together. Referencing suits one-to-many or many-to-many data, large or unbounded growth, and data shared across documents, at the cost of extra queries or $lookup.

16. How do you design a schema in MongoDB?

Design the schema around your application's query patterns rather than around normalized entities. Embed data that is read together and has bounded size, reference data that is large, unbounded or shared, and avoid growing arrays without limit because of the 16 MB document cap. Duplicating some data is acceptable when it avoids joins. Patterns such as bucket, subset and computed help with specific workloads.

17. What is replication and what is a replica set in MongoDB?

Replication keeps copies of the same data on multiple servers, and a replica set is a group of mongod instances that maintain the same dataset. One member is the primary and takes all writes, while the secondaries replicate its oplog and can take over. If the primary fails, the remaining members hold an election and choose a new primary automatically, which gives high availability.

18. How does a replica set election work?

When the primary becomes unreachable, eligible secondaries call an election, and a candidate needs votes from a majority of voting members to become the new primary. This is why you should have an odd number of voting members, for example three, or add an arbiter. The member with the most up-to-date oplog and highest priority is usually favoured. Members vote by comparing terms and oplog position.

19. What is sharding and when would you use it?

Sharding is MongoDB's method of horizontal scaling that splits a collection's data across multiple servers called shards, based on a shard key. A sharded cluster has shards (each usually a replica set), mongos routers that direct queries, and config servers that store metadata. Use it when data or throughput exceeds what a single replica set can handle, not as a first step.

20. How do you choose a good shard key?

A good shard key has high cardinality, spreads writes and reads evenly, and appears in most queries so they target a single shard. A monotonically increasing key such as a timestamp or default ObjectId creates a hotspot on the last chunk. Options are a hashed key for even distribution or a compound key combining a good-distribution field with a query field. The shard key can be changed later with resharding in MongoDB 5.0 and later.

sh.shardCollection("shop.orders", { customerId: "hashed" })

21. Does MongoDB support ACID transactions?

Yes, MongoDB supports ACID transactions: single-document operations have always been atomic, and multi-document transactions were added in 4.0 for replica sets and 4.2 for sharded clusters. Transactions carry a performance cost, so good schema design with embedding should reduce how often you need them. They run inside a session.

const s = db.getMongo().startSession()
s.withTransaction(() => {
  s.getDatabase("bank").acc.updateOne({ _id: 1 }, { $inc: { bal: -50 } })
  s.getDatabase("bank").acc.updateOne({ _id: 2 }, { $inc: { bal: 50 } })
})

22. What is write concern in MongoDB?

Write concern defines how many replica set members must acknowledge a write before MongoDB reports success. w: 1 waits for the primary only, w: "majority" waits for a majority of voting members and protects against rollback, and w: 0 does not wait for acknowledgment. The j: true option additionally waits for the write to reach the on-disk journal, and wtimeout sets a time limit.

db.orders.insertOne({ item: "pen" }, { writeConcern: { w: "majority", j: true } })

23. What is read concern and read preference?

Read concern controls the consistency and isolation of the data a read returns, with levels such as local, available, majority, linearizable and snapshot. Read preference controls which replica set member the read goes to, such as primary, primaryPreferred, secondary, secondaryPreferred or nearest. Reading from secondaries can return stale data because of replication lag.

24. What is the CAP theorem and where does MongoDB fit?

The CAP theorem says a distributed system can guarantee only two of consistency, availability and partition tolerance, and since partitions are unavoidable you effectively choose between consistency and availability. MongoDB is generally described as CP: with a single primary and majority writes, it favours consistency, and during a partition the minority side cannot accept writes. Read and write concerns let you tune this tradeoff.

25. When would you choose MongoDB over a SQL database?

Choose MongoDB when your data is hierarchical or varies in structure, the schema evolves quickly, you need horizontal scaling, or you have high write volume, such as content, catalogs, events and user profiles. Choose SQL when you need complex multi-table joins, strict relational integrity, or heavy transactional reporting. Both can be mixed, and MongoDB now supports transactions, so the decision depends on data shape and access patterns.

26. What is a compound index and why does field order matter?

A compound index covers several fields in a defined order, and it can serve queries that use a leftmost prefix of those fields. An index on { a: 1, b: 1, c: 1 } supports queries on a, on a, b, and on a, b, c, but not on b alone. Order also decides whether the index can satisfy a sort, so follow equality, sort, range (ESR) when designing it.

db.orders.createIndex({ status: 1, createdAt: -1 })
db.orders.find({ status: "paid" }).sort({ createdAt: -1 })

27. What is a covered query and how do you make one?

A covered query is answered entirely from an index, so MongoDB never reads the documents themselves. Every field in the filter and in the projection must be in the same index, and _id must be excluded unless it is indexed too. In explain(), a covered query shows totalDocsExamined: 0. Multikey indexes cannot cover queries on the array field.

db.users.createIndex({ email: 1, name: 1 })
db.users.find({ email: "a@x.com" }, { name: 1, _id: 0 })

28. What is the difference between `skip()`/`limit()` pagination and range-based pagination?

skip(n) makes MongoDB walk past n documents before returning results, so deep pages get slower and slower. Range-based (keyset) pagination instead filters on the last seen sort value, such as _id or a timestamp, and uses an index to jump straight to the next page. It is faster and stable under inserts, but it only allows moving to next or previous pages, not to an arbitrary page number.

db.posts.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).limit(20)

29. What does this query return: `db.users.find({ tags: { $all: ["a", "b"] } })` compared with `{ tags: ["a", "b"] }`?

$all matches documents whose tags array contains both "a" and "b" in any order and with any other elements. The plain { tags: ["a", "b"] } is an exact array match, so it only matches an array that is exactly ["a", "b"] in that same order. Use $all for containment and the plain form only when you really need equality.

30. When do you need `$elemMatch` instead of a normal dot-notation query?

Use $elemMatch when several conditions must hold on the same array element. With plain dot notation, each condition may be satisfied by a different element, which gives false matches. For example, { "items.qty": { $gt: 5 }, "items.price": { $lt: 10 } } can match one item with a high quantity and another with a low price.

db.orders.find({
  items: { $elemMatch: { qty: { $gt: 5 }, price: { $lt: 10 } } }
})

31. What are the common ways to update array elements, such as `$`, `$[]` and `$[<identifier>]`?

The positional $ operator updates the first array element that matched the query filter. $[] updates all elements of the array, and $[<identifier>] with arrayFilters updates only the elements that satisfy a given condition. Use $addToSet instead of $push when you want to avoid duplicates, and $pull to remove matching elements.

db.orders.updateOne(
  { _id: 1 },
  { $set: { "items.$[i].shipped": true } },
  { arrayFilters: [{ "i.qty": { $gt: 5 } }] }
)

32. What is an upsert and how can it cause duplicate documents?

An upsert is an update with upsert: true that inserts a new document if nothing matches the filter. Two concurrent upserts on the same filter can both decide to insert, creating duplicates unless a unique index exists on the filter field. With a unique index, one of them fails with a duplicate key error, which the application should catch and retry as a plain update.

db.counters.updateOne({ _id: "visits" }, { $inc: { n: 1 } }, { upsert: true })

33. How do you use `$group` and what happens to memory with large pipelines?

$group collects documents by an _id expression and applies accumulators such as $sum, $avg, $min, $max, $push and $first. Blocking stages like $group and $sort have a 100 MB memory limit per stage by default, and with allowDiskUse: true they can spill to temporary files on disk. Filtering early with $match and projecting only needed fields reduces memory use.

db.sales.aggregate([{ $group: { _id: "$region", total: { $sum: "$amt" } } }],
  { allowDiskUse: true })

34. What does `$unwind` do and what happens to documents with empty or missing arrays?

$unwind deconstructs an array field and outputs one document per array element, copying the other fields. By default, documents where the array is empty, null or missing are dropped from the output. Set preserveNullAndEmptyArrays: true to keep them, which is important for counts and reports that must include every parent document.

db.posts.aggregate([
  { $unwind: { path: "$tags", preserveNullAndEmptyArrays: true } }
])

35. How would you find duplicate values in a collection?

Group by the field, count the documents in each group, and keep the groups with a count above one. This pipeline returns each duplicated value together with the _id list of the documents that share it. To prevent duplicates in future, create a unique index on that field after cleaning the data.

db.users.aggregate([
  { $group: { _id: "$email", ids: { $push: "$_id" }, n: { $sum: 1 } } },
  { $match: { n: { $gt: 1 } } }
])

36. What is the difference between `$match` before `$group` and after `$group`?

A $match before $group filters raw input documents and can use indexes, so it reduces the work the pipeline does. A $match after $group filters the grouped results, similar to HAVING in SQL, and cannot use the collection indexes. Put every condition that does not depend on the grouped values before the group stage. The optimizer can move some matches earlier, but you should not rely on it.

37. What are partial, sparse and TTL indexes, and when would you use each?

A partial index only includes documents that match a filter expression, which makes it smaller and cheaper to maintain. A sparse index skips documents that lack the indexed field, and partial indexes are generally preferred because they are more flexible. A TTL index removes documents automatically after a time, which suits sessions, logs and tokens. The TTL deletion runs in a background task about every 60 seconds, so expiry is not instant.

db.orders.createIndex({ createdAt: 1 }, { partialFilterExpression: { status: "open" } })

38. What are the downsides of too many indexes, and how do you find unused ones?

Every index consumes disk and RAM and must be updated on each insert, update and delete, so too many indexes slow down writes and put pressure on the cache. Use the $indexStats aggregation stage to see how often each index was used since the last restart, then drop the ones with no accesses. Also remove redundant indexes, since a compound index already covers its prefix.

db.orders.aggregate([{ $indexStats: {} }])

39. How does MongoDB's text search work, and when should you use Atlas Search instead?

A text index lets you run $text queries with stemming, stop words and relevance scoring, and a collection can have only one text index. It is basic: no fuzzy matching, autocomplete or advanced ranking. For those needs, Atlas Search, built on Lucene, provides richer full-text features, but it requires MongoDB Atlas, while self-managed deployments often use an external search engine.

db.articles.createIndex({ title: "text", body: "text" })
db.articles.find({ $text: { $search: "mongodb index" } })

40. What is the bucket pattern and when would you use it?

The bucket pattern groups many small related records, such as time-series readings, into one document per time window or per count. This cuts the number of documents and index entries, and makes range reads cheaper. Keep each bucket bounded, for example one hour or 200 readings, so it never approaches the 16 MB limit. For pure time-series data, native time series collections handle bucketing for you.

41. How do time series collections differ from regular collections?

Time series collections are specialized collections, introduced in MongoDB 5.0, that store sequences of measurements with a timeField and optional metaField in a compressed internal columnar-style bucket format. They give faster inserts and queries over time ranges and much lower storage than regular collections. They are best for append-mostly data such as sensor readings and metrics, and updates and deletes are more limited.

db.createCollection("metrics", {
  timeseries: { timeField: "ts", metaField: "device", granularity: "minutes" }
})

42. How do you enforce a schema in MongoDB?

Use collection-level schema validation with a $jsonSchema validator, which checks documents on insert and update. The validationLevel setting controls whether existing documents are checked, and validationAction decides whether violations are rejected with error or only logged with warn. In application code, ODMs such as Mongoose add another layer, but database validation protects against any client.

db.createCollection("users", {
  validator: { $jsonSchema: { required: ["email"],
    properties: { email: { bsonType: "string" } } } }
})

43. What are change streams and what are they used for?

Change streams let an application subscribe to real-time changes on a collection, database or cluster, such as inserts, updates and deletes, without polling the oplog manually. They need a replica set or sharded cluster and are resumable with a resume token, so a consumer can restart without losing events. Typical uses are cache invalidation, syncing to search indexes, and event-driven microservices.

const cs = db.orders.watch([{ $match: { operationType: "insert" } }])

44. What causes replication lag and how do you reduce its impact?

Replication lag is the delay between a write on the primary and its application on a secondary. Common causes are slow disks, network latency, secondaries under heavy read load, long-running operations or very large bulk writes. You can check it with rs.printSecondaryReplicationInfo(). To limit its impact, size secondaries like the primary, avoid reading critical data from secondaries, and use w: "majority" for important writes.

45. What happens to writes during a primary failover, and how do you handle it in an application?

During an election, which usually takes a few seconds, the replica set has no primary, so writes fail until a new one is elected. Writes acknowledged with only w: 1 may be rolled back if the old primary had not replicated them. Use w: "majority" for important writes and enable retryable writes, which is the default in modern drivers, so a failed write is retried once automatically.

46. What is a jumbo chunk and how do you avoid it?

A jumbo chunk is a chunk that grows larger than the configured chunk size but cannot be split because all its documents share the same shard key value. The balancer cannot move it, so it creates uneven data distribution. Avoid it by choosing a shard key with high cardinality, and by using a compound key so that identical values can still be split into smaller ranges.

47. What is the difference between a targeted query and a scatter-gather query in a sharded cluster?

A targeted query includes the shard key (or a prefix of it), so mongos sends it only to the shards that hold the data. A scatter-gather query has no shard key in the filter, so mongos broadcasts it to every shard and merges the results, which costs more as the cluster grows. Design the shard key around your most frequent query filter.

48. How would you secure a MongoDB deployment?

Enable authentication and role-based access control, create users with least-privilege roles, and never expose the database to the public internet. Bind to private interfaces, restrict access with firewalls or VPC rules, and encrypt traffic with TLS. Enable encryption at rest and auditing where needed, and for sensitive fields consider client-side field level encryption. Keep MongoDB updated and rotate credentials.

49. How do you back up and restore a MongoDB database?

Common methods are mongodump and mongorestore for logical backups, filesystem or cloud snapshots for large deployments, and managed continuous backups with point-in-time recovery in Atlas. Logical dumps are simple but slow for large data. For a consistent snapshot of a replica set, take it from a secondary, and use --oplog with mongodump for point-in-time consistency.

mongodump --uri="mongodb://localhost:27017/shop" --oplog --out=/backup
mongorestore --oplogReplay /backup

50. What is the WiredTiger storage engine and what are its key features?

WiredTiger is MongoDB's default storage engine since version 3.2. It provides document-level concurrency control, so multiple writes to different documents in one collection do not block each other. It also offers compression with snappy by default (zstd and zlib are options), a configurable internal cache that defaults to about 50% of RAM minus 1 GB, and a journal with checkpoints for crash recovery.

More Databases interview questions

Mongoose · DBMS and SQL · All interview questions