MongoDB Mastery
MongoDB Mastery lessons / Module 1: MongoDB Fundamentals

What MongoDB is and when to use it

Level 1: Beginner15 min

MongoDB stores data as documents: JSON-like records with nested objects and arrays. This lesson explains the model, how it differs from relational databases, and where it fits.

This lesson is interactive. Run every example against an in-browser MongoDB, see explain() plans drawn as trees, and take the quiz.
Open the interactive lesson
You will learn to
  • Describe documents, collections and databases
  • Explain how BSON relates to JSON
  • List the workloads where MongoDB is a strong or weak fit
  • Run your first commands in the shell

conceptThe concept

MongoDB is a general-purpose document database. A document is a set of field/value pairs, where values can be scalars, nested documents or arrays. Documents live in collections, and collections live in databases. A deployment is normally a replica set (three or more copies of the data) and can be sharded to scale horizontally.

Open the interactive lesson to use this diagram.

RelationalMongoDB
DatabaseDatabase
TableCollection
RowDocument
ColumnField (can be nested or an array)
Primary key_id field (required, unique per collection)
JOINEmbedding, or $lookup in aggregation
Foreign key constraintNone enforced by the server; modelled in the application

Documents are stored as BSON (binary JSON). BSON adds types JSON lacks (dates, 32/64-bit integers, Decimal128, ObjectId, binary) and length prefixes that make documents fast to traverse.

whyWhy it matters

The document model maps directly onto objects in application code, so a single read can return everything a screen needs. That removes most joins from the hot path and lets the schema evolve without migrations that lock tables. Replication and automatic failover are built in, and sharding spreads both data and write load across machines.

Good fitsCatalogs and content, user profiles and sessions, orders and carts, IoT and event data (time series collections), real-time analytics, mobile backends, and anything where the shape of the data varies or evolves quickly.
Weaker fitsWorkloads dominated by ad-hoc joins across many entities, heavy multi-row transactional accounting with strict cross-entity constraints, or teams that rely on the database to enforce relational integrity everywhere. MongoDB supports multi-document transactions, but designing so you rarely need them is the point of the model.

Use caseReal-world scenario

An online shop stores each order with its line items, payment and shipping details embedded. The order page loads with one indexed read. The product catalog stores electronics, books and groceries in one products collection even though each category has different attributes. Analytics run as aggregation pipelines on a secondary so they do not slow down checkout.

Try itExamples

Run and edit these in the interactive lesson.

Look around
show dbs
use shop
show collections
Your first queries
db.orders.countDocuments()
db.customers.findOne()
db.orders.findOne({}, { orderNo: 1, status: 1, items: 1 })
An order embeds its line items as an array of sub-documents. No join needed to show the order page.
Flexible shape in one collection
db.products.find({ category: "books" }, { name: 1, attributes: 1 }).limit(2)
db.products.find({ category: "electronics" }, { name: 1, attributes: 1 }).limit(2)
Books and electronics share a collection but carry different attributes.

internalsUnder the hood

A mongod process hosts databases. Each collection and each index is a separate table in the WiredTiger storage engine, stored as a B-tree in its own file. Documents are kept in BSON, compressed on disk (snappy by default), and decompressed in the WiredTiger cache. Every write on the primary also records an entry in the oplog, which secondaries replay to stay in sync. You will dig into each of these in levels 4 to 6.

Avoid / preferDo this, not that

Avoid
db.orders.insertOne({ _id: 1001, customerId: 66 })
db.order_items.insertMany([
  { orderId: 1001, productId: "P016", qty: 1 },
  { orderId: 1001, productId: "P078", qty: 2 }
])
Prefer
db.orders.insertOne({
  _id: 1001, customerId: 66,
  items: [
    { productId: "P016", qty: 1, price: 681.5 },
    { productId: "P078", qty: 2, price: 17.02 }
  ]
})

Copying a relational schema table for table gives you joins on every page view without a relational optimizer. Store together what the application reads together.

Avoid
mongod --dbpath /data/db   # one server in production
Prefer
mongod --replSet rs0 --dbpath /data/db
# three members: one primary and two secondaries

A standalone has no automatic failover, cannot run multi-document transactions and cannot serve change streams. A three-member replica set is the smallest production deployment.

PitfallsCommon mistakes

  • Assuming "schemaless" means "no schema design". You still design for access patterns; the schema simply lives in your application and, ideally, in a validator.
  • Normalizing everything into many collections and joining with $lookup everywhere, which recreates a relational design without the relational optimizer.
  • Running a single mongod in production. Use a replica set so you get failover and can take backups from a secondary.

LimitsWhen not to use it

MongoDB is a poor fit when the data is deeply relational and queried in unpredictable ways: many-to-many joins across a dozen entities, ad-hoc reporting that joins everything with everything, or rules that need foreign keys enforced by the database. It is also not a columnar warehouse for scanning billions of rows per query, and not a cache. Use it where the application's access patterns are known and map naturally onto documents.

SecuritySecurity notes

Since 3.6 mongod binds to localhost by default, and that default exists for a reason: tens of thousands of instances exposed on port 27017 without authentication have been wiped and ransomed. Never open the port to the internet, and always run with security.authorization: enabled.

ChallengeTry it yourself

In one expression, list every collection in shop with its document count.

Show one solution
One solution
db.getCollectionNames().map(c => ({ collection: c, docs: db.getCollection(c).countDocuments() }))

InterviewQuestions you should be able to answer

How is MongoDB different from a relational database?

It stores documents (nested objects and arrays) instead of rows in fixed tables. Data accessed together is usually embedded together, so joins are rarer. Schemas are flexible but can be enforced with $jsonSchema validators. Scaling out is native through sharding, and high availability through replica sets. It supports ACID transactions: single-document always, multi-document since 4.0 (replica sets) and 4.2 (sharded).

What is BSON and why not store plain JSON?

BSON is a binary encoding of JSON-like documents. It adds types (Date, ObjectId, Int32, Int64, Decimal128, binary) and length prefixes so the server can skip over fields without parsing them. JSON has only one number type and no date type, which is not enough for a database.

QuizCheck your understanding

4 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.