| name | mongodb |
| description | MongoDB queries, aggregation, indexing, and administration. Use when user mentions "mongodb", "mongo", "mongosh", "mongoose", "nosql", "document database", "mongodb atlas", "aggregation pipeline", "mongodb query", "collection", "mongodb index", "replica set", "mongodb backup", "mongodump", or working with MongoDB databases. |
MongoDB
mongosh Basics
mongosh
mongosh "mongodb://localhost:27017/mydb"
mongosh "mongodb+srv://user:pass@cluster.mongodb.net/mydb"
mongosh --host rs0/host1:27017,host2:27017 --authenticationDatabase admin -u admin -p
show dbs
use mydb
show collections
db.stats()
db.collection.stats()
db.getCollectionNames()
db.dropDatabase()
db.createCollection("logs", { capped: true, size: 1048576, max: 1000 })
CRUD Operations
Insert
db.users.insertOne({ name: "Alice", email: "alice@example.com", age: 30 })
db.users.insertMany([
{ name: "Bob", email: "bob@example.com", age: 25 },
{ name: "Carol", email: "carol@example.com", age: 35 }
])
db.users.insertMany(docs, { ordered: false })
Find
db.users.find()
db.users.find({ age: { $gt: 25 } })
db.users.findOne({ email: "alice@example.com" })
db.users.find({ status: "active" }, { name: 1, email: 1, _id: 0 })
db.users.find().sort({ age: -1 }).limit(10).skip(20)
db.users.countDocuments({ status: "active" })
db.users.distinct("status")
Update
db.users.updateOne(
{ email: "alice@example.com" },
{ $set: { age: 31, updatedAt: new Date() } }
)
db.users.updateMany(
{ status: "inactive" },
{ $set: { archived: true }, $currentDate: { updatedAt: true } }
)
db.users.updateOne(
{ email: "dave@example.com" },
{ $set: { name: "Dave", age: 28 } },
{ upsert: true }
)
db.users.replaceOne(
{ _id: ObjectId("...") },
{ name: "Alice", email: "alice@new.com", age: 31 }
)
Delete
db.users.deleteOne({ email: "bob@example.com" })
db.users.deleteMany({ status: "inactive", lastLogin: { $lt: ISODate("2024-01-01") } })
db.users.findOneAndDelete({ email: "bob@example.com" })
Query Operators
Comparison and Logical
{ age: { $eq: 30 } }
{ age: { $gt: 25, $lte: 40 } }
{ status: { $in: ["active", "pending"] } }
{ status: { $nin: ["banned"] } }
{ $and: [{ age: { $gte: 18 } }, { status: "active" }] }
{ $or: [{ role: "admin" }, { age: { $gte: 21 } }] }
{ age: { $not: { $gt: 65 } } }
Element and Evaluation
{ phone: { $exists: true } }
{ age: { $type: "number" } }
{ name: { $regex: /^alice/i } }
{ bio: { $regex: "engineer", $options: "i" } }
{ score: { $mod: [10, 0] } }
Array Operators
{ tags: { $all: ["mongodb", "nosql"] } }
{ tags: { $size: 3 } }
{ results: { $elemMatch: { score: { $gt: 90 }, subject: "math" } } }
{ "scores.0": { $gt: 80 } }
Update Operators
{ $set: { status: "active" } }
{ $unset: { tempField: "" } }
{ $inc: { views: 1, score: -5 } }
{ $rename: { "old_field": "new_field" } }
{ $min: { lowScore: 50 } }
{ $max: { highScore: 100 } }
{ $mul: { price: 1.1 } }
{ $push: { tags: "new-tag" } }
{ $push: { scores: { $each: [90, 85], $sort: -1, $slice: 10 } } }
{ $addToSet: { tags: "unique-tag" } }
{ $pull: { tags: "old-tag" } }
{ $pull: { results: { score: { $lt: 50 } } } }
{ $pop: { queue: -1 } }
Aggregation Pipeline
db.orders.aggregate([
{ $match: { status: "completed", createdAt: { $gte: ISODate("2025-01-01") } } },
{ $group: {
_id: "$customerId",
totalSpent: { $sum: "$amount" },
orderCount: { $sum: 1 },
avgOrder: { $avg: "$amount" },
lastOrder: { $max: "$createdAt" }
}},
{ $sort: { totalSpent: -1 } },
{ $limit: 10 },
{ $project: {
customerId: "$_id",
totalSpent: 1,
orderCount: 1,
avgOrder: { $round: ["$avgOrder", 2] },
_id: 0
}}
])
$lookup (Join)
db.orders.aggregate([
{ $lookup: {
from: "users",
localField: "customerId",
foreignField: "_id",
as: "customer"
}},
{ $unwind: "$customer" },
{ $project: { amount: 1, "customer.name": 1, "customer.email": 1 } }
])
db.orders.aggregate([
{ $lookup: {
from: "products",
let: { productIds: "$items.productId" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$productIds"] } } },
{ $project: { name: 1, price: 1 } }
],
as: "productDetails"
}}
])
$facet (Multiple Aggregations in One Pass)
db.products.aggregate([
{ $facet: {
priceRanges: [
{ $bucket: { groupBy: "$price", boundaries: [0, 25, 50, 100, Infinity] } }
],
topRated: [
{ $sort: { rating: -1 } },
{ $limit: 5 },
{ $project: { name: 1, rating: 1 } }
],
totalCount: [
{ $count: "count" }
]
}}
])
Indexing
db.users.createIndex({ email: 1 })
db.users.createIndex({ email: 1 }, { unique: true })
db.orders.createIndex({ customerId: 1, createdAt: -1 })
db.articles.createIndex({ tags: 1 })
db.articles.createIndex({ title: "text", body: "text" })
db.articles.find({ $text: { $search: "mongodb aggregation" } })
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
db.orders.createIndex(
{ createdAt: -1 },
{ partialFilterExpression: { status: "active" } }
)
db.logs.createIndex({ "metadata.$**": 1 })
db.users.getIndexes()
db.users.dropIndex("email_1")
db.users.dropIndexes()
Schema Design Patterns
Embedding vs Referencing
{
_id: ObjectId("..."),
name: "Alice",
addresses: [
{ type: "home", street: "123 Main St", city: "Springfield" },
{ type: "work", street: "456 Corp Ave", city: "Shelbyville" }
]
}
{
_id: ObjectId("..."),
customerId: ObjectId("..."),
items: [
{ productId: ObjectId("..."), qty: 2, price: 29.99 }
]
}
Denormalization
Store computed or copied fields to avoid joins:
{ title: "My Post", authorId: ObjectId("..."), authorName: "Alice" }
Polymorphic Pattern
Single collection, different shapes distinguished by a type field:
{ type: "email", to: "alice@example.com", subject: "Welcome", body: "..." }
{ type: "sms", to: "+1234567890", message: "Your code is 1234" }
{ type: "push", deviceToken: "abc...", title: "New message", payload: { ... } }
Mongoose ODM (Node.js)
Schema and Model
const mongoose = require("mongoose");
await mongoose.connect("mongodb://localhost:27017/mydb");
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 },
role: { type: String, enum: ["user", "admin"], default: "user" },
profile: {
bio: String,
avatar: String
},
tags: [String],
createdAt: { type: Date, default: Date.now }
});
userSchema.virtual("isAdmin").get(function () {
return this.role === "admin";
});
userSchema.methods.toPublic = function () {
const { _id, name, email, role } = this.toObject();
return { id: _id, name, email, role };
};
userSchema.statics.findByEmail = function (email) {
return this.findOne({ email: email.toLowerCase() });
};
userSchema.pre("save", function (next) {
this.updatedAt = new Date();
next();
});
const User = mongoose.model("User", userSchema);
Queries with Mongoose
const users = await User.find({ role: "admin" }).sort({ name: 1 }).limit(10).lean();
const user = await User.findById(id).select("name email");
await User.findOneAndUpdate({ email }, { $inc: { loginCount: 1 } }, { new: true });
await User.deleteMany({ lastLogin: { $lt: cutoffDate } });
Populate (Reference Resolution)
const postSchema = new mongoose.Schema({
title: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
});
const Post = mongoose.model("Post", postSchema);
const posts = await Post.find().populate("author", "name email").lean();
const posts = await Post.find().populate({ path: "comments", populate: { path: "user" } });
lean()
lean() returns plain JS objects instead of Mongoose documents. Use for read-only queries -- skips hydration, significantly faster.
Transactions
const session = await mongoose.startSession();
session.startTransaction();
try {
await Account.updateOne({ _id: from }, { $inc: { balance: -amount } }, { session });
await Account.updateOne({ _id: to }, { $inc: { balance: amount } }, { session });
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
Transactions require a replica set (or sharded cluster). For local dev, start mongod with --replSet rs0 and run rs.initiate().
MongoDB Atlas
Connection
mongosh "mongodb+srv://cluster0.abc123.mongodb.net/mydb" --apiVersion 1 --username admin
mongoose.connect("mongodb+srv://admin:password@cluster0.abc123.mongodb.net/mydb?retryWrites=true&w=majority")
Key Settings
- Network Access: whitelist IP addresses or use
0.0.0.0/0 for dev (not production).
- Database Access: create users with specific roles (readWrite, atlasAdmin).
- Backups: Atlas provides continuous backups and point-in-time restore for M10+ clusters. Snapshots can be downloaded or restored to a new cluster.
Performance
explain()
db.orders.find({ customerId: ObjectId("...") }).explain("executionStats")
Key fields: totalDocsExamined vs nReturned (ratio should be close to 1:1), executionTimeMillis, winningPlan.stage (IXSCAN good, COLLSCAN bad), indexBounds.
Database Profiler
db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().sort({ ts: -1 }).limit(5)
db.setProfilingLevel(0)
Index Hints
db.orders.find({ status: "active" }).hint({ status: 1, createdAt: -1 })
db.orders.find({ status: "active" }).hint("status_1_createdAt_-1")
Backup and Restore
mongodump --uri="mongodb://localhost:27017/mydb" --out=/backup/$(date +%F)
mongodump --uri="mongodb://localhost:27017/mydb" --collection=users --out=/backup
mongodump --uri="mongodb://localhost:27017/mydb" --gzip --archive=backup.gz
mongorestore --uri="mongodb://localhost:27017" /backup/2025-04-13/
mongorestore --uri="mongodb://localhost:27017/mydb" --collection=users /backup/mydb/users.bson
mongorestore --uri="mongodb://localhost:27017" --gzip --archive=backup.gz
mongorestore --drop --uri="mongodb://localhost:27017" /backup/2025-04-13/
Replica Sets
Setup
mongod --replSet rs0 --port 27017 --dbpath /data/rs0-0
mongod --replSet rs0 --port 27018 --dbpath /data/rs0-1
mongod --replSet rs0 --port 27019 --dbpath /data/rs0-2
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
})
rs.status()
rs.conf()
Read Preference and Write Concern
db.users.find().readPref("secondaryPreferred")
"mongodb://host1,host2,host3/mydb?replicaSet=rs0&readPreference=secondaryPreferred"
db.users.insertOne({ name: "Alice" }, { writeConcern: { w: "majority", wtimeout: 5000 } })
Change Streams
const changeStream = db.collection("orders").watch();
changeStream.on("change", (change) => {
console.log(change.operationType, change.fullDocument);
});
const pipeline = [{ $match: { "fullDocument.status": "shipped" } }];
const changeStream = db.collection("orders").watch(pipeline, { fullDocument: "updateLookup" });
const changeStream = db.collection("orders").watch([], { resumeAfter: lastResumeToken });
const stream = Order.watch();
stream.on("change", (data) => { });
Requires replica set or sharded cluster. Use fullDocument: "updateLookup" to include the full document on update events.
Common Patterns
Cursor-Based Pagination
const pageSize = 20;
const firstPage = await db.orders.find().sort({ _id: -1 }).limit(pageSize).toArray();
const lastId = firstPage[firstPage.length - 1]._id;
const nextPage = await db.orders.find({ _id: { $lt: lastId } }).sort({ _id: -1 }).limit(pageSize).toArray();
Full-Text Search
db.articles.createIndex({ title: "text", body: "text" });
db.articles.find(
{ $text: { $search: "mongodb performance" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
db.articles.aggregate([
{ $search: { index: "default", text: { query: "mongodb performance", path: ["title", "body"] } } },
{ $project: { title: 1, score: { $meta: "searchScore" } } }
])
Geospatial Queries
db.places.insertOne({
name: "Central Park",
location: { type: "Point", coordinates: [-73.965, 40.782] }
})
db.places.createIndex({ location: "2dsphere" })
db.places.find({
location: { $nearSphere: { $geometry: { type: "Point", coordinates: [-73.97, 40.77] }, $maxDistance: 2000 } }
})
db.places.find({
location: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [[[...], ...]] } } }
})