MongoDB¶
MongoDB is a popular NoSQL document database that stores data in flexible, JSON-like documents.
Installation¶
Using Docker¶
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
mongo:latest
Local Installation¶
# Ubuntu/Debian
sudo apt-get install mongodb
# macOS
brew tap mongodb/brew
brew install mongodb-community
Connection¶
Using MongoDB Driver (Node.js)¶
const { MongoClient } = require('mongodb');
const uri = 'mongodb://admin:password@localhost:27017';
const client = new MongoClient(uri);
async function connect() {
await client.connect();
const db = client.db('mydatabase');
return db;
}
Using Mongoose (ORM)¶
const mongoose = require('mongoose');
await mongoose.connect('mongodb://admin:password@localhost:27017/mydatabase');
CRUD Operations¶
Create Documents¶
const users = db.collection('users');
// Insert one
await users.insertOne({
name: 'John Doe',
email: 'john@example.com',
age: 30
});
// Insert many
await users.insertMany([
{ name: 'Jane Doe', email: 'jane@example.com', age: 25 },
{ name: 'Bob Smith', email: 'bob@example.com', age: 35 }
]);
Read Documents¶
// Find one
const user = await users.findOne({ email: 'john@example.com' });
// Find many
const allUsers = await users.find({}).toArray();
// Find with filter
const adults = await users.find({ age: { $gte: 18 } }).toArray();
// Find with projection (select specific fields)
const names = await users.find({}, { projection: { name: 1, _id: 0 } }).toArray();
Update Documents¶
// Update one
await users.updateOne(
{ email: 'john@example.com' },
{ $set: { age: 31 } }
);
// Update many
await users.updateMany(
{ age: { $lt: 18 } },
{ $set: { isMinor: true } }
);
// Upsert (update or insert)
await users.updateOne(
{ email: 'new@example.com' },
{ $set: { name: 'New User' } },
{ upsert: true }
);
Delete Documents¶
// Delete one
await users.deleteOne({ email: 'john@example.com' });
// Delete many
await users.deleteMany({ age: { $lt: 18 } });
Query Operators¶
Comparison Operators¶
// Equal
{ age: 30 }
// Greater than
{ age: { $gt: 18 } }
// Greater than or equal
{ age: { $gte: 18 } }
// Less than
{ age: { $lt: 65 } }
// In array
{ status: { $in: ['active', 'pending'] } }
// Not equal
{ status: { $ne: 'inactive' } }
Logical Operators¶
// AND
{ $and: [{ age: { $gte: 18 } }, { age: { $lt: 65 } }] }
// OR
{ $or: [{ status: 'active' }, { role: 'admin' }] }
// NOT
{ age: { $not: { $lt: 18 } } }
Array Operators¶
// Element in array
{ tags: 'javascript' }
// All elements in array
{ tags: { $all: ['javascript', 'nodejs'] } }
// Array size
{ tags: { $size: 3 } }
Indexing¶
Create Indexes¶
// Single field index
await users.createIndex({ email: 1 });
// Compound index
await users.createIndex({ lastName: 1, firstName: 1 });
// Unique index
await users.createIndex({ email: 1 }, { unique: true });
// Text index (for search)
await users.createIndex({ name: 'text', bio: 'text' });
List Indexes¶
Drop Index¶
Aggregation Pipeline¶
const results = await users.aggregate([
// Stage 1: Match documents
{ $match: { age: { $gte: 18 } } },
// Stage 2: Group by field
{ $group: {
_id: '$country',
averageAge: { $avg: '$age' },
count: { $sum: 1 }
}},
// Stage 3: Sort results
{ $sort: { count: -1 } },
// Stage 4: Limit results
{ $limit: 10 }
]).toArray();
Mongoose Schema¶
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
age: {
type: Number,
min: 0,
max: 120
},
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', userSchema);
// Usage
const user = new User({
name: 'John Doe',
email: 'john@example.com',
age: 30
});
await user.save();
Transactions¶
const session = client.startSession();
try {
await session.withTransaction(async () => {
await users.insertOne({ name: 'John' }, { session });
await orders.insertOne({ userId: 1, amount: 100 }, { session });
});
} finally {
await session.endSession();
}
Best Practices¶
Connection Management¶
// Create client once and reuse
let cachedClient = null;
async function connectToDatabase() {
if (cachedClient) {
return cachedClient;
}
const client = await MongoClient.connect(uri, {
maxPoolSize: 10
});
cachedClient = client;
return client;
}
Schema Design¶
- Embed related data that is accessed together
- Use references for data that is accessed independently
- Avoid deep nesting (keep it under 3-4 levels)
- Use arrays for one-to-few relationships
- Use references for one-to-many or many-to-many
Performance¶
// Use projection to limit returned fields
await users.findOne({ _id: 1 }, { projection: { name: 1, email: 1 } });
// Use lean() with Mongoose for faster queries
await User.find({}).lean();
// Create indexes on frequently queried fields
await users.createIndex({ email: 1 });