Section 5: MongoDB Basics

πŸ“ Documents & BSON: MongoDB's Data Format

Understanding MongoDB documents and BSON - the secret sauce behind MongoDB's flexibility and power

πŸ“– The Tale of Two Developers: SQL Tables vs MongoDB Documents

Meet Sarah (using MySQL) and Alex (using MongoDB). Both are building a social media app. Let's watch their struggle with storing user profiles...

😰

Sarah's SQL Struggle

The Rigid Structure Problem

Day 1: Creating User Table

-- Basic user info
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100),
  age INT
);

"Perfect! Simple and clean!" 😊

Day 5: Boss wants to add phone numbers!

-- Oh no, need another table!
CREATE TABLE user_phones (
  id INT PRIMARY KEY,
  user_id INT,
  phone VARCHAR(20),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

"Okay... added a new table." πŸ˜…

Day 10: Now add addresses, hobbies, work history...

-- 😱 Explosion of tables!
CREATE TABLE user_addresses (...)
CREATE TABLE user_hobbies (...)
CREATE TABLE user_education (...)
CREATE TABLE user_work_history (...)
CREATE TABLE user_social_links (...)
-- Now need 5-way JOINs to get profile! πŸ’€

"This is a NIGHTMARE! 7 tables just for one user profile!" 😭

😎

Alex's MongoDB Magic

The Flexible Document Way

Day 1 to Day 10: ONE Document for Everything!

// All data in ONE place! πŸŽ‰
{
  _id: ObjectId("507f1f77..."),
  name: "John Doe",
  email: "john@email.com",
  age: 28,
  phones: ["+1-555-1234", "+1-555-5678"],
  address: {
    street: "123 Main St",
    city: "New York",
    zip: "10001"
  },
  hobbies: ["coding", "gaming", "reading"],
  education: [{
    school: "MIT",
    degree: "BS Computer Science",
    year: 2018
  }],
  social: {
    twitter: "@johndoe",
    linkedin: "linkedin.com/in/johndoe"
  }
}

"One query, one document, all the data! Life is good!" 🎯✨

πŸ’‘ The MongoDB Document Philosophy

"Store data the way you USE it, not the way databases FORCE you to!"
MongoDB documents mirror your application's objects - natural, flexible, and powerful! πŸš€

πŸ“„ What are MongoDB Documents?

A MongoDB document is a data structure composed of field-value pairs, similar to JSON objects.
Think of it as a digital file cabinet where each file (document) can have completely different fields!

❌ Traditional SQL Table

id name email age
1 John john@ex.com 28
2 Alice alice@ex.com NULL

😱 Problems:

  • All rows MUST have same columns
  • Can't add fields to just one row
  • NULL for missing data (wastes space)
  • Can't store arrays or nested objects

βœ… MongoDB Documents

// Document 1 - has age
{
  _id: 1,
  name: "John",
  email: "john@ex.com",
  age: 28
}

// Document 2 - no age, has hobbies!
{
  _id: 2,
  name: "Alice",
  email: "alice@ex.com",
  hobbies: ["coding", "gaming"]
}

🎯 Benefits:

  • Each document can have different fields!
  • No wasted space on NULL values
  • Arrays and nested objects supported
  • Flexibility to evolve schema over time

πŸ”‘ Key Characteristics of Documents

1. Flexible Schema

Documents in the same collection don't need identical structure. Add new fields anytime without migrations!

2. Hierarchical Structure

Documents can contain embedded documents and arrays. Store related data together instead of JOINs!

3. Natural Mapping to Objects

Documents mirror your application's objects. What you code is what you store!

4. Size Limit: 16 MB

Each document can be up to 16 MB. That's about 16,000 pages of text! More than enough for most use cases.

πŸ”„ JSON vs BSON: The Secret Sauce

🎭 The Behind-the-Scenes Magic

You write documents in JSON (human-readable), but MongoDB secretly stores them in BSON (binary, optimized for machines)!

You Write JSON Human-Readable Converts MongoDB Stores BSON Binary, Optimized Returns You Read JSON Back to Readable!

Translation happens automatically! You never have to think about BSON - it's MongoDB's internal optimization.

Aspect πŸ“ JSON ⚑ BSON
Full Name JavaScript Object Notation Binary JSON
Format Text-based
Human-readable
Binary
Machine-optimized
Size Larger
More characters
Smaller
Compressed
Speed Slower
Parsing needed
⚑ Faster
Direct read
Data Types String, Number, Boolean, Array, Object, null
(6 types only)
All JSON types + Date, Binary, ObjectId, Int32, Int64, Decimal128...
(Many more!)
Use Case APIs, Config files, Data exchange Database storage, Performance-critical systems
Example {"name": "John", "age": 28} \x16\x00\x00\x00\x02name\x00...
(Binary representation)

πŸ’‘ Why MongoDB Uses BSON Internally?

1
Speed: BSON is faster to parse and traverse. Binary format allows direct memory access without string parsing.
2
Rich Data Types: BSON supports dates, binary data, ObjectId, different number types (Int32, Int64, Decimal128) - JSON only has "number".
3
Efficient Storage: BSON includes document length at the beginning, making it easy to skip over documents without parsing entire content.
4
Indexing: BSON format makes it easier to create and use indexes efficiently.

🎨 BSON Data Types: The Complete Toolkit

BSON supports WAY more data types than JSON. Here's your complete reference! πŸ“š

String UTF-8 encoded text
{ name: "John Doe", city: "New York" }

Most common type. Use for text, names, descriptions, URLs, etc.

Numbers Multiple numeric types!
{ age: 28, // Int32 (32-bit integer)
  count: NumberLong(9876543210), // Int64 (64-bit integer)
  price: 99.99, // Double (floating point)
  balance: NumberDecimal("1234.56") // Decimal128 (precise!) }

Pro Tip: Use Decimal128 for money - it's precise! Regular floats have rounding errors.

Boolean true or false
{ isActive: true, isPremium: false }

Use for flags, toggles, yes/no questions.

Date Timestamps and dates
{ createdAt: new Date(),
  birthday: ISODate("1995-06-15T00:00:00Z") }

Super useful! JSON doesn't have date type - must use strings. BSON has native date support!

ObjectId Unique document identifier
{ _id: ObjectId("507f1f77bcf86cd799439011") }

Structure (12 bytes):

4 bytes
Timestamp
5 bytes
Random value
3 bytes
Counter

Automatically created for _id field if not provided. Guaranteed unique!

Array Ordered list of values
{ hobbies: ["coding", "gaming", "reading"],
  scores: [95, 87, 92],
  tags: [] // Empty array is valid! }

Can contain any BSON type, even mixed types! Very powerful for lists.

Object Embedded/nested document
{ address: {
    street: "123 Main St",
    city: "New York",
    zip: "10001"
  } }

Game changer! No JOINs needed. Store related data together!

Null Represents absence of value
{ middleName: null }

Use when field exists but has no value. Or just omit the field entirely!

Binary Data Raw binary (images, files)
{ profilePic: BinData(0, "iVBORw0KGgo...") }

Note: Use for small files only (< 16 MB). Large files? Use GridFS!

πŸ—οΈ Document Structure Patterns

Learn the art of structuring documents for optimal performance! 🎨

Pattern 1: Flat Document (Simple)

{
  _id: ObjectId("..."),
  name: "John Doe",
  email: "john@example.com",
  age: 28,
  city: "New York"
}
βœ… Best For:
  • Simple data with no relationships
  • Configuration settings
  • Small lookup tables

Pattern 2: Embedded Document (One-to-One)

{
  _id: ObjectId("..."),
  name: "John Doe",
  email: "john@example.com",
  address: { // Embedded document!
    street: "123 Main St",
    city: "New York",
    state: "NY",
    zip: "10001"
  },
  phone: {
    home: "+1-555-1234",
    mobile: "+1-555-5678"
  }
}
βœ… Best For:
  • One-to-one relationships (user β†’ address)
  • Data that's always queried together
  • Related data that doesn't change independently
🎯 Benefits:
  • No JOINs needed - all data in one document
  • Atomic updates - update user and address together
  • Better performance - single read operation

Pattern 3: Array of Primitives (One-to-Few)

{
  _id: ObjectId("..."),
  name: "John Doe",
  email: "john@example.com",
  hobbies: ["coding", "gaming", "reading"],
  skills: ["JavaScript", "Python", "MongoDB"],
  scores: [95, 87, 92, 88]
}
βœ… Best For:
  • Tags, categories, labels
  • Skills, hobbies, interests
  • When you have FEW items (< 100)

Pattern 4: Array of Embedded Documents (One-to-Many)

{
  _id: ObjectId("..."),
  title: "MongoDB Tutorial",
  author: "John Doe",
  comments: [ // Array of embedded documents!
    {
      user: "Alice",
      comment: "Great tutorial!",
      date: ISODate("2024-01-15")
    },
    {
      user: "Bob",
      comment: "Very helpful!",
      date: ISODate("2024-01-16")
    }
  ]
}
βœ… Best For:
  • Blog post β†’ comments
  • Order β†’ order items
  • Product β†’ reviews (if not too many)
⚠️ Watch Out:
  • Don't use if array will grow large (> 1000 items)
  • Document size limit is 16 MB!
  • If unbounded growth, use references instead

Pattern 5: References (Many-to-Many)

// User document
{
  _id: ObjectId("user123"),
  name: "John Doe",
  email: "john@example.com"
}

// Order document (references user)
{
  _id: ObjectId("order456"),
  userId: ObjectId("user123"), // Reference!
  items: ["laptop", "mouse"],
  total: 1299.99
}
βœ… Best For:
  • Many-to-many relationships (students ↔ courses)
  • Data that changes independently
  • Unbounded arrays (user β†’ unlimited orders)
  • Large related documents
πŸ“ Note:

Need to perform $lookup (JOIN) to get related data. Slower than embedded, but necessary for large/independent data.

🌍 Real-World Document Examples

Example 1: User Profile (Social Media)

{
  _id: ObjectId("507f1f77bcf86cd799439011"),
  username: "johndoe",
  email: "john@example.com",
  passwordHash: "$2b$10$...",
  profile: {
    fullName: "John Doe",
    bio: "Software Developer | Coffee Lover",
    avatar: "https://cdn.example.com/avatar.jpg",
    dateOfBirth: ISODate("1995-06-15"),
    location: {
      city: "New York",
      country: "USA"
    }
  },
  followers: [ObjectId("..."), ObjectId("...")],
  following: [ObjectId("..."), ObjectId("...")],
  stats: {
    postsCount: 245,
    followersCount: 1250,
    followingCount: 380
  },
  preferences: {
    theme: "dark",
    language: "en",
    notifications: {
      email: true,
      push: true,
      sms: false
    }
  },
  createdAt: ISODate("2023-01-15T10:30:00Z"),
  updatedAt: ISODate("2024-12-08T15:45:00Z"),
  isVerified: true,
  isActive: true
}

Example 2: E-commerce Product

{
  _id: ObjectId("65a1b2c3d4e5f6789"),
  sku: "LAPTOP-XPS13-2024",
  name: "Dell XPS 13 Laptop",
  description: "13.4-inch FHD+ display, Intel i7...",
  category: "Electronics",
  subcategory: "Laptops",
  brand: "Dell",
  price: {
    amount: NumberDecimal("1299.99"),
    currency: "USD",
    discount: {
      percentage: 15,
      validUntil: ISODate("2024-12-31")
    }
  },
  inventory: {
    inStock: 25,
    reserved: 3,
    warehouse: "NYC-01"
  },
  images: [
    {
      url: "https://cdn.example.com/laptop1.jpg",
      isPrimary: true
    },
    {
      url: "https://cdn.example.com/laptop2.jpg",
      isPrimary: false
    }
  ],
  specifications: {
    processor: "Intel Core i7-1355U",
    ram: "16GB DDR5",
    storage: "512GB NVMe SSD",
    display: "13.4\" FHD+ (1920x1200)",
    weight: "2.7 lbs"
  },
  tags: ["laptop", "dell", "ultrabook", "portable"],
  rating: {
    average: 4.7,
    count: 348
  },
  reviews: [ObjectId("..."), ObjectId("...")],  // References
  createdAt: ISODate("2024-01-10T08:00:00Z"),
  updatedAt: ISODate("2024-12-08T14:30:00Z"),
  isActive: true,
  isFeatured: true
}

Example 3: Blog Post with Comments

{
  _id: ObjectId("..."),
  title: "Getting Started with MongoDB",
  slug: "getting-started-with-mongodb",
  content: "MongoDB is a NoSQL database that...",
  excerpt: "Learn the basics of MongoDB...",
  author: {
    id: ObjectId("..."),
    name: "Jane Smith",
    email: "jane@example.com"
  },
  tags: ["mongodb", "nosql", "database", "tutorial"],
  categories: ["Databases", "Tutorials"],
  featuredImage: "https://cdn.example.com/mongodb.jpg",
  stats: {
    views: 15420,
    likes: 892,
    shares: 234
  },
  comments: [
    {
      id: ObjectId("..."),
      userId: ObjectId("..."),
      userName: "Bob Johnson",
      comment: "Great tutorial! Very helpful.",
      createdAt: ISODate("2024-12-05T10:30:00Z"),
      likes: 12
    },
    {
      id: ObjectId("..."),
      userId: ObjectId("..."),
      userName: "Alice Cooper",
      comment: "Thanks for sharing!",
      createdAt: ISODate("2024-12-06T14:15:00Z"),
      likes: 8
    }
  ],
  seo: {
    metaTitle: "MongoDB Tutorial - Complete Guide",
    metaDescription: "Learn MongoDB from scratch...",
    keywords: ["mongodb", "tutorial", "nosql"]
  },
  publishedAt: ISODate("2024-12-01T09:00:00Z"),
  updatedAt: ISODate("2024-12-08T11:20:00Z"),
  status: "published",
  isFeatured: true
}

πŸ’» Live Document Console

Create and query MongoDB documents interactively!

● LIVE
MongoDB Shell - Documents & BSON
● Connected to: localhost:27017
πŸ“ INPUT
βœ… OUTPUT
// Click example buttons above or write your own! πŸ‘† Ready to execute commands... Press "RUN" or Ctrl+Enter
πŸ’‘ Tip: Try modifying the examples!

βœ… Best Practices for Document Design

1

Design for Your Queries

Structure documents based on how you'll query them. If you always need user + address together, embed address. If you query them separately, use references.

Example:
E-commerce: Embed user's current cart items (always queried together). Reference past orders (queried separately).
2

Avoid Unbounded Arrays

Arrays that grow without limit will hit the 16 MB document size limit. Use references for unlimited relationships.

❌ Bad:
user.orders: [...] // Unlimited
βœ… Good:
order.userId: ObjectId(...)
3

Use Appropriate Data Types

Choose the right BSON type. Use NumberDecimal for money, Date for timestamps, ObjectId for IDs.

βœ“ price: NumberDecimal("99.99") - Precise!
βœ— price: 99.99 - Float rounding errors!
4

Keep Documents Under 16 MB

This is a hard limit. For large files (images, videos), use GridFS. For large arrays, use references.

Tips:
  • Don't embed large binary data
  • Limit array size (< 1000 items ideal)
  • Use GridFS for files > 16 MB
5

Plan for Schema Evolution

MongoDB is schema-less but plan for changes. Add new fields easily, but consider backward compatibility.

Strategy:
Use default values in code. Old documents without new fields? No problem! Just return defaults.

❓ Interview Questions & Answers

Q1 What is a MongoDB document? How is it different from a SQL row? β–Ό

Answer:

A MongoDB document is a data structure composed of field-value pairs, similar to JSON objects. It's stored in BSON (Binary JSON) format internally.

Key Differences from SQL rows:

  • Schema Flexibility: Documents in the same collection can have different fields. SQL rows must have same columns.
  • Nested Data: Documents support embedded documents and arrays. SQL requires separate tables and JOINs.
  • NULL handling: MongoDB: just omit field. SQL: must use NULL, wastes space.
  • Data Types: BSON supports more types (Date, Binary, ObjectId) than SQL's basic types.

Example: In MongoDB, one user document can have "age" field while another doesn't. In SQL, all rows must have age column (even if NULL).

Q2 What is BSON? Why doesn't MongoDB use JSON directly? β–Ό

Answer:

BSON stands for Binary JSON. MongoDB stores documents in BSON format internally, though you work with JSON.

Why BSON instead of JSON?

  • Speed: Binary format is faster to parse and traverse. No string parsing needed.
  • Rich Data Types: BSON supports Date, Binary, ObjectId, NumberDecimal - JSON only has string, number, boolean, array, object, null.
  • Efficient Storage: BSON includes document length at the beginning, making it easy to skip documents without parsing entire content.
  • Indexing: Binary format makes indexing more efficient.

The Flow: You write JSON β†’ MongoDB converts to BSON β†’ Stores BSON β†’ Returns JSON to you. Conversion is automatic!

Q3 When should you embed documents vs use references? β–Ό

Answer:

Use Embedding When:

  • One-to-One or One-to-Few relationships: User β†’ Address (one address)
  • Data accessed together: Always show user with their address
  • Data doesn't change independently: Address only changes when user updates it
  • Bounded array: Blog post β†’ 50 comments (limited, won't grow huge)

Use References When:

  • One-to-Many or Many-to-Many: User β†’ Unlimited Orders
  • Data changes independently: Product price changes, but order history stays same
  • Unbounded arrays: User β†’ Followers (can be millions)
  • Large documents: Embedding would exceed 16 MB limit

Example: Embed user's current shopping cart (small, accessed together). Reference user's past orders (unlimited, queried separately).

Q4 What is the maximum document size in MongoDB? Why? β–Ό

Answer:

Maximum document size is 16 MB.

Reasons for the limit:

  • Memory Efficiency: Large documents consume more RAM during queries. 16 MB keeps memory usage reasonable.
  • Network Performance: Huge documents slow down network transfer. 16 MB is good balance.
  • Encourages Good Design: Forces you to use references for large/unbounded data instead of embedding everything.

What if you need more?

  • Use GridFS for files > 16 MB (images, videos, large files)
  • Split large arrays into separate collection with references
  • Store large text in external storage (S3) and keep URL in document

Note: 16 MB is huge! That's ~16,000 pages of text. Most documents are < 1 KB.

Q5 What is ObjectId? Explain its structure. β–Ό

Answer:

ObjectId is a 12-byte unique identifier automatically generated by MongoDB for each document's _id field.

Structure (12 bytes total):

  • 4 bytes: Timestamp (seconds since Unix epoch) - Allows sorting by creation time!
  • 5 bytes: Random value (unique per machine and process)
  • 3 bytes: Incrementing counter

Example: ObjectId("507f1f77bcf86cd799439011")

Benefits:

  • Globally Unique: No collisions even across distributed systems
  • No coordination needed: Generated locally, no central server
  • Sortable by time: First 4 bytes = timestamp, so sorting by _id = sorting by creation time
  • Lightweight: Only 12 bytes vs UUIDs which are 16 bytes

Extract timestamp: ObjectId("...").getTimestamp() gives you document creation time!

Q6 How does MongoDB handle schema changes? β–Ό

Answer:

MongoDB is schema-less (flexible schema), making schema evolution very easy.

How it works:

  • No Migration Needed: Just start inserting documents with new fields. No ALTER TABLE!
  • Backward Compatible: Old documents without new fields continue to work
  • Forward Compatible: New code can handle old documents (use defaults for missing fields)

Example Scenario:

Old documents: { name: "John", email: "..." }

Add phone field: Just insert { name: "Alice", email: "...", phone: "..." }

Query old docs: Return phone = null or default value in code

Best Practice: Handle missing fields in application code with default values. No database migration needed!

Optional Validation: MongoDB 3.6+ supports schema validation if you want to enforce structure.