Section 5: MongoDB Basics

πŸ” Query Operators: Your Database Superpowers

Master the art of filtering and querying data like a pro - with stories, animations, and real-world examples

πŸ“– The Tale of the Library Search Master

Imagine you're the librarian of the world's largest library with 1 million books. A student walks in and says:

"I need all books about programming, published after 2020, with more than 300 pages, written by authors whose name starts with 'J', and available in English or Spanish."

Without query operators: You'd have to manually check all 1 million books! πŸ“šπŸ“šπŸ“š That would take YEARS!

With query operators: You wave your magic wand (MongoDB query) and in 0.5 seconds, you get exactly 23 books that match ALL those criteria! ✨

πŸ’‘ That's the Power of Query Operators!

Query operators are like superpowers that let you search, filter, and find exactly what you need from millions of documents in milliseconds! They're the secret weapon that makes MongoDB incredibly powerful! πŸš€

In this guide, you'll learn ALL the query operators and become a Search Master yourself! Let's dive in! πŸŠβ€β™‚οΈ

🎯 Query Operator Categories

MongoDB has 4 main categories of query operators. Think of them as different tools in your search toolbox! 🧰

πŸ”’

Comparison

Compare values

$eq, $ne, $gt, $gte, $lt, $lte, $in, $nin
🧠

Logical

Combine conditions

$and, $or, $not, $nor
🎨

Element

Check existence/type

$exists, $type
πŸ“¦

Array

Query arrays

$all, $elemMatch, $size

πŸ”’ Comparison Operators: The Number Crunchers

Comparison operators let you compare values - think of them as the "greater than", "less than", "equal to" you learned in math class! πŸ“

πŸ“Š Visual: How Comparison Works
Finding Users with age >= 25 πŸ“₯ INPUT: All Users (1000 documents) 20 18 30 25 22 35 ... and 994 more users COMPARISON FILTER { age: { $gte: 25 } } Checking: age >= 25 βœ— REJECTED age < 25 (650 users) βœ“ OUTPUT: Matched Users 30 25 35 28 ... 350 users total

$eq Equal To (Exact Match)

Finds documents where field value equals the specified value.

// Find users with age exactly 25
db.users.find({ age: { $eq: 25 } })
// Shorthand: db.users.find({ age: 25 })

// Find users in New York
db.users.find({ city: { $eq: "New York" } })

Use Case: Finding exact matches - specific ID, email, status, etc.

$ne Not Equal (Exclusion)

Finds documents where field value is NOT equal to the specified value.

// Find users NOT 25 years old
db.users.find({ age: { $ne: 25 } })

// Find users NOT from New York
db.users.find({ city: { $ne: "New York" } })

// Find active users (status not 'inactive')
db.users.find({ status: { $ne: "inactive" } })

Use Case: Excluding specific values - filter out deleted items, skip certain statuses.

$gt $gte Greater Than / Greater Than or Equal

$gt finds values strictly greater. $gte includes the boundary value.

// Find users older than 25 (excludes 25)
db.users.find({ age: { $gt: 25 } })  // 26, 27, 28...

// Find users 25 or older (includes 25)
db.users.find({ age: { $gte: 25 } })  // 25, 26, 27...

// Find products over $100
db.products.find({ price: { $gt: 100 } })

// Find products at least $100
db.products.find({ price: { $gte: 100 } })

Use Case: Age restrictions, price ranges, date filters (events after a date).

$lt $lte Less Than / Less Than or Equal

$lt finds values strictly less. $lte includes the boundary value.

// Find users younger than 25 (excludes 25)
db.users.find({ age: { $lt: 25 } })  // 18, 19, 24...

// Find users 25 or younger (includes 25)
db.users.find({ age: { $lte: 25 } })  // 18, 19, 25

// COMBINE for range: users between 18 and 30
db.users.find({ 
  age: { $gte: 18, $lte: 30 }  // 18 <= age <= 30
})

Use Case: Budget limits, age restrictions, date ranges (events before a date).

$in In Array (Match ANY)

Finds documents where field value matches ANY value in the specified array.

// Find users from NYC, LA, or Chicago
db.users.find({ 
  city: { $in: ["New York", "Los Angeles", "Chicago"] }
})

// Find products with specific IDs
db.products.find({ 
  _id: { $in: [101, 102, 103] }
})

// Find users with status 'active' OR 'pending'
db.users.find({ 
  status: { $in: ["active", "pending"] }
})

Use Case: Multiple choice filters - cities, categories, IDs, statuses.

$nin Not In Array (Exclude ALL)

Finds documents where field value does NOT match ANY value in the array.

// Find users NOT from NYC, LA, or Chicago
db.users.find({ 
  city: { $nin: ["New York", "Los Angeles", "Chicago"] }
})

// Exclude deleted and archived orders
db.orders.find({ 
  status: { $nin: ["deleted", "archived", "cancelled"] }
})

Use Case: Blacklisting values - exclude specific categories, regions, statuses.

🧠 Logical Operators: The Combiners

Logical operators let you combine multiple conditions - like saying "this AND that" or "this OR that"! πŸ”—

$and ALL Conditions Must Be True

Finds documents that match ALL specified conditions (intersection).

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

// Implicit $and (more common)
db.users.find({ 
  age: { $gte: 25, $lte: 35 },
  city: "New York"
})

// Explicit $and needed when same field appears twice
db.users.find({ 
  $and: [
    { age: { $ne: 25 } },
    { age: { $exists: true } }
  ]
})

Use Case: Multiple requirements - "premium users from NYC" or "products in stock AND under $50".

$or ANY Condition Can Be True

Finds documents that match AT LEAST ONE of the specified conditions (union).

// Find users from NYC OR LA
db.users.find({ 
  $or: [
    { city: "New York" },
    { city: "Los Angeles" }
  ]
})

// Find premium users OR users with 100+ posts
db.users.find({ 
  $or: [
    { isPremium: true },
    { postCount: { $gte: 100 } }
  ]
})

// Complex: (age < 18 OR age > 65) AND city = NYC
db.users.find({ 
  $or: [
    { age: { $lt: 18 } },
    { age: { $gt: 65 } }
  ],
  city: "New York"
})

Use Case: Alternative conditions - "VIP OR spent $1000+" or "email OR phone required".

$not Inverts the Condition

Negates the effect of a query expression.

// Find users NOT aged 25-35
db.users.find({ 
  age: { $not: { $gte: 25, $lte: 35 } }
})

// Find users whose name does NOT start with 'J'
db.users.find({ 
  name: { $not: /^J/ }
})

// Find products NOT in price range $50-$100
db.products.find({ 
  price: { $not: { $gte: 50, $lte: 100 } }
})

Use Case: Negating complex conditions - "not in this range", "doesn't match pattern".

$nor NONE of the Conditions Are True

Finds documents that fail ALL specified conditions (neither this nor that).

// Find users who are NOT from NYC and NOT from LA
db.users.find({ 
  $nor: [
    { city: "New York" },
    { city: "Los Angeles" }
  ]
})

// Find users who are NOT premium AND NOT active
db.users.find({ 
  $nor: [
    { isPremium: true },
    { status: "active" }
  ]
})

Use Case: Excluding multiple conditions - "not this and not that".

🎨 Element Operators: The Existence Checkers

Element operators check if fields exist or what type they are - like asking "Does this field exist?" or "Is this a number?" πŸ•΅οΈ

$exists Check if Field Exists

Finds documents based on whether a field exists or not.

// Find users who have a phone number
db.users.find({ phone: { $exists: true } })

// Find users who DON'T have a phone number
db.users.find({ phone: { $exists: false } })

// Find products with discount field
db.products.find({ discount: { $exists: true } })

// Find incomplete user profiles (missing email)
db.users.find({ email: { $exists: false } })

Use Case: Data validation - finding incomplete records, optional fields, migration cleanup.

$type Check Field Data Type

Finds documents where field is of the specified BSON type.

// Find users where age is a number
db.users.find({ age: { $type: "number" } })
// or: db.users.find({ age: { $type: 1 } })

// Find users where phone is a string
db.users.find({ phone: { $type: "string" } })

// Find documents where _id is ObjectId
db.users.find({ _id: { $type: "objectId" } })

// Common BSON types:
// "double" (1), "string" (2), "object" (3), 
// "array" (4), "objectId" (7), "bool" (8), 
// "date" (9), "null" (10), "int" (16), "long" (18)

Use Case: Type validation - finding data with wrong types, schema enforcement.

πŸ“¦ Array Operators: The List Masters

Array operators work with arrays (lists) - checking if arrays contain certain elements or matching specific patterns! πŸ“‹

$all Array Contains ALL Elements

Finds documents where array contains ALL specified elements (in any order).

// Find users who have BOTH "coding" and "music" hobbies
db.users.find({ 
  hobbies: { $all: ["coding", "music"] }
})

// Find products with ALL required tags
db.products.find({ 
  tags: { $all: ["electronics", "sale", "featured"] }
})

// Order doesn't matter - this matches:
// hobbies: ["music", "gaming", "coding"]
// hobbies: ["coding", "reading", "music", "travel"]

Use Case: Required skills - "must have Python AND React", required features - "must have all tags".

$elemMatch Array Element Matches Multiple Conditions

Finds documents where at least ONE array element matches ALL specified conditions.

// Find users with a score >= 80 AND <= 100
db.students.find({ 
  scores: { $elemMatch: { $gte: 80, $lte: 100 } }
})

// Find orders with item quantity > 10 AND price < 50
db.orders.find({ 
  items: { 
    $elemMatch: { 
      quantity: { $gt: 10 }, 
      price: { $lt: 50 } 
    }
  }
})

// Find users with comments that have likes > 100
db.users.find({ 
  comments: { 
    $elemMatch: { 
      likes: { $gt: 100 },
      status: "approved"
    }
  }
})

Use Case: Complex array queries - orders with specific item conditions, users with qualifying posts.

$size Array Has Exact Length

Finds documents where array has exactly the specified number of elements.

// Find users with exactly 3 hobbies
db.users.find({ hobbies: { $size: 3 } })

// Find orders with exactly 5 items
db.orders.find({ items: { $size: 5 } })

// Find users with no friends (empty array)
db.users.find({ friends: { $size: 0 } })

// Note: $size doesn't accept ranges
// To find arrays with size > 3, use:
db.users.find({ hobbies.3: { $exists: true } })

Use Case: Exact counts - "users with 3 addresses", "orders with 1 item", validation.

πŸ”€ Regular Expressions: The Pattern Matchers

Regular expressions (regex) let you search for patterns in text - like "starts with", "ends with", "contains"! 🎯

$regex Pattern Matching

Matches documents where field value matches the specified regular expression pattern.

// Find users whose name starts with "J"
db.users.find({ name: { $regex: /^J/ } })
// or: db.users.find({ name: /^J/ })

// Find users whose email ends with "@gmail.com"
db.users.find({ email: { $regex: /@gmail\.com$/ } })

// Case-insensitive search for "john"
db.users.find({ 
  name: { $regex: /john/i }
})

// Find products containing "phone" (case-insensitive)
db.products.find({ 
  name: { $regex: "phone", $options: "i" }
})

// Common patterns:
// ^abc   - starts with "abc"
// abc$   - ends with "abc"
// ^abc$  - exact match "abc"
// abc    - contains "abc"
// [a-z]  - any lowercase letter
// [0-9]  - any digit
// .      - any character
// .*     - any characters (0 or more)

Use Case: Text search - autocomplete, "starts with" filters, email domain checks, partial matching.

⚠️ Performance Warning

Regex queries can be SLOW on large collections without proper indexes! Use anchored patterns (starting with ^) for better performance, and consider text indexes for complex searches.

πŸ’» Live Query Console - Try It Yourself!

Practice query operators with a real sample database. Watch the animated query flow in action! πŸš€

πŸ“Š Sample Database: users collection

πŸ‘€ Alice
Age: 25
City: NYC
Hobbies: [coding, reading]
Premium: true
πŸ‘€ Bob
Age: 32
City: LA
Hobbies: [gaming, music]
Premium: false
πŸ‘€ Charlie
Age: 28
City: Chicago
Hobbies: [coding, music, yoga]
Premium: true
πŸ‘€ Diana
Age: 35
City: NYC
Hobbies: [reading, yoga]
Premium: false
πŸ‘€ Eve
Age: 22
City: Boston
Hobbies: [gaming]
Premium: false

πŸ“ Try These Example Queries:

MongoDB Query Console
● Ready
πŸ“₯ Query Input
πŸ’‘ Tip: Press Ctrl+Enter to run query
πŸ“€ Query Results
Run a query to see results...

🌍 Real-World Query Scenarios

πŸ›’

E-commerce Product Search

Comparison + Logical

Requirement: Find laptops priced between $500-$1500, in stock, with at least 8GB RAM, from brands Apple, Dell, or HP.

db.products.find({
  category: "laptop",
  price: { $gte: 500, $lte: 1500 },
  stock: { $gt: 0 },
  specs: { 
    $elemMatch: { 
      name: "RAM", 
      value: { $gte: 8 } 
    }
  },
  brand: { $in: ["Apple", "Dell", "HP"] }
})
πŸ”

User Profile Validation

Element + Logical

Requirement: Find incomplete profiles - users who are active but missing either email verification OR phone number.

db.users.find({
  status: "active",
  $or: [
    { emailVerified: { $ne: true } },
    { phone: { $exists: false } }
  ]
})
πŸ“±

Content Moderation

Array + Regex

Requirement: Find posts with more than 100 likes, containing word "giveaway" (case-insensitive), and tagged with "contest" or "prize".

db.posts.find({
  likes: { $gt: 100 },
  content: { $regex: /giveaway/i },
  tags: { $in: ["contest", "prize"] }
})
πŸ“Š

User Segmentation

Complex Multi-Operator

Requirement: Find "power users" - registered over 1 year ago, have premium OR made 50+ posts, from US/UK/Canada, and still active.

db.users.find({
  registeredAt: { 
    $lt: new Date(Date.now() - 365*24*60*60*1000) 
  },
  $or: [
    { isPremium: true },
    { postCount: { $gte: 50 } }
  ],
  country: { $in: ["USA", "UK", "Canada"] },
  status: "active",
  lastLogin: { 
    $gte: new Date(Date.now() - 30*24*60*60*1000) 
  }
})

❓ Interview Questions & Answers

Q1 What's the difference between $in and $all operators? β–Ό

Answer:

$in checks if field value matches ANY value in the array (OR logic):

// Find users from NYC OR LA
db.users.find({ city: { $in: ["NYC", "LA"] } })

$all checks if array field contains ALL specified values (AND logic):

// Find users who have BOTH "coding" AND "music" hobbies
db.users.find({ hobbies: { $all: ["coding", "music"] } })

Key Difference:

  • $in: Works on single-value fields, matches if value equals ANY in the array
  • $all: Works on array fields, matches if array contains ALL specified elements

Use $in for: Multiple choice filters (cities, categories, IDs)

Use $all for: Required skills, must-have tags, complete sets

Q2 When should you use explicit $and vs implicit $and? β–Ό

Answer:

Implicit $and (most common - preferred when possible):

// Multiple conditions on different fields
db.users.find({ 
  age: { $gte: 25 },
  city: "NYC",
  status: "active"
})

Explicit $and (required in specific cases):

// When same field needs multiple operator conditions
db.users.find({ 
  $and: [
    { age: { $ne: 25 } },
    { age: { $exists: true } }
  ]
})

// When you need multiple conditions on same field
db.products.find({ 
  $and: [
    { price: { $gte: 100 } },
    { price: { $lt: 1000 } }
  ]
})

When to use explicit $and:

  • Same field appears with different operators
  • Complex nested conditions requiring explicit grouping
  • Combining with $or for complex logic

Best Practice: Use implicit $and (simpler syntax) whenever possible. Only use explicit $and when syntactically required.

Q3 How do you query date ranges in MongoDB? β–Ό

Answer:

MongoDB stores dates as ISODate objects. Use comparison operators for date ranges:

Find documents after a specific date:

db.orders.find({ 
  createdAt: { $gte: new Date("2024-01-01") }
})

Find documents in a date range:

// Orders from January 2024
db.orders.find({ 
  createdAt: { 
    $gte: new Date("2024-01-01"),
    $lt: new Date("2024-02-01")
  }
})

Find documents from last 7 days:

const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);

db.posts.find({ 
  publishedAt: { $gte: sevenDaysAgo }
})

Common Patterns:

  • Last N days: $gte: new Date(Date.now() - N*24*60*60*1000)
  • Specific year: $gte: "2024-01-01", $lt: "2025-01-01"
  • Before date: $lt: new Date("2024-01-01")

Pro Tip: Always use ISODate format or JavaScript Date objects. Create indexes on date fields for fast range queries!

Q4 How can you optimize regex queries for better performance? β–Ό

Answer:

Regex queries can be SLOW without optimization. Here are key strategies:

1. Use anchored patterns (starts with ^):

// FAST - can use index
db.users.find({ name: /^John/ })

// SLOW - cannot use index efficiently
db.users.find({ name: /John/ })

2. Create text indexes for complex searches:

// Create text index
db.products.createIndex({ description: "text" })

// Use text search instead of regex
db.products.find({ $text: { $search: "laptop gaming" } })

3. Case-insensitive optimization:

// SLOW - case-insensitive regex
db.users.find({ email: /[email protected]/i })

// FASTER - store lowercase field
db.users.find({ emailLower: "[email protected]" })

4. Avoid wildcards at the beginning:

// BAD - starts with wildcard (very slow)
db.products.find({ name: /.*phone.*/ })

// BETTER - if possible, use prefix
db.products.find({ name: /^iPhone/ })

Performance Tips:

  • Anchor patterns with ^ for index usage
  • Use text indexes for full-text search
  • Store normalized (lowercase) versions for case-insensitive searches
  • Consider external search engines (Elasticsearch) for complex text search
Q5 Can you use multiple operators on the same field? How? β–Ό

Answer:

Yes! You can combine multiple operators on the same field using two approaches:

1. Implicit AND (single object):

// Find users aged 25-35 (both conditions must be true)
db.users.find({ 
  age: { $gte: 25, $lte: 35 }
})

// Find products priced $50-$500, in stock
db.products.find({ 
  price: { $gte: 50, $lte: 500 },
  stock: { $gt: 0, $lte: 100 }
})

2. Explicit $and (when needed):

// When combining conditions that can't be in same object
db.users.find({ 
  $and: [
    { age: { $ne: 25 } },
    { age: { $exists: true } }
  ]
})

Common Combinations:

  • Range: { $gte: min, $lte: max }
  • Exclude & Check Type: $and: [{ $ne: value }, { $type: "string" }]
  • Exists & Not Equal: $and: [{ $exists: true }, { $ne: null }]

Important: All operators in the same object are implicitly AND-ed together. This is the most common and cleanest syntax for range queries!

Q6 What's the difference between $exists: false and checking for null? β–Ό

Answer:

These check for DIFFERENT things in MongoDB:

$exists: false - Field doesn't exist at all:

// Matches documents where "phone" field is missing
db.users.find({ phone: { $exists: false } })

// Matches: { name: "John" }
// Does NOT match: { name: "John", phone: null }

Checking for null - Field exists but value is null:

// Matches documents where "phone" is explicitly null
db.users.find({ phone: null })

// Matches: { name: "John", phone: null }
// ALSO matches: { name: "John" } (missing field!)

// To check ONLY for null (not missing):
db.users.find({ 
  phone: null,
  phone: { $exists: true }
})

Key Differences:

  • $exists: false: Field not in document
  • value: null: Field exists, value is null OR field missing
  • Both null and exists: Field exists AND is null (not missing)

Use Cases:

  • $exists: false: Finding incomplete records, data validation, migration checks
  • null check: Finding explicitly unset values, cleared fields