Section 5: MongoDB Basics

🎯 CRUD Operations: Create, Read, Update, Delete in MongoDB

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: "[email protected]",
  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! πŸš€

πŸ”„ CRUD Workflow

πŸ“
CREATE
Add new data
β†’
πŸ“–
READ
Retrieve data
β†’
✏️
UPDATE
Modify existing
β†’
πŸ—‘οΈ
DELETE
Remove data

πŸ“ CREATE Operations

Insert new documents into collections.

insertOne() - Add Single Document

db.users.insertOne({ name: "Alice Johnson", email: "alice@example.com", age: 25, city: "New York" })

Returns: { acknowledged: true, insertedId: ObjectId("...") }

insertMany() - Add Multiple Documents

db.users.insertMany([ { name: "Bob", email: "bob@ex.com", age: 30 }, { name: "Carol", email: "carol@ex.com", age: 28 }, { name: "Dave", email: "dave@ex.com", age: 35 } ])

πŸ’» Try CREATE Operations Live

// Click "Run" to see the result...

πŸ“– READ Operations

Query and retrieve documents from collections.

find() - Get Multiple Documents

// Find all users db.users.find() // Find users in New York db.users.find({ city: "New York" }) // Find users aged 25-35 db.users.find({ age: { $gte: 25, $lte: 35 } })

Query Operators

Comparison:
  • $eq - Equal
  • $ne - Not equal
  • $gt - Greater than
  • $gte - Greater or equal
  • $lt - Less than
  • $lte - Less or equal
Logical:
  • $and - All conditions
  • $or - Any condition
  • $not - Negate
  • $in - Match array
  • $nin - Not in array

πŸ’» Try READ Operations Live

// Click "Run" to see the result...

✏️ UPDATE Operations

Modify existing documents.

updateOne() - Update Single Document

db.users.updateOne( { email: "alice@example.com" }, { $set: { age: 26, city: "Boston" } } )

Update Operators

Common Operators:
  • $set - Set field value
  • $inc - Increment number
  • $push - Add to array
  • $pull - Remove from array
  • $unset - Delete field
// Increment age by 1 db.users.updateOne( { name: "Alice" }, { $inc: { age: 1 } } ) // Add hobby to array db.users.updateOne( { name: "Alice" }, { $push: { hobbies: "reading" } } )

πŸ’» Try UPDATE Operations Live

// Click "Run" to see the result...

πŸ—‘οΈ DELETE Operations

⚠️ Use carefully! Deletions are permanent.

deleteOne() - Delete Single Document

db.users.deleteOne({ email: "alice@example.com" })

deleteMany() - Delete Multiple Documents

// Delete all inactive users db.users.deleteMany({ isActive: false }) // ⚠️ NEVER do this - deletes EVERYTHING! db.users.deleteMany({})
Safety Tips:
  • Always test filter with find() first
  • Consider soft deletes (mark as deleted)
  • Take backups before bulk deletes
  • Require confirmation in production

πŸ’» Try DELETE Operations Live ⚠️ Simulated

// Click "Run" to see the result...
πŸ’» Live CRUD Console
● Connected
INPUT
OUTPUT
Click an example button above to start!

🌍 Real-World Document Examples

Example 1: User Profile (Social Media)

{
  _id: ObjectId("507f1f77bcf86cd799439011"),
  username: "johndoe",
  email: "[email protected]",
  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: "[email protected]"
  },
  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
}

βœ… 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.