MongoDB Roadmap

MongoDB is a document database. It stores records as flexible, JSON-like documents, so data that is read together can be stored together. It is widely used for product catalogues, content systems, event data and mobile and web back ends, and it appears in many back-end and full-stack job descriptions.

This roadmap takes you from your first document to what interviews test: the data model and types, CRUD and query operators, aggregation pipelines, schema design, indexes and explain plans, transactions, replica sets, sharding and security. Each topic has a short explanation, an example where it helps and an exercise. Core topics are expected of everyone, recommended topics are common in jobs and optional ones can wait.

By the end you will be able to design a collection around real queries, write and tune them and explain how MongoDB keeps data available and safe. If you are new to databases, read the SQL Roadmap roadmap alongside this one, since the comparison is a favourite interview theme.

Level
Beginner to advanced
Time
8–12 weeks at about 8 hours a week
Scope
46 topics in 8 stages
Before you start
Basic programming in a language with a MongoDB driver, such as JavaScript Roadmap or Python Roadmap; Familiarity with JSON objects and arrays; Helpful: the ideas of tables, keys and joins from SQL Roadmap; Comfort with the terminal

What you will be able to do

  • Explain the document model, BSON types and when MongoDB is or is not a good fit.
  • Insert, query, update and delete documents with the correct operators.
  • Write aggregation pipelines that group, join and reshape data.
  • Design schemas around access patterns, choosing between embedding and referencing.
  • Create indexes and use explain to confirm that queries use them.
  • Describe transactions, replica sets, write concerns and sharding and when each is needed.
  • Apply authentication, role-based access, encryption and backups to a deployment.

The roadmap at a glance

  1. Stage 1MongoDB BasicsBeginner · 1 week · 6 topics
  2. Stage 2Data Model and BSON TypesBeginner · 1 week · 6 topics
  3. Stage 3CRUD and Query OperatorsBeginner · 2 weeks · 9 topics
  4. Stage 4AggregationIntermediate · 2 weeks · 5 topics
  5. Stage 5Schema Design and ValidationIntermediate · 1–2 weeks · 4 topics
  6. Stage 6Indexing and PerformanceIntermediate · 2 weeks · 5 topics
  7. Stage 7Transactions, Replication and ScalingAdvanced · 2 weeks · 6 topics
  8. Stage 8Security and OperationsAdvanced · 1–2 weeks · 5 topics

Stage 1 MongoDB Basics

Beginner1 week6 topics

Goal. Explain what MongoDB is, when it fits and run your first commands in the shell and Atlas.

What is MongoDB? and MongoDB Basics Core

MongoDB is a document database. Instead of rows in tables, it stores documents: JSON-like records with named fields that may hold nested objects and arrays. Documents live in collections, and collections live in databases. A collection does not force every document to have the same fields, although you can add rules when you want them.

Because a document can hold everything about an entity together, many reads need one lookup instead of several joins. MongoDB is used for catalogues, content, event data, user profiles and mobile back ends. It has its own query language, a rich aggregation framework, secondary indexes, transactions, replication and sharding.

Practice. Write a single JSON document for a blog post that includes its author and three comments, and note what a relational model would need.

MongoDB Terminology Core

The vocabulary maps to relational ideas. A database holds collections. A collection is like a table; a document is like a row; a field is like a column but belongs to the document, not the collection. Every document has an _id field that is unique in its collection, and MongoDB adds one if you omit it. An index speeds up queries on fields. A cursor is the object that returns query results in batches. A replica set is a group of servers holding the same data and a sharded cluster splits data across groups of servers.

There are no foreign keys. Relationships are expressed by embedding a document or by storing the _id of another document and looking it up.

Practice. Draw a table that maps ten relational terms to their MongoDB equivalents.

NoSQL vs SQL and When to Use MongoDB? Core

NoSQL vs SQL is about trade-offs, not about one being modern. Relational databases, covered in SQL Roadmap, give strong schemas, joins and decades of tooling. MongoDB gives flexible documents, easy horizontal scaling and a natural fit with objects in code.

When to use MongoDB? Good fits: data whose shape varies between records (product attributes), data that is read and written as one unit (an order with its lines), high write volumes, and fast-changing early products. Poor fits: data with many relationships queried in many directions, heavy multi-table reporting, and cases where every rule must be enforced by relations. MongoDB supports multi-document transactions, so strict correctness is possible, but it is not the first design tool to reach for in such systems.

Practice. Pick three systems you know and decide for each whether you would choose MongoDB or a relational database, with a reason.

What is MongoDB Atlas? Core

MongoDB Atlas is the managed cloud service for MongoDB, running on the major cloud providers. It sets up replica sets, backups, monitoring, upgrades, access control and scaling for you, and has a free tier that is suitable for learning. Beyond hosting it offers Atlas Search, vector search, charts, triggers and data federation.

To start, create a cluster, add a database user, allow your IP address in the network access list and copy the connection string, which starts with mongodb+srv://. Keep the credentials out of source code by reading them from environment variables. You can also run MongoDB yourself with the Community Server or in a container, which is useful for local development.

Practice. Create a free Atlas cluster, add a user and connect to it from the shell.

Developer Tools and Language Drivers Core

Developer Tools include mongosh, the modern shell, MongoDB Compass, a graphical tool for browsing data, building aggregation pipelines and viewing explain plans, the Atlas UI and the command-line database tools. Editor extensions let you run queries from VS Code.

Language Drivers connect your code to the database. Official drivers exist for JavaScript and Node.js, Python (PyMongo), Java, C#, Go, Rust, PHP, Ruby and others. Create one client at start-up and reuse it; the driver keeps a connection pool. Libraries such as Mongoose for Node.js add schemas and models on top of the driver.

const client = new MongoClient(process.env.MONGODB_URI);
const users = client.db('shop').collection('users');
const user = await users.findOne({ email: 'a@example.com' });

Practice. Connect from your preferred language, insert one document and read it back.

JavaScript in the Shell and in Queries Recommended

mongosh is a JavaScript environment, so you can use variables, loops and functions while working with data. This is handy for quick scripts, such as updating many documents with a computed value.

const cursor = db.orders.find({ status: 'new' });
cursor.forEach(o => print(o._id, o.total));

Older versions allowed server-side JavaScript through $where and mapReduce. They are slow, run outside indexes and are a security risk, so prefer normal operators and the aggregation pipeline. Atlas also supports triggers and functions written in JavaScript for scheduled or event-based tasks.

Practice. Write a shell script that prints the total of all orders with a JavaScript loop, then replace it with an aggregation.

Stage 2 Data Model and BSON Types

Beginner1 week6 topics

Goal. Describe how MongoDB stores values and choose the right type for each field.

Data Model & Data Types, BSON vs JSON Core

BSON vs JSON: MongoDB stores documents in BSON, a binary format that extends JSON. It supports more types, such as dates, binary data, 64-bit integers and exact decimals, and it is fast to scan. You write and read documents in a JSON-like syntax, and drivers convert them to native types. Documents are limited to 16 MB.

The Data Model & Data Types you choose matter for correctness and speed: a date stored as a string cannot be compared as a date, and a number stored as text sorts alphabetically. Queries are type-sensitive, so { age: 30 } does not match the string "30". Decide types at design time and enforce them with schema validation.

Practice. Insert a document with a date stored as a string and one as a real date, then sort and compare both.

ObjectId, String, Boolean and Null Core

ObjectId is a 12-byte identifier that MongoDB generates for _id. It contains a timestamp, a random value and a counter, so values are roughly in creation order, and you can read the creation time from it. String is UTF-8 text. Boolean is true or false. Null is an explicit missing value.

The query { field: null } matches documents where the field is null or does not exist; use $exists or $type to tell them apart. Strings compare in binary order unless you set a collation, which controls case and language rules. Keep ObjectId values as ObjectId and not as strings when you query, or the match fails.

Practice. Create a document, read its creation time from the ObjectId and query it by id using a string and then an ObjectId.

Numbers: Int32, Int64 / Long, Double and Decimal128 Core

MongoDB has four numeric types. Int32 is a 32-bit whole number. Int64 / Long is a 64-bit whole number. Double is a 64-bit binary floating point number, the default for numbers typed in the shell. Decimal128 is a 128-bit decimal with exact base-10 arithmetic.

db.prices.insertOne({ item: 'pen', price: Decimal128('19.99'), qty: Int32(3) })

Use Decimal128 for money, because binary doubles cannot represent values like 0.1 exactly. Mixing types in one field is allowed but causes surprises in sorting and aggregation, so keep a field to one numeric type. Watch out for JavaScript, which has only doubles, and loses precision on integers above 2 to the power 53.

Practice. Store a price as Double and as Decimal128, add 0.1 and 0.2 to each and compare the results.

Date and Timestamp Core

Date holds a UTC moment as a count of milliseconds since the epoch, and it is what you want for created and updated times, expiry and ranges. Timestamp is a special internal type used by replication, made of seconds and a counter, and is rarely used in application data.

db.events.insertOne({ type: 'login', at: new Date() })
db.events.find({ at: { $gte: new Date('2025-01-01'), $lt: new Date('2025-02-01') } })

Store times in UTC and convert when displaying them. Use range queries with $gte and $lt, and index the date field. The aggregation operators $dateToString, $dateTrunc and $year group by time period.

Practice. Insert events with dates and write a query for all events in one calendar month.

Object, Array and Embedded Objects & Arrays Core

Object is a nested document, and Array is an ordered list of values, including other documents. Embedded Objects & Arrays are the heart of the document model.

{
  _id: 1,
  name: 'Asha',
  address: { city: 'Pune', postcode: '411001' },
  tags: ['admin', 'beta'],
  orders: [{ sku: 'A1', qty: 2 }, { sku: 'B7', qty: 1 }]
}

Reach into objects with dot notation, 'address.city', and into arrays by position, 'orders.0.sku', or by matching any element. Arrays can be indexed per element (a multikey index). Keep arrays bounded: an array that grows without limit makes the document large and slow to update, and can hit the 16 MB limit.

Practice. Query the example for users in Pune who have at least one order of SKU A1.

Binary Data, Regular Expression, Symbol, Undefined, Min Key and Max Key Optional

Some types are rare. Binary Data stores bytes, such as small files or UUIDs; larger files belong in GridFS or object storage. Regular Expression values can be stored and are also used in queries. Symbol is a deprecated type that older drivers produced for string-like values. Undefined is a deprecated type; do not write it. Min Key and Max Key compare lower and higher than every other value, and are used internally for sharding ranges.

Knowing them helps you read data from older systems and interpret $type output. For new designs use String, Binary Data and Date, and avoid the deprecated ones.

Practice. Run $type on a collection with mixed values and list the type names you see.

Stage 3 CRUD and Query Operators

Beginner2 weeks9 topics

Goal. Create, read, update and delete documents with the right methods and query operators.

Collection Methods and insert() and Related Methods Core

Collection Methods are the functions you call on a collection: insertOne, find, updateOne, deleteOne, aggregate, createIndex, countDocuments and more. The insert() and Related Methods are insertOne and insertMany; the old insert is deprecated.

db.users.insertOne({ name: 'Asha', email: 'asha@example.com' })
db.users.insertMany([{ name: 'Bruno' }, { name: 'Chen' }], { ordered: false })

If _id is missing, the server adds an ObjectId, and the result returns the id. By default insertMany is ordered and stops at the first error; with ordered: false it continues and reports all errors. A unique index violation raises a duplicate key error, which you handle in code.

Practice. Insert 100 generated documents with insertMany and handle a duplicate key error.

Comparison Operators: $eq, $ne, $gt, $gte, $lt, $lte, $in and $nin Core

Query Operators begin with comparison. $eq equals, $ne not equal, $gt greater than, $gte greater or equal, $lt less than, $lte less or equal, $in matches any value in a list and $nin matches none of them.

db.orders.find({
  total: { $gte: 50, $lt: 200 },
  status: { $in: ['paid', 'shipped'] },
  region: { $ne: 'EU' }
})

{ field: value } is shorthand for $eq. Comparisons only match the same type family, so numbers compare with numbers. $ne and $nin also match documents where the field is missing, which surprises people. Several conditions on one field go inside one object, as in total above.

Practice. Write queries for orders between 50 and 200, orders with status not in a list and orders with no status field.

Logical Operators: $and, $or, $nor and $not Core

Logical operators combine conditions. Several fields in one filter are joined with an implicit $and. $or matches if any condition is true, $nor matches if none is, and $not inverts the condition of a single operator.

db.users.find({
  $or: [{ role: 'admin' }, { score: { $gt: 90 } }],
  age: { $not: { $lt: 18 } }
})

Use an explicit $and when you need the same operator twice at the same level, because duplicate keys in an object overwrite each other. For $or, each branch can use its own index, so index every branch. $not also matches documents where the field is missing.

Practice. Find users who are admins or have a high score and are at least 18, then add a $nor to exclude two statuses.

Element and Evaluation Operators: $exists, $type, $regex and $text Core

Element operators look at the field itself: $exists tests presence and $type tests the BSON type. Evaluation operators run a computation: $regex matches a pattern and $text runs a full-text search against a text index.

db.people.find({ phone: { $exists: true, $type: 'string' } })
db.people.find({ name: { $regex: '^as', $options: 'i' } })
db.articles.createIndex({ title: 'text', body: 'text' })
db.articles.find({ $text: { $search: 'database indexing' } })

A regular expression anchored at the start (^as) can use an index; unanchored or case-insensitive ones usually scan. For serious search, use Atlas Search instead of $text. Only one text index is allowed per collection.

Practice. Find documents where a field is a string, then match names starting with a given prefix with and without an index and compare the plans.

Array Operators: $all, $elemMatch, $size and $slice Core

Array operators query inside arrays. $all requires every listed value, $elemMatch requires a single element that satisfies all conditions, $size matches an array length and $slice limits array elements in a projection.

db.posts.find({ tags: { $all: ['db', 'tutorial'] } })
db.carts.find({ items: { $elemMatch: { sku: 'A1', qty: { $gt: 1 } } } })
db.carts.find({ items: { $size: 3 } }, { items: { $slice: 2 } })

The difference between $elemMatch and separate conditions is key. Without it, items.sku: 'A1' and items.qty > 1 can be satisfied by different elements. $size accepts only exact numbers, not ranges, so store a count field if you need to query by length.

Practice. Show with two sample documents why separate conditions on array fields can return a wrong match and fix it with $elemMatch.

Projection: $include, $exclude and the $project Idea Core

Projection chooses which fields come back. In find, { name: 1 } includes a field and { internal: 0 } excludes it; you cannot mix inclusion and exclusion except for _id. Think of $include and $exclude as the two modes. In the aggregation pipeline the $project stage does the same and can also compute new fields.

db.users.find({}, { name: 1, email: 1, _id: 0 })
db.users.aggregate([{ $project: { name: 1, domain: { $arrayElemAt: [{ $split: ['$email', '@'] }, 1] } } }])

Returning fewer fields reduces network and memory use, and with an index that contains them, MongoDB can answer without reading documents (a covered query).

Practice. Return only name and email for users and compute a domain field in an aggregation.

Stage 4 Aggregation

Intermediate2 weeks5 topics

Goal. Build aggregation pipelines that filter, group, join and reshape data.

Aggregation and Pipelines, Stages and Operators Core

Aggregation processes documents through a pipeline. Pipelines, Stages and Operators fit together like this: a pipeline is an array of stages, each stage takes the documents from the previous one and passes new ones on, and operators such as $sum or $concat calculate values inside stages.

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

Think in steps. Reduce data early with $match and $project, then reshape. The server can use indexes for a leading $match and $sort. Each stage in memory is limited to 100 MB unless you allow disk use, which current versions do by default. Compass has a visual builder that shows each stage's output.

Practice. Build a pipeline in Compass that shows the five customers with the highest paid total.

$match, $sort, $limit and $skip Core

$match filters documents with the same syntax as find, so put it first to use an index and shrink the data. $sort orders documents; a sort on an indexed field at the start is cheap, while a sort late in the pipeline has to hold data in memory. $limit keeps the first n documents, and $skip drops the first n.

[{ $match: { year: 2025 } }, { $sort: { rating: -1 } }, { $skip: 20 }, { $limit: 10 }]

Order matters: $sort followed by $limit is optimised into a top-k sort. $skip before $limit makes pages. Add a tie-breaker such as _id to the sort so paging is stable.

Practice. Return page three of the top-rated movies for a year with a stable sort order.

$group and $sum Core

$group collects documents by an _id expression and calculates accumulators for each group. Common accumulators are $sum, $avg, $min, $max, $first, $push and $addToSet. Use _id: null to aggregate everything.

{ $group: {
    _id: { month: { $month: '$createdAt' }, region: '$region' },
    revenue: { $sum: '$total' },
    orders: { $sum: 1 }
} }

$sum: 1 counts documents, and $sum: '$field' adds a field's values. $group has no guaranteed output order, so follow it with $sort. A group with $push can grow beyond the 16 MB document limit if there are many items, so limit it.

Practice. Compute revenue and order count per region per month.

$lookup and $unwind Core

$lookup performs a left outer join with another collection. $unwind turns an array field into one document per element, which is often needed after a lookup or before grouping by array items.

db.orders.aggregate([
  { $lookup: { from: 'customers', localField: 'customerId',
               foreignField: '_id', as: 'customer' } },
  { $unwind: '$customer' },
  { $project: { total: 1, 'customer.name': 1 } }
])

Index the foreign field, or each lookup scans the other collection. If you use $lookup on almost every read, consider embedding the data instead, since the document model is designed to avoid joins. $unwind removes documents with empty or missing arrays unless you set preserveNullAndEmptyArrays.

Practice. Join orders to customers with $lookup and count orders per country after an $unwind.

More Stages: $addFields, $facet, $bucket and $out Recommended

Beyond the basics are stages you will meet in real code. $addFields and $set add computed fields. $facet runs several sub-pipelines on the same input, such as results plus counts by category for a search page. $bucket groups values into ranges. $count returns a count. $merge and $out write results to a collection, which is how you build summary collections. $unionWith combines two collections.

Remember that stages run in order and a mistake early on changes everything after. Test a pipeline stage by stage, and use explain to confirm index use.

Practice. Build a search results pipeline that returns ten items and counts per category in one call with $facet.

Stage 5 Schema Design and Validation

Intermediate1–2 weeks4 topics

Goal. Model data around access patterns, choosing between embedding and referencing.

Embedding or Referencing: Modelling Relationships Core

The main design question is whether related data lives inside the parent document (embedding) or in its own collection linked by _id (referencing). Embed when data is read together, belongs to one parent and is bounded in size, such as the address in a user or the lines in an order. Reference when data is shared by many parents, grows without limit, or is updated on its own, such as the comments on a popular post or authors shared by many books.

Design from the questions your application asks, not from the entities. Duplicating a small, stable field, such as a product name in an order line, is normal and speeds reads. Ask what must change together and what is read together.

Practice. Model a blog with users, posts and comments in two ways and list which queries each one makes easy.

Schema Design Patterns Recommended

Several patterns recur. Bucket groups time-series points into one document per hour or day. Subset keeps the most used part of a large array, such as the latest ten reviews, in the main document and the rest elsewhere. Computed stores a total or count that is expensive to calculate, updated as data changes. Extended reference copies a few fields from another document. Schema versioning stores a schemaVersion field so old and new shapes can be read during a migration.

Avoid unbounded arrays, very large documents and many tiny collections for the same entity. Test the design with your real queries and realistic data volume before committing to it.

Practice. Apply the subset pattern to a product with thousands of reviews and show the queries for the product page.

Schema Validation Recommended

Collections are flexible by default, but validation lets you add rules with a JSON Schema.

db.createCollection('users', { validator: { $jsonSchema: {
  bsonType: 'object',
  required: ['email', 'createdAt'],
  properties: {
    email: { bsonType: 'string', pattern: '^.+@.+$' },
    age: { bsonType: 'int', minimum: 0 }
  }
} }, validationAction: 'error' })

validationLevel decides whether existing documents are checked, and validationAction can be error or warn. Rules catch bad writes early and document the expected shape. Use them together with application-level validation, and roll out stricter rules in warn mode first on existing data.

Practice. Add a validator to a collection, try to insert an invalid document and read the error.

Using validate() and Data Integrity Checks Optional

The validate() command checks a collection's data and indexes for structural problems and returns statistics. It is a maintenance tool, used when you suspect corruption after a crash or a storage fault, and it can be slow on large collections.

db.runCommand({ validate: 'orders', full: true })

Do not confuse it with schema validation, which is about the shape of your data. For routine integrity, use schema rules, unique indexes and, in a replica set, regular restore tests of backups. If validate reports errors, restore from a replica or backup and ask your support channel before trying repairs.

Practice. Run validate on a small collection and read the output fields.

Stage 6 Indexing and Performance

Intermediate2 weeks5 topics

Goal. Choose and verify indexes, read explain plans and tune queries.

Indexing and Single Field Indexes Core

Indexing is how MongoDB avoids scanning every document. Without an index a query reads the whole collection, a collection scan. Indexes are B-tree structures on one or more fields; the _id field is always indexed. A Single Field Index sorts documents by one field and supports equality, range and sort on it.

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

The 1 means ascending and -1 descending, which matters little for single fields. Indexes speed reads and cost writes and memory, because each insert or update must update them. Index the fields your frequent queries filter and sort on. A unique index also enforces a rule.

Practice. Query a collection of 100,000 documents by email before and after creating an index and compare times.

Creating Indexes and Compound Indexes Core

Creating Indexes uses createIndex, and on a live system builds run in the background of normal traffic, though they use resources. Compound Indexes cover several fields in a defined order and serve queries that use a prefix of those fields.

db.orders.createIndex({ customerId: 1, createdAt: -1 })

Follow the equality, sort, range rule: put fields used with equality first, then fields you sort on, then range fields. This index serves filters on customerId, and on customerId plus a date, and sorting by date for one customer, but not a filter on createdAt alone. A multikey index on an array field creates entries per element, and a compound index can hold at most one array field.

Practice. Design the index for the query: orders of one customer, newest first, in a date range, and verify with explain.

Specialised Indexes: TTL, Geospatial and Atlas Search Recommended

Expiring Data with TTL Indexes removes documents automatically some seconds after a date field, which suits sessions and logs.

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

The cleanup runs about every minute, so deletion is not instant. Geospatial Indexes, 2dsphere, support queries on locations, with $near, $geoWithin and $geoIntersects, using GeoJSON points. Atlas Search Indexes are built on Apache Lucene and give relevance-ranked full-text search, fuzzy matching, autocomplete and facets, queried with the $search stage. They run only on Atlas, and replace the need to copy data into a separate search system such as Elasticsearch.

Practice. Create a TTL index for sessions and a 2dsphere index and find places within one kilometre of a point.

Query Optimization with explain() Core

Query Optimization starts with explain('executionStats').

db.orders.find({ customerId: 7 }).sort({ createdAt: -1 }).explain('executionStats')

Read the winning plan: IXSCAN means an index was used, COLLSCAN means a full scan, and SORT in memory means the index did not give the order. Compare totalDocsExamined and totalKeysExamined with nReturned: a big gap means the index is not selective enough. Fix with a better compound index, a narrower projection, a covered query, $match earlier in pipelines or by removing leading-wildcard regexes. The profiler and Atlas Performance Advisor point to slow queries.

Practice. Find a COLLSCAN in a sample app, add an index and show the improved executionStats.

Performance Optimization and Tuning Configuration Recommended

Performance Optimization is also about the working set: the indexes and hot documents should fit in RAM, because disk reads are slow. Keep documents small, avoid unbounded arrays, batch writes with bulkWrite, use projections and choose write concerns that match the need.

Tuning Configuration covers server settings: the WiredTiger cache size (by default about half of memory), connection pool size in the driver, read preference for secondary reads, journal settings and operating system limits such as file descriptors. Monitor with mongostat, mongotop and Atlas metrics, watching cache usage, queue lengths, replication lag and slow operations. Change one setting at a time.

Practice. List the first five metrics you would check when an application reports slow database responses.

Stage 7 Transactions, Replication and Scaling

Advanced2 weeks6 topics

Goal. Keep data consistent and available and explain how MongoDB scales.

Change Streams Recommended

Change streams let an application watch a collection, database or cluster and receive an event for each insert, update, replace or delete. They are built on the oplog, so they need a replica set, and they give each event a resume token, so a consumer can restart from where it stopped.

const stream = db.collection('orders').watch([{ $match: { operationType: 'insert' } }]);
for await (const change of stream) console.log(change.fullDocument._id);

Use them to update caches, send notifications or feed other systems without polling. Filter events with a pipeline, store the resume token after processing and make handlers idempotent, as an event can be delivered again after a restart.

Practice. Print every new order from a change stream and restart the consumer using a saved resume token.

Connection Strings and Driver Settings Core

A connection string tells the driver where to connect and how. mongodb+srv://user:pass@cluster0.example.net/shop?retryWrites=true&w=majority uses DNS to find the hosts of a replica set. Options set the write concern, read preference, TLS, application name and pool sizes.

Create one MongoClient for the process and reuse it; creating one per request wastes connections. Set timeouts such as serverSelectionTimeoutMS, URL-encode special characters in passwords and keep the string in an environment variable. Handle network errors in code, because a driver can fail over but your request may still need a retry.

Practice. Connect with an invalid password, an unreachable host and a valid string, and note the error each gives.

Transactions Core

A write to one document is atomic, even if it changes several nested fields, and that covers most needs. Transactions extend atomicity to several documents, collections or, on sharded clusters, shards.

const session = client.startSession();
await session.withTransaction(async () => {
  await accounts.updateOne({ _id: 1 }, { $inc: { bal: -100 } }, { session });
  await accounts.updateOne({ _id: 2 }, { $inc: { bal: 100 } }, { session });
});

They cost performance and have a time limit (60 seconds by default), so keep them short. withTransaction retries on transient errors. Good schema design reduces how often you need them, because related data in one document updates atomically.

Practice. Transfer a balance between two accounts in a transaction and abort in the middle to see rollback.

Replica Sets and Retryable Reads / Writes Core

Replica Sets are groups of servers that hold copies of the data. One primary accepts writes, secondaries copy changes from its oplog, and an election picks a new primary if it fails, usually within seconds. A set needs an odd number of voting members, and at least three nodes for fault tolerance.

Retryable Reads / Writes make drivers retry a read or a write once after a transient failure, such as a primary election, with exactly-once semantics for writes through transaction numbers. They are on by default in current drivers, so brief failovers are mostly hidden from applications. Connect with the full list of hosts or the SRV string so the driver finds the new primary.

Practice. Run a three-node local replica set, stop the primary and watch an election and a retried write.

Read & Write Concerns Core

Read & Write Concerns control durability and consistency. Write concern says how many members must acknowledge a write: w: 1 means the primary, w: 'majority' means most members, and j: true waits for the journal. Majority writes survive a failover. Read concern says what data a read may see: local is the node's latest data, majority returns data acknowledged by most members and linearizable gives the strongest guarantee, at a cost.

db.orders.insertOne({ total: 40 }, { writeConcern: { w: 'majority' } })

Read preference decides where reads go, such as primary or secondaryPreferred. Reading from secondaries can return stale data. Defaults in current versions are strong for most apps; lower them only for data you can afford to lose.

Practice. Explain which write and read concerns you would use for payments and for page-view counts.

Sharded Clusters and Scaling MongoDB Recommended

Scaling MongoDB starts vertically with a bigger machine, more RAM and faster disks, and with read replicas. When one replica set cannot hold the data or the write rate, Sharded Clusters split a collection across several replica sets, called shards. A mongos router sends operations to the right shards, and config servers hold the metadata.

sh.shardCollection('shop.events', { userId: 'hashed' })

The shard key is the key decision. A good key has many distinct values, spreads writes evenly and appears in common queries, so they target one shard. A monotonically increasing key such as a timestamp sends all new writes to one shard. Hashed keys spread writes; ranged keys support range queries. Shard late, and only when needed.

Practice. Compare a hashed userId key and a ranged date key for an events collection and name the hot spot of each.

Stage 8 Security and Operations

Advanced1–2 weeks5 topics

Goal. Secure a deployment, back it up and connect it to other data systems.

MongoDB Security and Role-based Access Control Core

MongoDB Security begins with turning on authentication; older default installs had none, which led to many exposed databases. Then restrict the network, use TLS, encrypt data and audit access. Role-based Access Control grants privileges to roles and roles to users.

db.createUser({ user: 'reporter', pwd: passwordPrompt(),
  roles: [{ role: 'read', db: 'shop' }] })

Built-in roles include read, readWrite, dbAdmin and clusterAdmin, and you can define custom roles for exact actions on exact collections. Give each application its own user with the minimum role, never use the admin user in code and keep credentials in a secrets store.

Practice. Create a read-only user for one database and confirm that a write is rejected.

Authentication Methods: Kerberos, LDAP Proxy Auth and x.509 Certificate Authentication Optional

Beyond the default SCRAM username and password, enterprise setups use other mechanisms. Kerberos Authentication ties logins to a company-wide ticket system, mostly in Enterprise Server. LDAP Proxy Auth forwards credentials to a directory service so users and groups come from the company directory. x.509 Certificate Authentication uses client certificates, which suits services and cluster members that need to prove identity without passwords. Atlas also supports OIDC and AWS IAM based sign-in.

Choose by what your organisation already runs. Whatever the method, authorisation still comes from roles, so map directory groups to roles.

Practice. Write down which authentication method fits a small start-up, a bank and a service running on a cloud platform.

Encryption: TLS / SSL, Encryption at Rest, Field Level and Queryable Encryption Recommended

TLS / SSL Encryption protects data in transit between clients and servers and between cluster members; Atlas enforces it. Encryption at Rest protects files on disk, through the storage engine's encryption in Enterprise or through the disk and cloud provider.

Client-Side Field Level Encryption encrypts chosen fields in the driver before they leave the application, so the server never sees plaintext, and keys stay with you in a key management service. Queryable Encryption goes further and allows equality searches on encrypted fields without the server learning the values. These protect against a compromised server or operator, at the price of query limits and extra key management. Use them for highly sensitive fields such as national ids.

Practice. Decide which fields of a patient record would need field level encryption and which only need disk encryption.

MongoDB Audit and Backups: mongodump and mongorestore Recommended

MongoDB Audit records who did what, such as logins, schema changes and access to collections, and is a feature of Enterprise Server and Atlas. Ship the logs to a central system and alert on unusual actions.

For backups, mongodump exports data to BSON files and mongorestore loads them again.

mongodump --uri="$MONGODB_URI" --db=shop --out=backup/
mongorestore --uri="$MONGODB_URI" --nsInclude='shop.*' backup/shop

These suit small and medium data and migrations. For large or busy systems use Atlas continuous backup, file system snapshots or Ops Manager with point-in-time recovery. A backup counts only when you have restored it into a test environment.

Practice. Dump a database, drop it, restore it and compare document counts.

Connecting MongoDB to Other Systems: Kafka, Spark and Elastic Search Optional

Data rarely lives in one place. The MongoDB Kafka connector moves data in both directions: change streams publish inserts and updates as events to Kafka topics, and a sink writes Kafka messages into collections. The Spark connector lets Apache Spark read collections as DataFrames and write results back, for large analytics jobs. Elastic Search is often fed from MongoDB through change streams or connectors when a team already runs it for search; on Atlas, Atlas Search is a simpler alternative that avoids the copy.

Change streams are the underlying feature: they let you watch a collection for changes and react in real time, with a resume token to continue after a restart.

Practice. Open a change stream on a collection and print each insert while you add documents from another shell.

Projects to build

Build these in order. Each one should end up in a public repository with a short README that explains your decisions.

  1. Contacts bookBeginner

    Build a command-line or small web contacts book on a local MongoDB. Support add, search by name or tag, edit and delete, and add a unique index on email.

    CRUD · Operators · Unique index · Driver

  2. Blog back endBeginner

    Create an API for users, posts and comments. Decide which parts to embed and which to reference, add pagination with a range filter and write schema validation for posts.

    Schema design · Pagination · Validation

  3. Sales analyticsIntermediate

    Load a sample orders dataset. Build pipelines for revenue per month, top customers, best sellers per category and a faceted summary, and save results with merge into a report collection.

    Aggregation · $lookup · $group · $facet

  4. Store locatorIntermediate

    Store shops as GeoJSON points, query the nearest ten to a location, filter by opening hours and use Atlas Search for autocomplete by shop name.

    Geospatial · Indexes · Atlas Search

  5. Event ingestion serviceAdvanced

    Write a service that stores millions of device events with the bucket pattern and a TTL index. Compare shard key choices on paper, measure insert speed with and without bulk writes and add a change stream consumer.

    Bucket pattern · TTL · Change streams · Shard keys

  6. Secure order systemAdvanced

    Add transactions for placing an order, role-based users for the app and for reporting, field level encryption for a sensitive field and a tested backup and restore script.

    Transactions · RBAC · Encryption · Backups

Interview questions

Try to answer each question out loud before you open the answer.

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

MongoDB is a document database. Data is stored as BSON documents in collections, with flexible fields, nested objects and arrays. Relational databases use fixed tables and relate them through joins. In MongoDB I model data around how it is read, often embedding related data, and I scale writes by sharding. It does support transactions and joins through $lookup, but the model favours fewer joins.

What is BSON and why not store plain JSON?Junior

BSON is a binary encoding of JSON-like documents. It adds types that JSON lacks, such as dates, 64-bit integers, Decimal128 and binary data, stores field lengths so the server can scan quickly, and supports efficient traversal. Drivers convert it to native types, so the application sees ordinary objects.

What is the `_id` field?Junior

Every document has an _id that is unique in its collection and indexed automatically. If I do not supply one, the driver generates an ObjectId, which holds a timestamp, a random part and a counter, so values increase roughly with time. I can use my own value, such as an email or a UUID, if it is unique and never changes.

How do you update a document without replacing it?Junior

Use an update operator such as $set, $inc, $push or $unset with updateOne or updateMany. If I pass a plain document without operators, it replaces the old one. Single-document updates are atomic, so $inc is safe for counters under concurrent requests.

What is the difference between `find` and `aggregate`?Junior

find filters and projects documents and returns them with sort, skip and limit. aggregate runs a pipeline of stages and can group, join, compute fields and reshape the output. If a task needs only filtering, I use find; for counts per group, joins or multi-step transformations I use aggregation.

When do you embed documents and when do you reference them?Mid

I embed when data is read together with its parent, belongs to it and stays small, for example order lines inside an order. I reference when data is shared, large or unbounded, or updated independently, for example comments on a popular post. The decision follows access patterns and growth, and I accept some duplication of stable fields for faster reads.

How do indexes work in MongoDB and what is the ESR rule?Mid

Indexes are B-trees that let queries avoid collection scans. For compound indexes I order fields by equality first, then sort fields, then range fields. That gives an index that filters precisely and returns documents already sorted. I verify with explain, looking for IXSCAN and a small gap between documents examined and returned.

How would you find and fix a slow query?Mid

Use the profiler or Atlas Performance Advisor to find slow operations, then run explain('executionStats'). If it shows COLLSCAN or an in-memory sort, I add or change an index. I also check that I project only needed fields, that pipelines $match early and that no large skip is used. After the change I compare executionStats again.

What does `$lookup` do and what are its limits?Mid

It joins documents from another collection in the same database, adding the matches as an array. It needs an index on the foreign field to be efficient, it can be costly on large inputs and across shards, and if I use it on almost every read, the schema probably should embed that data instead.

What is the difference between `$elemMatch` and querying array fields directly?Mid

Direct conditions on items.sku and items.qty can be satisfied by different array elements. $elemMatch requires one element to meet all conditions together. I use it whenever conditions must apply to the same sub-document.

Explain replica sets and failover.Mid

A replica set has one primary that takes writes and secondaries that replicate its oplog. If the primary becomes unreachable, the voting members hold an election and a secondary becomes primary, usually within seconds. Drivers rediscover the primary and, with retryable writes, retry once. I use at least three members, spread across zones.

How do write concern and read concern affect consistency?Senior

Write concern controls acknowledgement: w: 'majority' makes a write durable across a failover. Read concern controls the visibility of data: majority returns only data that most members have, and local may return data that could be rolled back. Reading from secondaries may be stale. I choose strong settings for money and relaxed ones for counters or logs.

How do you choose a shard key?Senior

A good key has high cardinality, spreads writes evenly and is present in most queries so they hit one shard. I avoid monotonically increasing keys, which create a hot shard, and use a hashed key or a compound key when needed. The key is hard to change, so I test it against the real workload before sharding.

When would you use transactions, and what do they cost?Senior

When a change must span several documents or collections atomically, such as moving money. They add latency and lock contention, have a time limit and make sharded operations slower. Because one document is atomic, I first try to model related data in one document, and use transactions for the rest.

How would you design a schema for time-series or event data?Senior

Use a time series collection if the version supports it, or apply the bucket pattern: one document per device per hour holding an array of measurements, with min, max and count precomputed. That cuts document and index counts. Add a TTL index for retention, shard by device and time if volume needs it and avoid unbounded arrays.

How do you secure a MongoDB deployment?Senior

Enable authentication and use role-based access with a separate least-privilege user per application. Keep the database off the public internet, require TLS and enable encryption at rest. Use client-side field level encryption for the most sensitive fields, turn on auditing, keep secrets out of code and test restores of backups.

Common mistakes

  • Running without authentication on a reachable server; enable it and restrict network access.
  • Modelling collections like relational tables; model around access patterns and embed where data is read together.
  • Growing arrays without limit; bound them or move the items to their own collection.
  • Querying without an index on a large collection; check with explain and add the index.
  • Using large skip values for paging; page with a range filter on an indexed field.
  • Storing money as Double; use Decimal128 or integer cents.
  • Matching _id strings against ObjectId values; convert types before querying.
  • Updating with a plain document and replacing the original; use operators like set.
  • Choosing a monotonically increasing shard key; use a hashed or compound key.

Ready-for-interview checklist

  • I can explain documents, collections, BSON and the 16 MB limit.
  • I can say when MongoDB is a good choice and when a relational database is better.
  • I can write queries with comparison, logical, element and array operators.
  • I can use update operators and upserts and explain atomicity of single-document writes.
  • I can build an aggregation pipeline with match, group, sort, lookup and unwind.
  • I can decide between embedding and referencing for a given use case.
  • I can add schema validation to a collection.
  • I can create compound indexes and apply the equality, sort, range rule.
  • I can read explain output and spot a collection scan.
  • I can explain replica sets, elections and retryable writes.
  • I can choose write and read concerns for different data.
  • I can explain shard keys and the hot shard problem.
  • I can set up users and roles, and describe encryption options and a backup plan.

Related roadmaps

FAQ

How long does it take to learn MongoDB?

The basics of CRUD and simple queries take about one to two weeks. Interview-level knowledge, including aggregation, schema design, indexing and replication, usually takes two to three months at around eight hours a week. Building a small project with real data helps most.

Should I learn SQL before MongoDB?

It helps, because many ideas, such as keys, indexes and transactions, appear in both, and interviews often ask you to compare them. You can learn them in either order. See the SQL Roadmap roadmap for the relational side.

Is MongoDB still worth learning?

Yes. It is one of the most used databases, offered by every major cloud and common in startups and large companies. It is not a replacement for relational databases, so learn both and know when each fits.

Is MongoDB schemaless?

Not exactly. Collections do not force a structure by default, but your application always assumes one. Good practice is to design the schema deliberately and use validation rules to enforce the important parts.

Do I need Atlas or can I run MongoDB locally?

You can learn everything locally with the Community Server or a container. Atlas is convenient for a free managed cluster and is needed for Atlas Search and some managed features.

Practise while you learn.Solve interview problems in the browser, with hints and a timer.
CodingSQLSystem design
Next Post Previous Post
No Comment
Add Comment
comment url