MongoDB: A Beginner's Guide to NoSQL Databases
MongoDB is the most popular NoSQL database today. Unlike SQL databases with rigid tables and predefined schemas, MongoDB uses a flexible document model that maps naturally to objects in your code. If you have ever wished your database schema could evolve as fast as your application, MongoDB is worth understanding.
The Document Model
MongoDB stores data as documents in collections — the equivalent of rows and tables in SQL. Each document is a JSON-like structure (BSON) with key-value pairs.
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "Alice Chen",
"email": "alice@example.com",
"role": "engineer",
"skills": ["JavaScript", "Python", "MongoDB"],
"address": {
"city": "San Francisco",
"state": "CA"
}
---The key difference from SQL: documents in the same collection do not need the same fields. One user document might have an address field while another does not. Arrays and nested objects are first-class citizens — no separate join tables for a user’s skills.
How This Compares to SQL
| SQL (Relational) | MongoDB (Document) |
|---|---|
| Table | Collection |
| Row | Document |
| Column | Field |
| JOINs | Embedded documents or references |
Embedding related data in a single document eliminates JOINs and makes reads faster. Instead of a users table joined to an addresses table, embed the address directly in the user document.
CRUD Operations
Create
// Insert a single document
db.users.insertOne({
name: "Bob",
email: "bob@example.com",
role: "designer"
---)
// Insert multiple documents
db.users.insertMany([
{ name: "Carol", role: "engineer" },
{ name: "Dave", role: "product" }
])Read
// Find all documents
db.users.find()
// Find with filter
db.users.find({ role: "engineer" })
// Find one document
db.users.findOne({ email: "bob@example.com" })
// Projection (select specific fields)
db.users.find(
{ role: "engineer" },
{ name: 1, email: 1, _id: 0 }
)Update
// Update one document
db.users.updateOne(
{ email: "bob@example.com" },
{ $set: { role: "senior designer" } }
)
// Update many documents
db.users.updateMany(
{ role: "engineer" },
{ $set: { department: "Engineering" } }
)
// Replace a document entirely
db.users.replaceOne(
{ email: "bob@example.com" },
{ name: "Bob Smith", email: "bob@example.com", role: "designer" }
)Delete
// Delete one document
db.users.deleteOne({ email: "bob@example.com" })
// Delete many documents
db.users.deleteMany({ role: "intern" })
// Drop entire collection
db.users.drop()Indexing
Indexes make queries fast. Without an index, MongoDB scans every document in a collection (a collection scan), which becomes prohibitively slow as data grows.
// Create a single-field index
db.users.createIndex({ email: 1 })
// Create a compound index
db.users.createIndex({ role: 1, name: 1 })
// Create a text index for search
db.users.createIndex({ name: "text", bio: "text" })
// List all indexes
db.users.getIndexes()Index Types
Single-field index: Most common. Speeds up queries on one field.
Compound index: Speeds up queries on multiple fields. Order matters — it supports queries on role and role + name but not name alone.
Text index: Full-text search across string fields. Faster than regex queries on large collections.
TTL index: Auto-deletes documents after a specified time. Perfect for session data or logs.
Geospatial index: Supports location-based queries like $near and $geoWithin for proximity searches.
Explaining a Query
db.users.find({ role: "engineer" }).explain("executionStats")This shows whether MongoDB used an index or performed a collection scan, how many documents it examined, and how long the query took. Always run this on slow queries before adding indexes.
The Aggregation Pipeline
The aggregation pipeline is MongoDB’s equivalent of SQL GROUP BY, JOINs, and complex transformations. It processes documents through a series of stages.
db.orders.aggregate([
// Stage 1: Filter
{ $match: { status: "completed" } },
// Stage 2: Group and calculate
{ $group: {
_id: "$customerId",
totalSpent: { $sum: "$total" },
orderCount: { $sum: 1 }
}},
// Stage 3: Sort
{ $sort: { totalSpent: -1 } },
// Stage 4: Limit
{ $limit: 10 }
])Common Pipeline Stages
| Stage | Purpose | SQL Equivalent |
|---|---|---|
$match | Filter documents | WHERE |
$group | Group by a field, apply aggregations | GROUP BY |
$sort | Order results | ORDER BY |
$project | Reshape documents (include/exclude fields) | SELECT |
$lookup | Join with another collection | LEFT JOIN |
$unwind | Deconstruct an array into multiple documents | UNNEST |
$limit | Limit the number of documents | LIMIT |
The aggregation pipeline is powerful but can be slow on large datasets without proper indexes. Always put $match as early as possible in the pipeline to reduce the number of documents entering subsequent stages.
Replication and High Availability
MongoDB provides high availability through replica sets — a group of mongod processes that maintain the same dataset:
- Primary — receives all write operations
- Secondaries — replicate data from the primary via oplog
- Arbiter — participates in elections but does not store data (useful for odd-numbered voting configurations)
// Check replica set status
rs.status()
// Step down primary manually
rs.stepDown(60)If the primary fails, the replica set holds an election to promote a secondary. Automatic failover typically completes within seconds. For production, always use a replica set with at least three members.
Sharding for Horizontal Scaling
When a single server cannot handle the workload, MongoDB shards data across multiple nodes:
- Shards — store the actual data (can be replica sets)
- Config servers — store metadata and shard configuration
- Mongos — routing service that directs queries to the correct shard
Choose a shard key carefully. A good shard key distributes writes evenly across shards. Common choices include hashed _id keys or high-cardinality fields like user_id. Avoid monotonically increasing shard keys (like timestamp) because they concentrate all writes on the last shard.
// Enable sharding on a database
sh.enableSharding("ecommerce")
// Shard a collection by hashed _id
sh.shardCollection("ecommerce.orders", { _id: "hashed" })
// Check sharding status
sh.status()When to Choose MongoDB Over SQL
Choose MongoDB when: your schema evolves frequently, you work with nested or array data, you need fast writes at scale, or you are prototyping.
Choose SQL when: your data is highly relational (invoices, payments, inventory), you need robust multi-row transactions, or you rely on SQL BI tools and ORMs.
The hybrid approach: Many applications use both. Store user profiles and product catalogs in MongoDB. Store financial transactions and reporting data in PostgreSQL.
Schema Design Best Practices
Embed unless there is a reason not to. Embedding related data in a single document gives you the best read performance. Only reference (use an ID to link) when the embedded data would cause the document to exceed MongoDB’s 16MB limit or when the related data needs to be queried independently.
Use the _id field wisely. MongoDB automatically generates ObjectId values for _id, but you can use any unique value. Using a natural unique key (like email or username) can simplify your queries and reduce index usage.
Avoid unbounded arrays. An array that grows without limit (like a comments array on a blog post) can cause performance problems as the document size increases. Paginate or use a separate collection for data that grows indefinitely.
Design for your access patterns. Before writing the schema, list every query your application needs. Then design the schema to answer those queries efficiently, even if it means duplicating data across documents. Data duplication is acceptable in MongoDB; expensive JOINs are not.
Related: Check our SQL vs NoSQL overview and database indexing guide.
FAQ
Is MongoDB free to use? Yes, MongoDB is open source under the Server Side Public License (SSPL). MongoDB Atlas offers a free tier (512MB storage) for hosted deployments. Community Server is free for self-hosted installations. Enterprise features (LDAP, auditing, encryption) require a subscription.
How does MongoDB handle transactions? MongoDB added multi-document ACID transaction support in version 4.0 (replica sets) and 4.2 (sharded clusters). Transactions work similarly to SQL — start a session, execute operations, commit or abort. However, transaction overhead is higher than individual document operations, so use them only when atomicity across documents is truly required.
What is the 16MB document size limit? MongoDB enforces a 16MB limit on individual documents. This prevents excessive memory consumption and ensures efficient replication. If your data exceeds 16MB, use a referencing pattern (split across multiple documents) or GridFS for large binary files.
When should I use MongoDB vs PostgreSQL? MongoDB excels at rapid development with flexible schemas, nested data, and horizontal scaling. PostgreSQL excels at relational integrity, complex queries with joins, and mature transactional guarantees. Many teams start with PostgreSQL and add MongoDB for specific use cases like catalogs, content management, or real-time analytics.
How do I migrate from SQL to MongoDB? Analyze your access patterns first. Embed related data that is read together. Denormalize where it avoids joins. Convert JOINs into embedded documents or references. Use the aggregation pipeline for GROUP BY queries. Test with production-scale data before cutting over — schema design that works for 10K documents may fail at 10M.