Introduction
MongoDB has become the defining database of the modern application stack. As the most popular NoSQL document database, MongoDB powers applications at companies ranging from startups to Fortune 500 enterprises. Its flexible document model, powerful query language, and scalable architecture make it an ideal choice for developers building modern applications.
This comprehensive guide takes you through MongoDB from fundamentals to advanced concepts. You will learn about the document model, CRUD operations, indexing strategies, and data modeling patterns that will help you build robust applications with MongoDB.
What is MongoDB?
MongoDB is a document-oriented NoSQL database that stores data in flexible, JSON-like documents. Unlike traditional relational databases with fixed schemas, MongoDB’s documents can vary in structure, allowing for dynamic and evolving data models.
Document Model
At the heart of MongoDB lies the document model. Documents are the basic unit of data in MongoDB, stored in a binary JSON format called BSON. Each document contains field-value pairs, where values can be strings, numbers, arrays, objects, or even binary data.
// A MongoDB document representing a user
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "john_doe",
"email": "[email protected]",
"age": 30,
"address": {
"street": "123 Main St",
"city": "New York",
"state": "NY",
"zip": "10001"
},
"tags": ["developer", "writer", "traveler"],
"created_at": ISODate("2026-01-15T10:30:00Z"),
"is_active": true
}
The _id field is automatically added to every document if not specified and acts as the primary key. It is unique within its collection and provides fast lookup performance.
Collections
Documents are organized into collections, which are analogous to tables in relational databases. However, unlike tables, collections do not enforce a rigid schema. This flexibility allows you to store documents with different structures within the same collection.
// Documents with different structures in the same collection
{ "name": "Product 1", "price": 29.99 }
{ "name": "Product 2", "price": 49.99, "category": "Electronics" }
{ "name": "Product 3", "price": 19.99, "features": ["wireless", "portable"], "in_stock": true }
Databases
Collections reside within databases. A MongoDB instance can host multiple databases, each serving a different application or purpose.
MongoDB Instance
โโโ admin (system database)
โโโ config (sharding configuration)
โโโ local (replication data)
โโโ myapp (application database)
โโโ users collection
โโโ products collection
โโโ orders collection
Installation and Setup
Getting MongoDB up and running is straightforward, with options for local development, containerized deployment, and cloud hosting.
Local Installation
For local development, you can install MongoDB Community Server directly on your machine.
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install gnupg curl
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor
echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] \
http://repo.mongodb.org/apt/debian bookworm/mongodb-org/8.0 main" | \
sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
# Start MongoDB
sudo systemctl start mongod
sudo systemctl enable mongod
Docker Installation
For quick setup and isolation, Docker provides an excellent option.
# Run MongoDB container
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
-v mongodb_data:/data/db \
mongo:latest
# Connect to MongoDB
docker exec -it mongodb mongosh -u admin -p password
MongoDB Atlas (Cloud)
For production workloads, MongoDB Atlas provides a fully managed cloud solution.
// Connect to Atlas cluster using Node.js driver
const { MongoClient } = require('mongodb');
const uri = 'mongodb+srv://<username>:<password>@cluster.mongodb.net/?retryWrites=true&w=majority';
const client = new MongoClient(uri);
async function connect() {
await client.connect();
const database = client.db('myapp');
const collection = database.collection('users');
// Perform operations
const user = await collection.findOne({ username: 'john_doe' });
console.log(user);
await client.close();
}
connect();
CRUD Operations
MongoDB provides a powerful set of operations for creating, reading, updating, and deleting documents.
Create Operations
Insert documents into collections using insertOne or insertMany.
// Insert a single document
db.users.insertOne({
username: 'jane_smith',
email: '[email protected]',
age: 28,
tags: ['designer', 'photographer']
})
// Insert multiple documents
db.products.insertMany([
{ name: 'Laptop', price: 999.99, category: 'Electronics' },
{ name: 'Mouse', price: 29.99, category: 'Electronics' },
{ name: 'Desk', price: 299.99, category: 'Furniture' }
])
Read Operations
Query documents using find or findOne.
// Find all documents
db.users.find()
// Find with filter
db.users.find({ age: { $gte: 25 } })
// Find single document
db.users.findOne({ username: 'john_doe' })
// Find with projection (only return specified fields)
db.users.find(
{ is_active: true },
{ username: 1, email: 1, _id: 0 }
)
// Find with array filters
db.orders.find({
items: { $elemMatch: { product_id: '123', quantity: { $gte: 2 } } }
})
// Find with text search
db.articles.find({ $text: { $search: 'mongodb tutorial' } })
Update Operations
Modify existing documents using updateOne, updateMany, or findOneAndUpdate.
// Update single document
db.users.updateOne(
{ username: 'john_doe' },
{ $set: { age: 31, 'address.city': 'Boston' } }
)
// Update multiple documents
db.products.updateMany(
{ category: 'Electronics' },
{ $mul: { price: 1.1 } } // Increase by 10%
)
// Add element to array
db.users.updateOne(
{ username: 'john_doe' },
{ $push: { tags: 'mentor' } }
)
// Remove element from array
db.users.updateOne(
{ username: 'john_doe' },
{ $pull: { tags: 'beginner' } }
)
// Increment a field
db.counters.updateOne(
{ _id: 'order_sequence' },
{ $inc: { sequence_value: 1 } }
)
// Update with upsert (insert if not exists)
db.products.updateOne(
{ sku: 'NEW-PRODUCT' },
{ $set: { name: 'New Product', price: 49.99 } },
{ upsert: true }
)
Delete Operations
Remove documents using deleteOne or deleteMany.
// Delete single document
db.users.deleteOne({ username: 'john_doe' })
// Delete multiple documents
db.users.deleteMany({ is_active: false })
// Delete all documents in collection
db.logs.deleteMany({})
Query Operators
MongoDB provides a comprehensive set of query operators for filtering and manipulating data.
Comparison Operators
// $eq - equals
db.products.find({ price: { $eq: 29.99 } })
// $ne - not equals
db.products.find({ category: { $ne: 'Electronics' } })
// $gt, $gte - greater than, greater than or equal
db.orders.find({ total: { $gt: 100, $lte: 500 } })
// $lt, $lte - less than, less than or equal
db.products.find({ stock: { $lte: 10 } })
// $in - matches any value in array
db.products.find({ category: { $in: ['Electronics', 'Gadgets'] } })
// $nin - matches none of the values in array
db.users.find({ role: { $nin: ['admin', 'moderator'] } })
Logical Operators
// $and - matches all conditions
db.users.find({
$and: [
{ age: { $gte: 18 } },
{ is_active: true }
]
})
// $or - matches any condition
db.products.find({
$or: [
{ category: 'Electronics' },
{ price: { $lt: 50 } }
]
})
// $not - inverts the expression
db.users.find({ age: { $not: { $gte: 18 } } })
// $nor - matches none of the conditions
db.users.find({
$nor: [
{ role: 'admin' },
{ is_active: false }
]
})
Element Operators
// $exists - checks if field exists
db.users.find({ email: { $exists: true } })
// $type - checks BSON data type
db.products.find({ price: { $type: 'number' } })
// $regex - pattern matching
db.users.find({ email: { $regex: /@gmail\.com$/ } })
Array Operators
// $all - contains all elements
db.products.find({ tags: { $all: ['wireless', 'portable'] } })
// $elemMatch - matches array element
db.scores.find({
scores: { $elemMatch: { subject: 'Math', score: { $gte: 90 } } }
})
// $size - array length
db.users.find({ tags: { $size: 3 } })
Indexes
Indexes are crucial for query performance. MongoDB supports various index types to optimize different query patterns.
Creating Indexes
// Create single-field index
db.users.createIndex({ email: 1 })
// Create compound index (order matters)
db.products.createIndex({ category: 1, price: -1 })
// Create unique index
db.users.createIndex({ username: 1 }, { unique: true })
// Create sparse index (only indexes documents with the field)
db.employees.createIndex({ social_security_number: 1 }, { sparse: true })
// Create TTL index (auto-delete after expiration)
db.sessions.createIndex({ created_at: 1 }, { expireAfterSeconds: 3600 })
// Create text index
db.articles.createIndex({ title: 'text', content: 'text' })
// Create geospatial index
db.places.createIndex({ location: '2dsphere' })
Index Management
// List all indexes
db.users.getIndexes()
// Drop an index
db.users.dropIndex('email_1')
// Drop all indexes (except _id)
db.users.dropIndexes()
// Hint - force use of specific index
db.products.find({ category: 'Electronics' }).hint({ category: 1 })
Index Performance
Use explain() to analyze query performance.
// Analyze query execution
db.users.find({ email: '[email protected]' }).explain('executionStats')
// Covered query (uses only index, no document fetch)
db.users.find(
{ email: '[email protected]' },
{ _id: 0, email: 1 }
).explain()
Data Modeling
MongoDB’s flexible schema allows for various data modeling patterns depending on your application’s needs.
Embedding vs Referencing
Choose between embedding related data directly or referencing separate collections.
// Embedding - store related data in single document
{
username: 'john_doe',
address: {
street: '123 Main St',
city: 'New York',
zip: '10001'
}
}
// Referencing - store related data in separate collection
// users collection
{ _id: 1, username: 'john_doe', address_id: 101 }
// addresses collection
{ _id: 101, street: '123 Main St', city: 'New York', zip: '10001' }
Common Patterns
// One-to-Many with embedding
{
customer_id: 1,
name: 'John Doe',
orders: [
{ order_id: 101, date: '2026-01-01', total: 150.00 },
{ order_id: 102, date: '2026-01-15', total: 75.00 }
]
}
// One-to-Many with referencing
// customer document
{ _id: 1, name: 'John Doe' }
// orders collection
{ customer_id: 1, order_id: 101, date: '2026-01-01', total: 150.00 }
{ customer_id: 1, order_id: 102, date: '2026-01-15', total: 75.00 }
// Many-to-Many with referencing
// students collection
{ _id: 1, name: 'Alice', courses: [101, 102] }
// courses collection
{ _id: 101, name: 'Math' }
{ _id: 102, name: 'Science' }
// enrollments collection (junction)
{ student_id: 1, course_id: 101, enrolled_date: '2026-01-01' }
Schema Validation
Enforce data quality with JSON Schema validation.
db.createCollection('users', {
validator: {
$jsonSchema: {
bsonType: 'object',
required: ['username', 'email'],
properties: {
username: {
bsonType: 'string',
description: 'must be a string and is required'
},
email: {
bsonType: 'string',
pattern: '^.+@.+\..+$',
description: 'must be a valid email'
},
age: {
bsonType: 'int',
minimum: 13,
maximum: 120
}
}
}
}
})
Aggregation Pipeline
The aggregation framework enables powerful data transformations and analytics.
Basic Pipeline
// Simple aggregation
db.orders.aggregate([
{ $match: { status: 'completed' } },
{ $group: { _id: '$customer_id', total: { $sum: '$total' } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
])
Common Stages
// $match - filter documents
{ $match: { status: 'active', age: { $gte: 18 } } }
// $group - group and aggregate
{
$group: {
_id: '$category',
count: { $sum: 1 },
avgPrice: { $avg: '$price' },
minPrice: { $min: '$price' },
maxPrice: { $max: '$price' }
}
}
// $project - reshape documents
{
$project: {
_id: 0,
name: 1,
totalValue: { $multiply: ['$price', '$quantity'] }
}
}
// $sort - order documents
{ $sort: { created_at: -1, name: 1 } }
// $limit and $skip - pagination
{ $skip: 20 }
{ $limit: 10 }
// $unwind - deconstruct arrays
{ $unwind: '$tags' }
// $lookup - join collections
{
$lookup: {
from: 'products',
localField: 'product_id',
foreignField: '_id',
as: 'product_details'
}
}
Operators in Aggregation
// Arithmetic
{
$project: {
total: { $add: ['$price', '$tax'] },
discount: { $multiply: ['$price', 0.1] }
}
}
// String
{
$project: {
username: { $toUpper: '$username' },
initials: { $substrCP: ['$name', 0, 2] }
}
}
// Date
{
$project: {
year: { $year: '$created_at' },
month: { $month: '$created_at' },
day: { $dayOfMonth: '$created_at' }
}
}
// Conditional
{
$project: {
discount_price: {
$cond: {
if: { $gte: ['$price', 100] },
then: { $multiply: ['$price', 0.9] },
else: '$price'
}
}
}
}
Transactions
MongoDB supports multi-document ACID transactions for operations requiring atomicity across multiple documents or collections.
// Start a session
const session = client.startSession();
session.withTransaction(async () => {
const accounts = client.db('bank').collection('accounts');
const transfers = client.db('bank').collection('transfers');
// Debit from source
await accounts.updateOne(
{ _id: 'account_a' },
{ $inc: { balance: -100 } },
{ session }
);
// Credit to destination
await accounts.updateOne(
{ _id: 'account_b' },
{ $inc: { balance: 100 } },
{ session }
);
// Record transfer
await transfers.insertOne({
from: 'account_a',
to: 'account_b',
amount: 100,
timestamp: new Date()
}, { session });
});
Best Practices
Follow these patterns for optimal MongoDB performance and maintainability.
Schema Design
Design your schema based on your application’s query patterns. Consider how data will be accessed and updated.
Indexing Strategy
Create indexes that support your most frequent queries. Avoid over-indexing, as each index impacts write performance.
Connection Management
Use connection pooling for production applications. Configure appropriate pool sizes based on your workload.
const client = new MongoClient(uri, {
maxPoolSize: 10,
minPoolSize: 5,
maxIdleTimeMS: 30000
});
Common Pitfalls
Avoid these frequent mistakes when working with MongoDB.
Unbounded Collections
Without proper indexing or pagination, queries can become slow as collections grow.
Deep Nesting
Avoid deeply nested documents that become difficult to query and update.
Large Arrays
Very large arrays can impact query performance and memory usage.
External Resources
Conclusion
MongoDB provides a powerful, flexible foundation for modern application development. Its document model aligns naturally with object-oriented programming, while its query language and aggregation framework enable sophisticated data processing.
This guide covered the fundamental concepts you need to get started with MongoDB: the document model, CRUD operations, query operators, indexing, data modeling, and aggregation. With these skills, you can build robust applications that leverage MongoDB’s strengths.
In the next article, we will explore MongoDB operations, including deployment, scaling, replication, and security configuration for production environments.
Comments