Skip to main content

πŸƒ MongoDB Concepts

Relational databases make you draw a rigid grid before you store a single row. MongoDB flips that around: you store whole objects β€” the same shape your JavaScript already uses β€” and worry about structure only where it actually helps. This lesson gives you the mental model behind that freedom.

Week 8 · Day 3 (Wednesday: MongoDB Basics) · Lecture 1

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what a document database is and how it differs from a relational one
  • Describe the database β†’ collection β†’ document hierarchy in your own words
  • Read a MongoDB document and identify its fields, the _id, and its BSON types
  • Map relational terms (table, row, column, join) to their MongoDB equivalents
  • Decide between embedding and referencing for a given relationship
  • Recognize when MongoDB is a good fit β€” and when a SQL database is the better call

Estimated Time: 55 minutes

Practice: Model a blog's users, posts, and comments as MongoDB documents.

In This Lesson

Why a Document Database?

Imagine you're describing a customer to a colleague. You'd naturally say something like: "Ada β€” email ada@example.com, 36, has a home address and a work address, and she's interested in programming and hiking." That's one coherent thing with some nested details and a couple of lists.

A relational database forces you to shatter that single idea across several tables β€” a users table, an addresses table, an interests table β€” then stitch them back together with joins every time you want the whole picture. A document database like MongoDB lets you store Ada as she actually is: one self-contained document that holds her nested address and her list of interests together.

MongoDB is the most widely used document-oriented NoSQL database. "NoSQL" just means "not the traditional table-and-row model" β€” it's a family, not a single product. MongoDB belongs to the document store branch of that family.

graph TD A[NoSQL Databases] --> B[Document Stores] A --> C[Key-Value Stores] A --> D[Column-Family Stores] A --> E[Graph Databases] B --> B1[MongoDB] B --> B2[CouchDB] C --> C1[Redis] C --> C2[DynamoDB] D --> D1[Cassandra] E --> E1[Neo4j]

Because a document looks almost exactly like a JavaScript object, MongoDB feels natural to full-stack JS developers: the data you send from the browser, the data your Node server processes, and the data on disk all share the same JSON-like shape. That end-to-end consistency is why MongoDB pairs so often with Node and Express.

The MongoDB Hierarchy

MongoDB organizes data in three nested layers. Understanding this hierarchy is the single most important concept in the lesson β€” everything else builds on it.

MongoDB hierarchy: a database contains collections, which contain documents πŸ—„οΈ Database: "shop" πŸ“ Collection: users πŸ“„ { _id, name: "Ada", … } πŸ“„ { _id, name: "Grace", … } πŸ“„ { _id, name: "Linus", … } πŸ“ Collection: products πŸ“„ { _id, name: "Laptop", … } πŸ“„ { _id, name: "Phone", … } πŸ“„ { _id, name: "Mouse", … }
A single MongoDB database holds many collections; each collection holds many documents.

Database

The outermost container. A single MongoDB server can host many databases side by side β€” one for your shop app, one for your blog, one for testing. You typically use one database per application.

Collection

A named group of related documents, roughly like a table in SQL β€” but with a crucial difference: a collection does not force every document to have the same fields. You'll usually keep documents of the same "kind" together (all users in users, all orders in orders), but MongoDB won't stop you from storing slightly different shapes in the same collection.

Document

The basic unit of data β€” a single record, stored as a set of field/value pairs. This is the object you actually read and write. A document can nest other documents and arrays to any depth, which is what lets one document capture a whole "thing" the way Ada's record did.

πŸ’‘ A quick analogy

Think of a filing cabinet: the cabinet is the database, each drawer is a collection, and each folder inside a drawer is a document. Unlike a paper cabinet, though, every folder can hold a different set of papers.

Documents, BSON & _id

Here is a real document from a users collection. Read it top to bottom β€” notice how it mixes a string, a number, a nested object, and an array, all in one record.

{
  "_id": ObjectId("60a91cd3b3e55a0015c0e980"), // primary key, auto-created
  "name": "Ada Lovelace",                       // string field
  "email": "ada@example.com",
  "age": 36,                                     // number field
  "address": {                                   // nested (embedded) document
    "street": "123 Main St",
    "city": "London",
    "zip": "SW1A 1AA"
  },
  "interests": ["programming", "mathematics"],  // array field
  "isActive": true                              // boolean field
}

BSON: JSON with superpowers

You write documents as JSON, but MongoDB stores them as BSON β€” "Binary JSON." BSON is a compact binary encoding of JSON that adds data types plain JSON can't express, such as a true Date, a 64-bit integer, and the ObjectId type. That's why releaseDate: new Date("2023-04-15") survives a round-trip as a real date instead of becoming a string.

πŸ“– Why not just JSON?

JSON only knows strings, numbers, booleans, null, arrays, and objects. Databases need more: distinct integer vs. floating-point types, binary data, and dates that sort correctly. BSON extends JSON with those types while keeping the familiar field/value feel. You'll almost always work in JSON-shaped code and let the driver handle the BSON conversion.

The _id field

Every document has a unique _id that acts as its primary key. If you don't supply one, MongoDB generates an ObjectId β€” a 12-byte value that is globally unique and, handily, encodes the creation timestamp in its first 4 bytes. That means an ObjectId is roughly sortable by creation time for free.

// You can generate an ObjectId yourself with the driver:
import { ObjectId } from 'mongodb';

const id = new ObjectId();          // e.g. new ObjectId("665f...c2a1")
id.getTimestamp();                  // β†’ a Date: when the id was created

// You may also use your own _id (must be unique within the collection):
{ _id: "user-ada", name: "Ada" }   // a string primary key is perfectly valid

Output

typeof document._id        β†’ "object"   (an ObjectId, not a plain string)
document._id.toString()    β†’ "60a91cd3b3e55a0015c0e980"

MongoDB vs. Relational

If you've seen SQL databases, this translation table will make MongoDB click instantly. The concepts map cleanly β€” only the vocabulary and the storage shape change.

Relational (e.g. PostgreSQL)MongoDBWhat it is
DatabaseDatabaseTop-level container
TableCollectionA group of similar records
RowDocumentA single record
ColumnFieldOne key/value pair
Primary key_idUnique identifier for a record
JOIN across tablesEmbedding or $lookupRelating data together
Fixed schema (defined up front)Flexible schema (per document)How structure is enforced
SQL query languageQuery documents (JSON filters)How you ask for data

⚠️ "Flexible schema" is a tool, not a free pass

Flexible doesn't mean shapeless. Most real applications keep a consistent structure within a collection so queries stay predictable. MongoDB just moves schema enforcement out of the database's rigid rules and into your application code (or optional schema validation) β€” you gain flexibility, but you also own the discipline.

Embedding vs. Referencing

Because relationships aren't handled by joins, you decide how related data is arranged. There are two strategies, and choosing well is the heart of MongoDB data modeling.

graph LR subgraph Embedding U1["user document"] --> A1["addresses array
inside the same doc"] end subgraph Referencing U2["user document"] --> R1["address_ids: ObjectId list"] R1 -.points to.-> C2["addresses collection"] end

Embedding β€” keep related data together

Store the related data inside the parent document. Ada's addresses live right in her user record.

// One self-contained user document
{
  "_id": ObjectId("..."),
  "name": "Ada Lovelace",
  "addresses": [
    { "type": "home", "street": "123 Main St", "city": "London" },
    { "type": "work", "street": "456 Analytical Ave", "city": "London" }
  ]
}
// Reading a user gives you their addresses in a single query β€” no join.

Great when: the data is always used together, belongs to one owner, and the "many" side is bounded (a few addresses, not a million log lines). This is the default you should reach for first.

Referencing β€” link by _id

Store a pointer (the related document's _id) instead of the data itself, and keep the related documents in their own collection.

// user document keeps only references
{ "_id": ObjectId("u1"), "name": "Ada", "post_ids": [ObjectId("p1"), ObjectId("p2")] }

// posts collection holds the full posts
{ "_id": ObjectId("p1"), "author_id": ObjectId("u1"), "title": "On Analytical Engines" }
{ "_id": ObjectId("p2"), "author_id": ObjectId("u1"), "title": "Notes on Numbers" }

Great when: the related data is large, shared by many parents, updated independently, or unbounded in count. You fetch it with a second query or a $lookup aggregation.

βœ… The rule of thumb

"Data that is accessed together should be stored together." Start by embedding. Switch to referencing when the embedded data grows without limit, is duplicated across many documents, or needs to be queried on its own. You'll practice this decision constantly β€” it's a design skill, not a fixed formula.

When to Reach for MongoDB

No database is universally "best." MongoDB shines in some situations and is the wrong tool in others. Being able to say why you chose it is what separates a developer from someone who just follows tutorials.

MongoDB is a strong fit for…

  • Evolving requirements β€” early-stage products whose data shape changes weekly
  • Naturally nested data β€” content, product catalogs, user profiles with varied attributes
  • High write throughput β€” event logs, activity streams, IoT sensor readings
  • Horizontal scale β€” datasets spread across many servers via sharding
  • Full-stack JS teams β€” the JSON-everywhere workflow removes friction

Prefer a SQL database when…

  • Your data is highly relational with many-to-many links best served by real joins
  • You need multi-row, multi-table transactions as a core, constant requirement (finance, inventory)
  • The schema is stable and strong integrity constraints matter more than flexibility

πŸ’‘ It's not either/or

Large systems commonly use both β€” PostgreSQL for transactional records and MongoDB for flexible, high-volume content. Choosing the right store per job is called polyglot persistence. (MongoDB has supported multi-document transactions since v4.0, but they're best used sparingly rather than as the backbone of your design.)

Practice & Quiz

πŸ‹οΈ Exercise 1: Model a blog

Goal: Sketch documents for a simple blog with users, posts, and comments. Decide what to embed and what to reference, and justify each choice in a comment.

Assume: a user writes many posts; a post has a handful of comments; comments are only ever read together with their post.

πŸ’‘ Hint

Comments are few-per-post and always read with the post β†’ strong candidate for embedding. A user can have unbounded posts that are also browsed on their own β†’ reference the author from each post rather than embedding posts in the user.

βœ… Solution
// users collection β€” one document per person
{
  "_id": ObjectId("u1"),
  "name": "Ada Lovelace",
  "email": "ada@example.com"
}

// posts collection β€” reference the author, embed the comments
{
  "_id": ObjectId("p1"),
  "author_id": ObjectId("u1"),      // reference: posts are browsed independently
  "title": "Hello, MongoDB",
  "body": "My first post...",
  "comments": [                      // embed: few per post, always read together
    { "author": "Grace", "text": "Great read!", "createdAt": "2026-01-10" },
    { "author": "Linus", "text": "Thanks for sharing." }
  ]
}

If comments could grow into the thousands (a viral post), you'd move them to their own collection and reference back to the post instead.

πŸ‹οΈ Exercise 2: Read a document

Goal: Given the document below, answer: which collection would it live in, what is its primary key, and which field is an embedded document?

{
  "_id": ObjectId("665f0a12ab34cd56ef789012"),
  "sku": "LP-2024",
  "name": "UltraBook 14",
  "price": 1299.99,
  "specs": { "cpu": "Core i7", "ram": "16GB", "storage": "512GB" },
  "tags": ["laptop", "electronics"]
}
βœ… Solution

A sensible collection is products. The primary key is the _id (an ObjectId). The embedded document is specs β€” a nested object holding cpu, ram, and storage. tags is an array of strings, and price is a BSON number.

🎯 Quick Quiz

Question 1: In MongoDB terms, a "row" from a relational table is most like a…

Question 2: What is BSON?

Question 3: You have a post that could receive an unlimited number of comments. The best default is to…

Best Practices & Pitfalls

βœ… Do

  • Design documents around how your app reads data, not around abstract "normal forms"
  • Embed data that's accessed together and bounded in size
  • Keep one kind of thing per collection so queries stay predictable
  • Let MongoDB generate ObjectId values unless you have a real reason not to
  • Consider schema validation once your shape stabilizes

❌ Don't

  • Create unbounded embedded arrays that grow forever (comments, logs, followers)
  • Blindly copy relational table designs into collections table-for-table
  • Assume "flexible schema" means "no schema" β€” inconsistent shapes create query bugs
  • Forget the 16 MB per-document size limit when embedding large data

⚠️ The 16 MB document limit

A single BSON document cannot exceed 16 MB. That's generous for normal records, but it's exactly why unbounded embedding is dangerous β€” a post that embeds a million comments will eventually hit the ceiling and fail. When growth is open-ended, reference.

Summary

πŸŽ‰ Key Takeaways

  • MongoDB is a document database: it stores JSON-like records instead of rigid rows
  • The hierarchy is database β†’ collection β†’ document
  • Documents are stored as BSON and each carries a unique _id (usually an ObjectId)
  • Relational terms map cleanly: tableβ†’collection, rowβ†’document, columnβ†’field
  • You model relationships by embedding (data used together) or referencing (large/shared/unbounded data)
  • Choose MongoDB for evolving, nested, high-write data β€” SQL for heavily relational, transaction-critical data

πŸ“š Additional Resources

πŸš€ What's Next?

You can now read a document and reason about how to model data. Next, you'll actually put data in and take it back out: the four operations every application lives on β€” CRUD Operations with the official MongoDB Node.js driver.

πŸŽ‰ Solid foundation!

The document mindset you built here is what makes everything else in MongoDB feel obvious.