Documents & BSON • Schema Design • Indexes • Aggregation • Replication & Sharding • 2026

MongoDB Interview Questions

31 questions What each one tests, an answer frame, a spoken answer 38 min read

This page is for developers facing a MongoDB round, from a first backend job to a senior data role. Most rounds open with documents, BSON and ObjectId, then spend real time on schema design: when to embed, when to reference, and how data grows. Next come indexes and explain, a query or aggregation you write live, and then replica sets, sharding, write concern and transactions for more senior roles. Each question shows what the interviewer is checking, the shape of a strong answer and a short answer you can say out loud. Practise saying them, then swap in stories from your own projects.

Search all questions by round, difficulty and level, or save the ones you want to practise.

Documents & BSON 3 questions

Easy Technical round Fresher Practice question

1. What is a document in MongoDB, and why does MongoDB store documents as BSON instead of plain JSON?

What the interviewer is really testing:
Whether you understand the basic unit of data and why its binary format matters for types and speed, not just that MongoDB "stores JSON".
Answer frame:

Document: one record made of field and value pairs, which can nest documents and arrays.

Collection: a group of documents, like a table, but the documents need not share fields.

BSON: a binary form of JSON with extra types (dates, 64-bit integers, decimals, ObjectId, binary) and size prefixes so fields can be skipped quickly.

Sample spoken answer:

"A document is one record, stored as field and value pairs, much like a JSON object. Values can be nested documents or arrays, so one order can carry its line items inside it. A collection is a group of documents, roughly like a table, except the documents don't have to share the same fields unless I add validation. Under the hood MongoDB stores documents as BSON, which is a binary form of JSON. There are two reasons. First, types: JSON only has strings, numbers, booleans, arrays, objects and null, but BSON adds dates, 32 and 64-bit integers, decimals, ObjectIds and raw binary. Second, speed: BSON records the size of documents, arrays and strings up front, so the server can skip over them without parsing text. One limit worth knowing is that a single document can't be larger than 16 megabytes."

Red flag to avoid:

Saying MongoDB stores raw JSON text, or that documents in a collection must all have the same fields.

They may ask next:
  • Why would you store a price as a decimal type rather than a double?
  • What happens when one document stores a field as a string and another stores it as a number, and you query for the number?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

2. What is inside an ObjectId, and can you rely on it to sort documents by when they were created?

What the interviewer is really testing:
Whether you know how the default _id is built and the limits of using it as a creation timestamp.
Answer frame:

Structure: 12 bytes: a 4-byte timestamp in seconds, a 5-byte random value per process, a 3-byte counter.

Where made: usually by the driver on the client, so no round trip to the server is needed.

Ordering: roughly by creation time, only to the second, and not exact across machines.

Sample spoken answer:

"An ObjectId is the default type for _id, and it's 12 bytes. The first four bytes are a timestamp in seconds since the Unix epoch. Then there are five random bytes generated once per process, and the last three bytes are a counter that starts at a random value. The driver normally creates it on the client, which is why an insert doesn't need to ask the server for an id first. Because the timestamp comes first, sorting by _id gives you roughly the order documents were created, and I can read the time back with getTimestamp. But I wouldn't rely on it for exact order. It only has one-second precision, and two app servers with slightly different clocks can produce ids out of order. When the order really matters, I store a proper createdAt date and index it."

Red flag to avoid:

Claiming ObjectIds are generated by the server in strict global order, or that they are random and carry no time information.

They may ask next:
  • Can you use your own value for _id instead of an ObjectId, and when would you?
  • Is an ObjectId safe to expose in a public URL?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

3. What kinds of applications suit MongoDB well, and where would you rather keep a relational database?

What the interviewer is really testing:
Whether you pick a database from access patterns and relationships rather than hype, and know that transactions alone no longer decide it.
Answer frame:

Good fit: data read as a whole object, fields that vary between records, schema that changes often, large write volume that needs to scale out.

Weaker fit: many-to-many data queried from many angles, heavy ad-hoc joins and reporting, rules best enforced by foreign keys.

Deciding factor: how the data is read and written, not whether the database has transactions.

Sample spoken answer:

"I think about how the data is read. MongoDB fits well when the thing I load is naturally one object: a product with attributes that differ by category, a user profile with settings, an article with its tags, or event data coming in at high volume. Documents map straight to the objects in my code, I can add fields without a migration, and sharding lets me scale writes across machines. I'd lean relational when the data is a web of relationships that people query from many angles, like an accounting ledger or an inventory system where reports join five tables in ways nobody predicted, and where foreign keys and constraints are doing real work. It isn't about transactions any more, since MongoDB has multi-document transactions. It's about whether my queries line up with how I can shape the documents."

Red flag to avoid:

Saying MongoDB is always faster, or that it cannot do transactions, or choosing it only because the schema is "flexible".

They may ask next:
  • If a team only needs flexible custom fields on one table, would you add MongoDB or use a JSON column?
  • What does a relational database give you for free that you have to design for yourself in MongoDB?
Say it in 60 seconds

Schema Design 6 questions

Medium Technical round Fresher, Mid-level, Senior Practice question

4. How do you decide whether to embed related data inside a document or keep it in its own collection with a reference?

What the interviewer is really testing:
The core MongoDB modelling skill: whether you design from how the data is queried and how big it can grow.
Answer frame:

Embed when: the data is read together, belongs to one parent, and stays small and bounded.

Reference when: the data is shared, read on its own, updated often by itself, or can grow without limit.

Middle ground: keep a reference but copy a few fields you always show, and accept updating those copies.

Sample spoken answer:

"I start from the queries. If I almost always read two things together and the child only belongs to one parent, I embed. An order's line items or a user's shipping addresses are good examples: there are a few of them, they're owned by the parent, and one read gets everything. I reference when the related data is shared by many parents, is read on its own, changes independently a lot, or can grow without a limit. Comments on a popular post or orders for a customer can run into the thousands, so they go in their own collection with an indexed field pointing back. Often I do a bit of both: I keep the author's id on a post but also copy the author's name, because that's what the page shows. The cost is that renaming an author means updating those copies, which is fine if it's rare."

Red flag to avoid:

Normalising everything as if it were a relational schema, or embedding everything including data that grows without limit.

They may ask next:
  • How would you model a many-to-many relationship like students and courses?
  • What does it cost you when copied fields go out of date?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

5. Why is an array that keeps growing without limit a problem inside a document, and how would you model that data instead?

What the interviewer is really testing:
Whether you have seen the classic anti-pattern and know more than one pattern to fix it.
Answer frame:

Problems: the 16 megabyte document limit, every read loading the whole array, and huge index entries if the array is indexed.

Separate collection: one document per item with an indexed reference back to the parent.

Subset or bucket: keep only the latest few embedded, or group items into bucket documents of fixed size.

Sample spoken answer:

"The hard limit is that a document can't pass 16 megabytes, so an array that grows forever will eventually break inserts. But it hurts long before that. Every time I read the parent, I pull the whole array into memory and over the network, even if the page shows five items. Updates get heavier as the document grows. And if the array is indexed, each element gets its own index entry, so the index balloons. The usual fix is to move the items into their own collection, one document each, with the parent's id and a date, and a compound index on those two fields. If the page always shows the newest few, I also keep a small embedded copy, using push with the slice option so it never holds more than, say, ten. For high-volume data like readings, I use buckets: one document per parent per hour, holding a fixed number of items."

Code:
db.posts.updateOne(
  { _id: postId },
  { $push: { recentComments: { $each: [comment], $slice: -10 } },
    $inc: { commentCount: 1 } }
)
Red flag to avoid:

Not knowing about the document size limit, or suggesting a bigger server as the fix.

They may ask next:
  • How would you page through the comments once they live in their own collection?
  • What keeps the embedded recent list and the full collection from disagreeing?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

6. People call MongoDB schemaless. How do you still make sure the documents in a collection have the right shape?

What the interviewer is really testing:
Whether you treat flexible schema as a tool rather than an excuse, and know the server-side option as well as the app-side one.
Answer frame:

Server side: a JSON Schema validator on the collection, with required fields, types and allowed values.

Rollout controls: validation action (error or warn) and validation level (strict or moderate).

App side: a schema layer such as Mongoose, plus a version field for documents that change shape.

Sample spoken answer:

"Flexible schema doesn't mean no schema. The shape just lives somewhere you choose. My first line is a validator on the collection itself, written in JSON Schema: I list required fields, their BSON types and allowed values, and the server rejects any insert or update that breaks the rules, no matter which app or script sent it. There are two useful knobs. The validation action can be error, which rejects the write, or warn, which lets it through and logs it, and I use warn when I'm adding rules to messy existing data. The validation level can be strict, or moderate, which skips checks on updates to documents that were already invalid. On top of that, the app usually has its own schema, like a Mongoose model, and for collections that evolve I add a schemaVersion field so code knows which shape it's reading."

Code:
db.createCollection("orders", {
  validator: { $jsonSchema: {
    bsonType: "object",
    required: ["customerId", "total", "status"],
    properties: {
      total: { bsonType: "decimal" },
      status: { enum: ["pending", "paid", "shipped"] }
    }
  } },
  validationAction: "error"
})
Red flag to avoid:

Saying there is no way to enforce structure in MongoDB, or relying only on app code when several services write to the same collection.

They may ask next:
  • How would you add a validator to a collection that already holds millions of documents?
  • If Mongoose already validates, why add a validator on the server too?
Say it in 60 seconds
Medium System design round Mid-level, Senior Practice question

7. Design the MongoDB collections for a blog with authors, posts, tags and comments. Walk me through your choices and your indexes.

What the interviewer is really testing:
Whether you can turn read patterns into collections and indexes, and explain each trade-off rather than draw tables.
Answer frame:

Queries first: list the pages: a post with its comments, an author page, a tag listing, the home feed.

Collections: authors, posts with a copied author summary and a tags array, comments in their own collection.

Indexes: a unique slug, author plus date, tag plus date, and post plus date on comments.

Trade-offs: copied author names need updating on rename; recent comments embedded for the first paint.

Sample spoken answer:

"I'd start by listing the pages: open a post, list posts by an author, list posts by tag, and the home feed by date. Then I'd have three collections. Authors hold the profile. Posts hold the title, body, a unique slug, a publishedAt date, a tags array, and a small author summary with the id, name and avatar, because every post page shows those. Comments go in their own collection with the postId and a date, because a popular post can get thousands. On the post I'd keep a comment count and maybe the latest three comments so the first view needs one read. For indexes: unique on slug, author id plus publishedAt descending for the author page, tags plus publishedAt for tag pages, which becomes a multikey index, and postId plus createdAt on comments. The trade-off is that renaming an author means updating their posts, which I'd do in a background job."

Red flag to avoid:

Embedding every comment inside the post with no limit, or designing collections without saying which queries they serve.

They may ask next:
  • How would the home feed change if each reader only sees posts from authors they follow?
  • Where would you store likes, and how would you stop one user liking twice?
Say it in 60 seconds
Hard System design round Senior Practice question

8. Thousands of sensors each send a reading every few seconds. How would you store that in MongoDB so it stays fast as the data piles up?

What the interviewer is really testing:
Whether you know the bucket pattern and time series collections, and think about retention and shard keys before the data gets big.
Answer frame:

Problem: one document per reading means a huge document count and very large indexes.

Buckets: group readings per sensor per time window, with summary fields precomputed.

Built-in option: a time series collection, which buckets internally, plus automatic expiry for retention.

Scale: shard on sensor plus time, never on time alone.

Sample spoken answer:

"One document per reading works at first, but at this rate the document count and the indexes grow fast, and the indexes stop fitting in memory. The classic fix is the bucket pattern: one document per sensor per hour, holding an array of readings plus summary fields like count, min, max and sum that I update as readings arrive. Charts that only need hourly numbers never touch the raw values. Newer MongoDB versions give me that without hand-rolling it: a time series collection, where I name the time field and a meta field like the sensor id, and the server stores readings in compressed buckets internally. I'd set an expiry so raw data older than my retention window drops off, and roll older data into summaries if the business needs history. If it outgrows one replica set, I'd shard on sensor id plus time. Time alone would send every new write to the same shard."

Code:
db.createCollection("readings", {
  timeseries: { timeField: "ts", metaField: "sensor", granularity: "seconds" },
  expireAfterSeconds: 60 * 60 * 24 * 30
})
db.readings.insertOne({ ts: new Date(), sensor: { id: "s-104", site: "plant-2" }, temp: 21.4 })
Red flag to avoid:

Storing each reading as a separate document with no plan for size, or sharding on the timestamp alone.

They may ask next:
  • How would you serve a chart of daily averages for the last year without scanning raw readings?
  • What happens to your design when a sensor goes offline and later sends an hour of readings at once?
Say it in 60 seconds
Hard Situational round Senior Practice question

9. You need to add a required field to a collection with tens of millions of documents, in production, with no downtime. How do you roll it out?

What the interviewer is really testing:
Whether you can change a live schema in safe steps, protecting both the application and the replica set.
Answer frame:

Code first: release code that handles the field missing, and writes it on every new document.

Backfill: update old documents in small batches, throttled, watching replication lag.

Enforce last: add a validator, in warn mode first, then error, once the backfill is complete.

Safety: make each step reversible and the backfill restartable.

Sample spoken answer:

"I'd do it in steps so nothing breaks at any point. First, ship code that reads the field with a sensible default when it's missing, and writes it on every new or updated document. At this point the app works with both shapes. Second, backfill the old documents. I wouldn't run one giant updateMany, because tens of millions of changes flood the oplog and can push secondaries far behind. Instead I'd walk the collection in _id order, updating a few thousand documents per batch where the field doesn't exist, with a pause between batches and a check on replication lag. Because the filter only matches documents still missing the field, I can stop and restart safely. Third, once a count shows none are missing, I add a validator requiring the field, first in warn mode to catch any writer I forgot, then in error mode. Only then does the code treat the field as required."

Red flag to avoid:

One huge update run in business hours, or adding a strict validator before old documents and all writers are ready.

They may ask next:
  • How would you find a service you forgot about that still writes documents without the field?
  • What would you do if the new value has to be computed from another collection?
Say it in 60 seconds

Queries & Updates 3 questions

Easy Coding round Fresher Practice question

10. Write a query that finds active users aged 18 to 30 in any of three cities, and returns only their name and email.

What the interviewer is really testing:
Whether you can write a basic filter with comparison and set operators and a projection without looking it up.
Answer frame:

Filter: fields in one filter document are combined with AND; use range operators for age and an in-list for cities.

Projection: include name and email, and turn _id off explicitly because it is returned by default.

Extras: sort and limit so the result is predictable and bounded.

Sample spoken answer:

"I'd call find on the users collection with two documents. The first is the filter. Fields listed together are combined with AND, so I put status equal to active, then age with greater-than-or-equal 18 and less-than-or-equal 30 in the same sub-document, which gives an inclusive range. For the cities I use the in operator with an array of the three names, which matches any of them. The second document is the projection: name one and email one. The _id field comes back unless I say otherwise, so I set it to zero. I'd also add a sort on name and a limit so the result is predictable and can't return the whole collection by accident. If this runs often, a compound index on status, city and age would serve the filter, and I'd check the plan with explain."

Code:
db.users.find(
  {
    status: "active",
    age: { $gte: 18, $lte: 30 },
    city: { $in: ["Austin", "Lisbon", "Manila"] }
  },
  { name: 1, email: 1, _id: 0 }
).sort({ name: 1 }).limit(50)
Red flag to avoid:

Writing age twice as two separate keys in the filter, where the second silently overwrites the first.

They may ask next:
  • How would you match two conditions on the same element of an array of addresses?
  • How would you write an OR across two different fields, like city or postcode?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

11. What do the set, inc, push and addToSet update operators do, and what happens if you send an update with no operator at all?

What the interviewer is really testing:
Whether you can change part of a document safely and know the difference between updating and replacing.
Answer frame:

Field changes: set writes a field; inc adds to a number and creates it if missing.

Arrays: push appends every time; addToSet appends only if that exact value is not already there.

No operator: updateOne rejects it; replaceOne swaps the whole document, keeping only _id.

Sample spoken answer:

"Set writes a value to a field, adding it if it doesn't exist, and leaves every other field alone. Inc adds a number to a field, or creates it with that number, and because a single-document update is atomic, two requests incrementing a counter at the same moment both count. Push appends to an array every time, so it can add duplicates. AddToSet only appends if the exact value isn't already in the array, which is handy for tags or a list of user ids who liked something. If I pass a plain document with no operators to updateOne, the modern drivers and the shell reject it, because an update must say what to change. If I really want to swap the whole document, that's replaceOne, and everything except _id is replaced. The older update method used to do that silently, which is how people wiped fields by accident."

Code:
db.posts.updateOne(
  { _id: postId },
  {
    $inc: { views: 1 },
    $set: { lastViewedAt: new Date() },
    $addToSet: { tags: "databases" }
  }
)
Red flag to avoid:

Reading the document, changing it in app code and writing it back, which loses concurrent changes, when an operator would do it atomically.

They may ask next:
  • How would you make this update create the document if it does not exist yet?
  • How do you remove one specific element from an array?
  • Can one update both set and unset the same field?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

12. How would you paginate a large collection for an infinite scroll feed? Why does skip and limit get slow on deep pages?

What the interviewer is really testing:
Whether you know why skip scales badly and can build range-based paging on a unique, indexed sort key.
Answer frame:

Skip and limit: simple, but the server still walks past every skipped entry, so a deep page costs far more than page one.

Range paging: remember the last item's sort values and ask for items after them, served straight from an index.

Tie-breaker: sort on a unique pair, such as createdAt plus _id, so no item is skipped or shown twice.

Trade-off: no jumping straight to page 37, which an infinite scroll does not need anyway.

Sample spoken answer:

"Skip and limit is the first thing people write, and it's fine for the first few pages. The problem is that skip isn't free. To skip fifty thousand documents the server still walks past fifty thousand index entries, so deep pages get slower and slower. It can also show duplicates or miss items when new posts arrive between page loads, because everything shifts. For a feed I use range-based paging, sometimes called keyset or cursor paging. I sort by createdAt descending with _id as a tie-breaker, since two posts can share a timestamp. The client sends back the createdAt and _id of the last item it saw, and I ask for items older than that, with a limit. With an index on createdAt and _id, every page costs about the same no matter how deep. The trade-off is that you can't jump straight to page 37, but an infinite scroll never needs to."

Code:
db.posts.createIndex({ createdAt: -1, _id: -1 })

// next page, given the last item the client saw
db.posts.find({
  $or: [
    { createdAt: { $lt: last.createdAt } },
    { createdAt: last.createdAt, _id: { $lt: last._id } }
  ]
}).sort({ createdAt: -1, _id: -1 }).limit(20)
Red flag to avoid:

Saying skip is free because an index exists, or paging on a sort key that is not unique so items repeat or go missing.

They may ask next:
  • How would you show a total count of results, and is it worth the cost on a big collection?
  • What changes if users can sort the feed by number of likes instead of by date?
Say it in 60 seconds

Indexes & Performance 7 questions

Easy Technical round Fresher, Mid-level Practice question

13. What happens when you run a query that no index supports, and what changes once you add the right index?

What the interviewer is really testing:
Whether you understand what an index physically is and what it costs, beyond "indexes make queries fast".
Answer frame:

Without: a collection scan reads every document to find matches.

With: a sorted B-tree of field values points to documents, so the server jumps to the matching range.

Cost: each index takes disk and memory and slows every insert, update and delete a little.

Sample spoken answer:

"Without a usable index, MongoDB does a collection scan: it reads every document and checks it against the filter. On a small collection that's fine, but on millions of documents it's slow and pushes useful data out of memory. An index is a sorted structure, a B-tree, holding the values of the indexed fields and a pointer to each document. With it, the server walks straight to the range of keys that match, then fetches only those documents. In explain output that's the difference between a COLLSCAN stage and an IXSCAN followed by a FETCH. Every collection gets a unique index on _id automatically. Indexes aren't free, though. Each one uses disk and memory, and every write has to update every index on the collection, so I only add the ones my real queries need."

Red flag to avoid:

Suggesting you index every field, or not knowing that indexes slow down writes.

They may ask next:
  • Can a single index help with both the filter and the sort of one query?
  • Why not just index every field to be safe?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

14. A query filters on status, sorts by date and has a range on price. How would you order the fields in a compound index, and why?

What the interviewer is really testing:
Whether you can reason about index order from how a B-tree is walked, and avoid the in-memory sort that kills performance.
Answer frame:

Rule of thumb: Equality fields first, then Sort fields, then Range fields.

Why sort before range: after the equality match, entries are already in date order, so no in-memory sort is needed.

Prefixes: an index on a, b, c also serves queries on a, and on a and b, but not on b alone.

Verify: check with explain; a very selective range can justify a different order.

Sample spoken answer:

"I'd use the equality, sort, range rule, so status first, then the date, then price. Here's why. Status is an exact match, so the server jumps to one contiguous block of the index. Inside that block, entries are ordered by date, so the results come out already sorted and the server can stop as soon as it has enough for the limit. Price goes last, and the server checks it while walking the keys. If I put price before the date, the entries for different prices would interleave their dates, and MongoDB would have to sort the results in memory, which is slow and has a memory cap. I also remember the prefix rule: this index serves queries on status alone, and status with date, but not date alone. It's a rule of thumb, not a law. If the price range is tiny and very selective, I'd compare both orders with explain."

Code:
db.products.createIndex({ status: 1, createdAt: -1, price: 1 })

db.products.find({ status: "live", price: { $gte: 10, $lte: 50 } })
  .sort({ createdAt: -1 })
  .limit(20)
Red flag to avoid:

Ordering fields by how selective they seem without thinking about the sort, or not knowing the prefix rule.

They may ask next:
  • Would this index still avoid a sort if the query sorted by date ascending?
  • What if the sort is on two fields in opposite directions?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

15. What is a multikey index, and what limit do you hit when you build a compound index over array fields?

What the interviewer is really testing:
Whether you know how arrays are indexed and the restriction that surprises people in production.
Answer frame:

What it is: an index on an array field gets one entry per element; MongoDB marks it multikey automatically.

Use: fast queries like "posts tagged databases" on a tags array.

Limit: in a compound index, each document can have at most one indexed field that holds an array.

Sample spoken answer:

"When I index a field that holds an array, MongoDB creates one index entry for every element, and flags the index as multikey on its own. I don't declare it. That's what makes a query like find posts where tags equals databases fast on a tags array. Two things to watch. First, size: a document with fifty tags adds fifty entries, so long arrays make the index big and writes heavier. Second, the compound limit: in a compound multikey index, any one document can have at most one of the indexed fields as an array. If I index tags and categories together and a document has arrays in both, the insert fails, and if such documents already exist, the index build fails. So I'd either index them separately or rethink the model. Multikey indexes also can't be a shard key."

Red flag to avoid:

Thinking you must declare an index as multikey, or not knowing the one-array-per-document rule for compound indexes.

They may ask next:
  • How would you index an array of embedded documents so you can query on one of their fields?
  • Can a multikey index cover a query on the array field without fetching documents?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

16. How would you make login sessions or one-time codes delete themselves automatically after a set time?

What the interviewer is really testing:
Whether you know TTL indexes and their catches, rather than writing a cron job to clean up.
Answer frame:

TTL index: an index on a date field with an expiry in seconds; a background task deletes expired documents.

Per-document time: set expiry to zero and store the exact expiry date in each document.

Catches: deletion is not instant, and documents without a real date in that field never expire.

Sample spoken answer:

"I'd use a TTL index. I create an index on a date field, like createdAt, with an expiry in seconds, say one hour for sessions, and a background task on the server removes documents once that time has passed. If different documents need different lifetimes, I set the expiry to zero and store an expireAt date in each document, so each one leaves at its own time. There are catches I always mention. The cleanup task runs about once a minute, so a document can hang around a little after it expires, which means my login check should still compare the date itself and never trust existence alone. The field has to hold a real BSON date. A string that looks like a date, or a missing field, means the document never expires. And on a replica set, the deletes happen on the primary and replicate like any other write."

Code:
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

db.otps.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 })
db.otps.insertOne({ userId, code: "482913", expireAt: new Date(Date.now() + 5 * 60 * 1000) })
Red flag to avoid:

Assuming documents vanish at the exact second, or storing the expiry as a string.

They may ask next:
  • Why might a one-time code still be accepted a few seconds after it expired, and how do you guard against that?
  • How would you change the expiry time on an existing TTL index?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

17. How do you use explain to judge whether a query is using its indexes well? Which numbers do you look at first?

What the interviewer is really testing:
Whether you can read a query plan and turn it into a fix, which is most of real MongoDB performance work.
Answer frame:

Mode: run explain with executionStats so you see what actually ran, not just the chosen plan.

Stages: COLLSCAN is a full scan; IXSCAN then FETCH is normal; a SORT stage means an in-memory sort.

Ratios: compare documents returned with keys and documents examined; close together is healthy.

Sample spoken answer:

"I run the query with explain in executionStats mode, so I see what actually happened. First I look at the winning plan's stages. A COLLSCAN on a big collection means no index was used. IXSCAN followed by FETCH is the normal good case. A SORT stage means the results were sorted in memory, which usually tells me the index doesn't match the sort. Then I compare three numbers: nReturned, totalKeysExamined and totalDocsExamined. In a healthy query they're close. If it returned twenty documents but examined two hundred thousand, the index is either missing a field or the fields are in the wrong order. If totalDocsExamined is zero, the index covered the whole query, which is the best case. I also glance at executionTimeMillis and at rejected plans, to see what else the planner considered."

Code:
db.orders.find({ customerId: cid, status: "paid" })
  .sort({ createdAt: -1 })
  .limit(20)
  .explain("executionStats")
Red flag to avoid:

Only checking that an index was used, without comparing documents examined against documents returned.

They may ask next:
  • What is a covered query, and what has to be true for one?
  • How would you find slow queries in production when you do not know which ones to explain?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

18. Emails must be unique, but some users sign up by phone and have no email. How do you set up the index so that works?

What the interviewer is really testing:
Whether you know how unique indexes treat missing fields and can use a partial index correctly.
Answer frame:

The trap: a plain unique index treats a missing field as null, so only one user can lack an email.

Sparse: skips documents without the field, but an explicit null still collides.

Partial: a unique index with a filter such as "email is a string" indexes only real emails.

Query rule: the planner uses a partial index only when the query fits inside its filter.

Sample spoken answer:

"A plain unique index on email won't work, because documents without the field are indexed as null, so the second phone-only user fails with a duplicate key error. A sparse unique index skips documents that don't have the field, which helps, but if any code writes email as an explicit null, those still collide. What I'd use is a partial index: unique on email, with a filter that only includes documents where email is a string. Now phone-only users aren't in the index at all, and real emails are still unique. Two more things. The planner only picks a partial index when it can tell the query's filter falls inside the index filter, so I confirm with explain. And I lowercase and trim emails before saving, or two users could register the same address with different capital letters."

Code:
db.users.createIndex(
  { email: 1 },
  { unique: true, partialFilterExpression: { email: { $type: "string" } } }
)
Red flag to avoid:

Believing a unique index ignores documents that lack the field, or enforcing uniqueness only with a check in app code.

They may ask next:
  • How would you enforce that each user has at least an email or a phone?
  • What happens if you try to build this index on a collection that already has duplicates?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

19. A teammate wants to drop an index that looks unused, to speed up writes. How do you make sure dropping it is safe?

What the interviewer is really testing:
Whether you check usage properly across the whole replica set and know the safe way to test a removal.
Answer frame:

Usage stats: check index usage counters on every member that serves reads; they reset on restart.

Hidden jobs: an index may enforce uniqueness or expire documents even if no query uses it.

Overlap: it may be redundant because it is a prefix of another index.

Test: hide the index first, watch, then drop.

Sample spoken answer:

"Good instinct, since every index costs something on every write, but I'd check a few things first. I'd look at the index usage stats, which count how often each index was used. Those counters are per server and reset when a server restarts, so I check every member that serves reads, over a period that covers monthly jobs and reports, not just one quiet afternoon. Next, I'd check whether it does a job besides speeding up queries. A unique index guards data, and a TTL index deletes expired documents, so dropping either changes behaviour even with zero reads. I'd also see whether another index already starts with the same fields, which makes this one redundant. Then, rather than dropping it straight away, I'd hide it. A hidden index is still maintained but the planner ignores it, so if something slows down I can unhide it instantly instead of waiting for a rebuild. After a week or two with no problems, I drop it."

Red flag to avoid:

Dropping it because one server shows zero uses since last week, without checking uniqueness, TTL or other members.

They may ask next:
  • Why does hiding an index not give you the write speed-up yet?
  • How would you find indexes that are exact duplicates or prefixes of each other?
Say it in 60 seconds

Aggregation 3 questions

Easy Technical round Fresher, Mid-level Practice question

20. Walk me through the aggregation pipeline. What is a stage, and which stages do you reach for most often?

What the interviewer is really testing:
Whether you understand the pipeline model and the few stages that cover most real reports.
Answer frame:

Model: documents flow through stages in order; each stage reshapes the stream for the next.

Everyday stages: match to filter, group to summarise, project to shape, sort and limit, unwind to flatten arrays, lookup to join.

Order matters: filter and sort early so they can use indexes and later stages see fewer documents.

Sample spoken answer:

"The aggregation pipeline is a list of stages, and documents flow through them in order, like an assembly line. Each stage takes the documents from the one before and passes a new stream on. The ones I use most are match, which filters just like a find; group, which buckets documents by a key and computes sums, counts or averages; project or its cousin addFields, to rename, compute or drop fields; sort and limit; unwind, which turns one document with an array into one document per element; and lookup, which pulls matching documents from another collection, a bit like a left join. Order matters a lot. A match at the start can use an index and cuts down what every later stage has to handle, so I filter as early as possible. The server does reorder some stages to help, but I don't count on that."

Red flag to avoid:

Putting the filter at the end of the pipeline, or not knowing that only early stages can use indexes.

They may ask next:
  • When would you use a find query instead of an aggregation?
  • What does unwind do to a document whose array is empty?
Say it in 60 seconds
Medium Coding round Mid-level Practice question

21. Write an aggregation that returns the top five customers by total spend on completed orders last month, with their names.

What the interviewer is really testing:
Whether you can write a real multi-stage pipeline and order the stages so it stays cheap.
Answer frame:

Filter first: match on status and the date range, backed by an index.

Summarise: group by customer id with a sum of amounts and a count.

Rank: sort by total descending and limit to five.

Enrich last: lookup customer names only for those five, then shape the output.

Sample spoken answer:

"I'd start with a match on status completed and createdAt within last month, using greater-than-or-equal the first day and less-than the first day of this month, so I don't miss orders late on the last day. An index on status and createdAt makes that fast. Then a group on customerId, summing the amount and counting orders. Then sort by total descending and limit five. The server combines sort followed by limit into a top five, so it doesn't sort everyone in full. Only after that do I do the lookup into customers, so I join five documents instead of thousands. Unwind turns the single-element customer array into a plain object, and a final project picks out the name, total and order count. In real code I'd compute the two dates in the app rather than hard-code them."

Code:
db.orders.aggregate([
  { $match: { status: "completed",
      createdAt: { $gte: ISODate("2026-08-01"), $lt: ISODate("2026-09-01") } } },
  { $group: { _id: "$customerId", total: { $sum: "$amount" }, orders: { $sum: 1 } } },
  { $sort: { total: -1 } },
  { $limit: 5 },
  { $lookup: { from: "customers", localField: "_id", foreignField: "_id", as: "customer" } },
  { $unwind: "$customer" },
  { $project: { _id: 0, name: "$customer.name", total: 1, orders: 1 } }
])
Red flag to avoid:

Doing the lookup before grouping, which joins every order, or using less-than-or-equal on the last day and missing late orders.

They may ask next:
  • Why is the lookup placed after the limit and not before the group?
  • How would you change this to show the top five per country?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

22. An aggregation on a large collection fails with an error about exceeding the memory limit. What is going on, and how do you fix it?

What the interviewer is really testing:
Whether you understand which stages hold data in memory and fix the pipeline, not just flip a flag.
Answer frame:

Cause: blocking stages like group and sort hold data in memory, and each stage has a cap of about 100 megabytes.

Real fixes: match early, drop unneeded fields before grouping, let an index provide the sort.

Spill to disk: allowing disk use lets those stages spill; it works but is slower.

Different limit: a single output document over 16 megabytes is a design problem, not memory.

Sample spoken answer:

"Stages like group and sort can't stream. They have to hold data before they can output anything, and each of those stages has a memory limit of about 100 megabytes. When the working set passes that and spilling to disk isn't allowed, the pipeline fails. My first fixes are to the pipeline itself. I move the match as early as possible so fewer documents go in, add a project before the group so each document carries only the fields I need, and put the sort right after the match so an index can supply the order instead of an in-memory sort. Then there's the allowDiskUse option, which lets those stages write temporary files. Newer versions do that by default, but it's slower. One trap is different: if a group pushes everything into one array, it can hit the 16 megabyte document limit, and disk won't fix that. For reports that run every day, I'd precompute results into a summary collection with a merge stage."

Red flag to avoid:

Only saying "turn on allowDiskUse" without reducing the data flowing through the pipeline.

They may ask next:
  • How would you keep a daily summary collection up to date without recomputing all of history?
  • Which stages can use an index, and which cannot?
Say it in 60 seconds

Replication & Sharding 3 questions

Medium Technical round Mid-level, Senior Practice question

23. How does a replica set work, and what exactly happens when the primary goes down?

What the interviewer is really testing:
Whether you understand replication, elections and what the application sees during a failover.
Answer frame:

Roles: one primary takes writes and records them in the oplog; secondaries copy and replay it.

Election: members exchange heartbeats; if the primary is unreachable, a secondary calls an election and needs a majority of votes.

App impact: a short window with no primary; retryable writes and a replica set connection string ride through it.

Rollback risk: writes only the old primary had can be rolled back; majority write concern prevents that.

Sample spoken answer:

"A replica set is a group of mongod servers holding the same data, usually three. One is the primary and takes all writes, recording each change in the oplog, a special capped collection. The secondaries tail that oplog and apply the same changes, a little behind. Members send each other heartbeats every couple of seconds. If the secondaries lose contact with the primary for the election timeout, which is ten seconds by default, an eligible secondary calls an election and needs votes from a majority of voting members. That's why you want an odd number of voters. For those seconds there's no primary, so writes fail, and drivers with retryable writes will retry once after the new primary is found. One subtle thing: if the old primary accepted writes that never reached a secondary, those get rolled back when it rejoins. Majority write concern is how I avoid losing them."

Red flag to avoid:

Saying secondaries share the write load, or that a failover is instant and loses nothing whatever the write concern.

They may ask next:
  • Why is a two-member replica set a bad idea, and where does an arbiter fit in?
  • What should your connection string include so the app finds the new primary quickly after a failover?
Say it in 60 seconds
Hard Technical round Senior Practice question

24. How do you choose a shard key for a big collection, and what goes wrong when you pick a bad one?

What the interviewer is really testing:
Whether you can reason about data distribution and query routing, the decision in sharding that is hardest to undo.
Answer frame:

Setup: mongos routers send queries to shards using ranges kept on config servers; a balancer moves ranges.

Good key: many distinct values, evenly used, not steadily increasing, and present in most queries.

Bad keys: a timestamp gives one hot shard; a low-variety field gives chunks that cannot split; a field missing from queries means asking every shard.

Options: hashed keys spread writes but make range queries go to every shard; compound keys balance both.

Sample spoken answer:

"In a sharded cluster, apps talk to mongos routers, which use metadata on the config servers to send each query to the shards that own that range of the shard key. So the key decides both where data lives and how queries are routed. I want a key with lots of distinct values, used fairly evenly, that doesn't just keep increasing, and that appears in most of my queries. The classic mistakes: shard on createdAt or an ObjectId and every new insert lands on the last range, so one shard does all the writing. Shard on something like country and a big country becomes a chunk that can't be split. Pick a key the queries don't include and every read is broadcast to all shards. A hashed key fixes the hot shard but turns range queries into broadcasts. For orders, I'd often use customerId plus orderDate. Newer versions can reshard, but it's heavy, so I choose carefully up front."

Red flag to avoid:

Choosing _id or a timestamp without mentioning hot shards, or saying the shard key can be changed any time at no cost.

They may ask next:
  • Your product has one huge customer that makes most of the traffic. How does that affect a customerId shard key?
  • What is the difference between a targeted query and a scatter-gather query, and how do you spot one?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

25. Explain write concern and read preference. What settings would you pick for writing and reading a payment record?

What the interviewer is really testing:
Whether you can trade durability and freshness against speed on purpose, per operation, rather than accept defaults blindly.
Answer frame:

Write concern: how many members must confirm a write: 1, a number, or majority; plus journal and a timeout.

Read preference: which member serves reads: primary, primaryPreferred, secondary, secondaryPreferred, nearest.

Read concern: how durable the returned data must be, such as local or majority.

Payment: majority with journal, read from the primary; send analytics to secondaries.

Sample spoken answer:

"Write concern is how much confirmation I wait for before a write counts as done. With w set to one, only the primary confirms, which is fast but can be rolled back if the primary fails right after. With majority, most members have it, so it survives a failover. I can also ask for the journal to be flushed and set a timeout so a write doesn't hang forever. Read preference is about where reads go. Primary is the default and always current. Secondary or nearest spreads load but can return data that's a bit behind. There's also read concern, which says how durable the data I read must be, and majority means it won't be rolled back. For a payment record I'd write with majority and journal, and read from the primary with majority read concern, because being wrong about money is worse than a few extra milliseconds. Dashboards and reports can read from secondaries."

Red flag to avoid:

Mixing up write concern and read preference, or reading balances from secondaries without mentioning lag.

They may ask next:
  • What does a write timeout error tell you about whether the write happened?
  • What does majority write concern cost you on a cluster spread across regions?
Say it in 60 seconds

Transactions & Consistency 2 questions

Medium Technical round Mid-level, Senior Practice question

26. MongoDB supports multi-document transactions. How do you use one, and why do experienced people tell you not to lean on them?

What the interviewer is really testing:
Whether you can write a correct transaction and also know that good schema design makes most of them unnecessary.
Answer frame:

Basics: start a session, run operations with that session, commit or abort; changes apply all or nothing.

Helper: withTransaction retries on transient errors; throwing inside it aborts.

Costs: extra overhead, write conflicts that force retries, a time limit, slower across shards.

Design first: a single-document write is already atomic, so model data that changes together into one document.

Sample spoken answer:

"Transactions work on replica sets and sharded clusters, not on a standalone server. I start a session, run my operations passing that session, and commit, and either everything applies or nothing does. In the Node driver I use withTransaction, which retries the whole callback on transient errors, like a write conflict or a failover mid-way. If I throw inside it, the transaction aborts. A money transfer is the textbook case: take from one account only if the balance is enough, add to the other, and throw if the first update didn't match. The reason people say don't lean on them is cost. They hold resources, conflict with each other and must retry, have a time limit after which they're aborted, and are slower across shards. And a write to one document is already atomic. So I try to design so data that must change together sits in one document, and keep transactions for the real cross-document cases."

Code:
const session = client.startSession();
try {
  await session.withTransaction(async () => {
    const from = await accounts.updateOne(
      { _id: fromId, balance: { $gte: amount } },
      { $inc: { balance: -amount } }, { session });
    if (from.modifiedCount !== 1) throw new Error("Insufficient balance");
    await accounts.updateOne({ _id: toId }, { $inc: { balance: amount } }, { session });
  });
} finally {
  await session.endSession();
}
Red flag to avoid:

Saying MongoDB has no transactions, or wrapping every write in one by habit.

They may ask next:
  • What happens if you forget to pass the session to one of the operations?
  • How would you model this so it needs no transaction at all?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

27. Your app reads from secondaries to spread load, and users complain that their changes vanish right after they save. What do you do?

What the interviewer is really testing:
Whether you connect a user-facing bug to replication lag and pick a fix that keeps consistency where it matters.
Answer frame:

Diagnose: writes go to the primary, reads hit a secondary that has not applied them yet.

Quick fix: read your own writes from the primary on screens right after a save.

Stronger fix: causally consistent sessions with majority read and write concern.

Rethink: keep secondary reads for reports, and check the lag itself.

Sample spoken answer:

"That's almost certainly replication lag. The save goes to the primary, but the next page load reads from a secondary that hasn't applied it yet, so the user sees the old version. First I'd confirm by checking replication lag on the secondaries and matching complaint times to lag spikes. Then I'd fix the reads that matter. The simplest fix is to read from the primary on any screen a user sees right after changing something, like their profile or cart. If we need secondary reads there, MongoDB supports causally consistent sessions: with majority write and read concern, a read in the same session is guaranteed to see that session's earlier writes. I'd also question the setup. Secondary reads don't add write capacity, and during a failover the load all lands on fewer servers anyway. I'd keep them for reports and analytics, and look at indexes before adding read traffic to secondaries."

Red flag to avoid:

Blaming the frontend cache without checking replication, or switching every read to the primary without asking why secondaries were used.

They may ask next:
  • What could make one secondary fall much further behind than the others?
  • If load on the primary is the real problem, what would you try before secondary reads?
Say it in 60 seconds

Mongoose 1 questions

Easy Technical round Fresher, Mid-level Practice question

28. What does Mongoose give you on top of the plain MongoDB driver in Node, and when would you skip it?

What the interviewer is really testing:
Whether you know what the ODM actually does, including its gaps, rather than treating it as MongoDB itself.
Answer frame:

Adds: schemas and models, type casting, validation, defaults, middleware hooks, virtuals and populate.

Gaps: rules exist only in app code; update validators are off by default; populate is extra queries, not a join.

Speed: lean returns plain objects, much lighter for read-heavy code.

Skip it: for heavy aggregation work, very hot paths, or services that want a thin layer.

Sample spoken answer:

"Mongoose is an ODM on top of the Node driver. It lets me define a schema and a model, and then it casts types, so a string id becomes an ObjectId, applies defaults, runs validation, and gives me pre and post hooks, virtual fields and populate to fill in referenced documents. That structure helps a team a lot. But I keep its limits in mind. The schema only exists in my app, so another service or a shell script can still write anything. Validators don't run on update queries unless I turn on runValidators. Populate is just extra queries behind the scenes, not a server-side join. And unique in a schema builds a unique index, it isn't a validator. For read-heavy endpoints I add lean, which skips building full documents. I'd skip Mongoose for a small service that's mostly aggregations or needs every bit of speed."

Code:
const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true, lowercase: true, trim: true },
  name: String,
  createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model("User", userSchema);

const recent = await User.find({ name: /ann/i }).sort({ createdAt: -1 }).limit(20).lean();
Red flag to avoid:

Thinking the Mongoose schema is enforced by the database, or that populate is a real join.

They may ask next:
  • Why might populate be slow on a list of a hundred posts, and what would you do instead?
  • What is the difference between a Mongoose document and the result of lean?
Say it in 60 seconds

Real Work 3 questions

Hard Behavioral round Mid-level, Senior Practice question

29. Tell me about a slow MongoDB query you tracked down in production. How did you find it, and what did you change?

What the interviewer is really testing:
Whether you have done real performance work: found the query with data, read the plan, fixed it, and proved the fix.
Answer frame:

Signal: what showed the problem, such as slow API timings or the slow query log.

Diagnosis: the explain output and the numbers that gave it away.

Fix: the index or query change and how you rolled it out safely.

Proof and habit: the before and after, and what you changed in the team process.

Sample spoken answer:

"At my last company, an order history endpoint went from snappy to several seconds as our biggest customers grew. Our API timings showed it, and the database's slow query log pointed to one find on orders, filtered by customerId and status and sorted by createdAt. Explain showed it using an index on customerId alone, then a SORT stage in memory, examining tens of thousands of documents to return twenty. The fix was a compound index on customerId, status and createdAt, in that order, so equality first and sort next. I built it during a quiet hour and watched replication lag while it built. After that the endpoint came back in tens of milliseconds, and explain showed keys examined close to documents returned. The old single-field index was now a prefix of the new one, so I dropped it after checking it wasn't used elsewhere. We then added an explain check to code review for new queries."

Red flag to avoid:

A story where the fix was "we added indexes" with no numbers, no explain output, and no check that it worked.

They may ask next:
  • How did you make sure the index build did not hurt production while it ran?
  • What would you have done if the same query also needed a range filter on amount?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

30. Tell me about a MongoDB schema decision you later had to reverse. What went wrong, and how did you migrate?

What the interviewer is really testing:
Whether you learn from real modelling mistakes and can migrate live data without breaking the app.
Answer frame:

Decision: what you chose and why it looked right at the time.

Symptom: how it failed as data grew.

Migration: the new model and a safe rollout, including backfill and reads during the switch.

Lesson: the rule you now apply up front.

Sample spoken answer:

"In a side product at my last job, we embedded each user's activity feed as an array inside the user document. It was simple, and one read gave the profile page everything. A year later, our most active users had thousands of entries. Loading a profile pulled megabytes, every new event rewrote a big document, and a couple of accounts were creeping toward the document size limit. We moved activity into its own collection, one document per event with userId and createdAt, and a compound index on those. We kept the latest five embedded for the profile header. The rollout went in steps: new events written to both places, a backfill script moving old arrays in batches, reads switched to the new collection, and finally the big arrays removed. The lesson I carry now is to ask, for every array, what's the most items this could ever hold."

Red flag to avoid:

Blaming MongoDB for the problem, or describing a one-shot migration with the app offline and no way back.

They may ask next:
  • How did you make the backfill safe to stop and restart part-way?
  • How did you check the old and new data matched before switching reads?
Say it in 60 seconds
Medium Behavioral round Mid-level, Senior Practice question

31. Tell me about a time a primary failed over or stepped down while your app was live. What broke, and what did you change?

What the interviewer is really testing:
Whether you have run MongoDB in production and made the application resilient to normal replica set events.
Answer frame:

Event: what happened on the database side.

Impact: what users or services saw, and how you knew.

Fixes: connection string, retryable writes, idempotent operations, timeouts.

Practice: how you tested failover on purpose afterwards.

Sample spoken answer:

"At my last company we had a routine maintenance stepdown on a three-member replica set, and for about fifteen seconds our checkout API threw errors. When we dug in, one old service had a connection string with only the primary's host instead of the replica set seed list, so it didn't find the new primary for a while. Another was on an old driver without retryable writes, so every write in that window failed instead of retrying once. We fixed the connection strings, upgraded the driver, and made sure our order writes were idempotent with a client-generated order id and a unique index, so a retry couldn't create a double order. We also set sensible server selection timeouts. Then we started stepping down the primary on purpose in staging every release. The next real failover was a blip nobody outside the team noticed."

Red flag to avoid:

Treating failover as a rare disaster rather than a normal event the app must handle, or having no follow-up changes.

They may ask next:
  • Why does a retried write need to be idempotent, even with retryable writes turned on?
  • How would you test a failover without risking production?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

The questions above are the prep. The call has ten more.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.

Download ClapAssist with 10 free minutes
Mac and Windows · Stays out of screen share · No card