70 Progressive Questions (Easy to Advanced + Scenarios)
70 comprehensive interview questions spanning database basics, Mongoose ODM abstractions, advanced aggregation pipelines, system architecture, performance tuning, and real-world scenario challenges.
Q51 [Scenario]: High concurrent orders cause item stock counts to drop below zero. How do you prevent inventory race conditions?
Avoid checking inventory client-side (if (item.stock > 0) item.save()). Instead, use an atomic update with a query filter condition: Item.updateOne({ _id: itemId, stock: { $gte: quantity } }, { $inc: { stock: -quantity } }). If modifiedCount === 0, inventory was insufficient, preventing negative stock levels atomically.
const result = await Item.updateOne(
{ _id: itemId, stock: { $gte: orderQty } }, // Condition filter
{$inc: { stock: -orderQty } } // Atomic reduction
);
if (result.modifiedCount === 0) {
throw new Error('Insufficient inventory or item out of stock');
}- Combine range filters directly inside atomic update queries.
- Eliminates client-side check-then-set race conditions.
Why does client-side stock checking (if stock > 0) fail under high concurrent write loads?
Q52 [Scenario]: An API endpoint fetching user activity logs takes 8 seconds. Walk through your step-by-step debugging workflow.
- Enable Database Profiler:
db.setProfilingLevel(1, { slowms: 100 })to log slow operations tosystem.profile. - Extract Explain Plan: Run
.explain('executionStats')on the offending query. - Inspect Stages: Check if
winningPlanshowsCOLLSCAN. Look attotalDocsExaminedvsnReturned. - Apply Indexing: Construct a compound index adhering to the ESR rule (
Equality, Sort, Range). - Verify: Re-run explain to verify
IXSCANstage execution.
- Identify slow queries via system profiler or MongoDB Atlas Performance Advisor.
- Apply ESR rule indexing to convert
COLLSCANintoIXSCAN.
Which system collection stores slow operation entries when database profiling is enabled?
Q54 [Scenario]: A user registration endpoint throws MongoServerError E11000 duplicate key error. How should your application handle this?
Catch the E11000 error code inside Express error-handling middleware. Parse the error.keyPattern or error.keyValue object to identify which field caused the collision (e.g., email), and return a user-friendly HTTP 409 Conflict status response ({ message: 'Email address already registered' }).
E11000is the MongoDB duplicate key error code.- Parse
err.keyValueto return targeted validation messages.
What standard MongoDB error code signifies a unique index duplicate key constraint violation?
Q55 [Scenario]: How do you implement global Soft Deletes in Mongoose so deleted documents are preserved for compliance?
Add an isDeleted: { type: Boolean, default: false } and deletedAt: Date field to the schema. Implement a Mongoose Query Pre-Middleware hook targeting find, findOne, countDocuments, and update regex patterns (/^find/) to append this.where({ isDeleted: false }) automatically.
schema.pre(/^find/, function(next) {
// Automatically filter out soft-deleted documents from all find queries
this.where({ isDeleted: { $ne: true } });
next();
});- Use query middleware regex (
/^find/) to append soft-delete filters globally. - Ensure compound indexes include
{ isDeleted: 1 }.
Which Mongoose middleware pattern allows applying soft-delete filters across find, findOne, and findById queries simultaneously?
Q56 [Scenario]: You need to rename a field across 50 million documents in production without downtime. How do you execute this?
Do NOT execute a blocking $rename update on 50M documents at once (locks memory and replication).
- Update application code to handle both old and new field names via Mongoose virtuals or getters.
- Write new data using the new field name.
- Run a background script processing documents in small batched cursor chunks using
$setand$unsetduring off-peak hours.
- Avoid single monolithic updates on massive production collections.
- Execute chunked background updates using cursor pagination.
Why is running db.collection.updateMany({}, { $rename: ... }) dangerous on a 50M document production table?
Q57 [Scenario]: An analytics dashboard calculates order metrics continuously, degrading database performance. How do you optimize this?
Apply the Pre-aggregation Pattern. Instead of running heavy $group pipeline calculations on raw transaction logs per dashboard load, maintain a separate DailyMetrics collection. Update summary counters incrementally in real time using atomic $inc updates during checkout events.
- Use the Pre-aggregation Pattern to store pre-calculated metrics.
- Update summary metrics incrementally using atomic
$incoperations.
Which design pattern replaces expensive on-the-fly aggregation queries with incremental pre-calculated metric documents?
Q58 [Scenario]: Audit logs must be retained for exactly 90 days and deleted automatically. How do you implement this efficiently?
Create a TTL index on the createdAt timestamp field with expireAfterSeconds: 7776000 (90 days). MongoDB's background thread will purge expired documents automatically without requiring cron jobs or application intervention.
- TTL indexes eliminate external cleanup cron scripts.
- Calculated in seconds ($90 ext{ days} imes 24 imes 3600 = 7,776,000 ext{ seconds}$).
How many seconds must be configured in expireAfterSeconds for a 90-day TTL index?
Q59 [Scenario]: Design a multi-tenant SaaS architecture in MongoDB. Should you use single collections, separate databases, or separate collections?
- Shared Collection (Discriminator/Tenant ID): Best for scale and operational simplicity. Filter all queries by
tenantId(addtenantIdas leading key in compound indexes). - Separate Databases per Tenant: Best for strict compliance, data isolation, and easy backup/restore per customer.
- Separate Collections: Anti-pattern if tenant counts reach thousands (exceeds namespace limits).
- Shared Collection +
tenantIdindex prefix = Maximum scalability. - Separate DBs = Maximum isolation compliance.
In a shared collection multi-tenant architecture, where should the tenantId field reside in compound indexes?
Q60 [Scenario]: Fetching 500,000 documents via Model.find() causes Node.js Out-Of-Memory (OOM) crashes. How do you resolve this?
Model.find() loads all 500,000 documents into application RAM simultaneously as array objects. Resolve this by streaming documents using Mongoose Cursor Stream (Model.find().cursor()) or batching with .eachAsync() or cursor .next(), processing documents sequentially with low memory overhead.
const cursor = User.find({ status: 'active' }).cursor();
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
await processDocument(doc); // Process one document at a time in memory
}- Never load massive datasets into application RAM arrays.
- Use streams (
.cursor()) to process documents in small memory chunks.
Which method returns a Node.js stream for processing large query datasets sequentially without flooding application RAM?