Cheatsheet MongoDB
Base de dados NoSQL orientada a documentos (JSON/BSON)
MongoDB
Databases and Collections
List databases
show dbs // In the Node.js driver: const dbs = await client.db().admin().listDatabases(); dbs.databases.forEach(d => console.log(d.name, d.sizeOnDisk));
show dbs lists all databases in mongosh. It only shows databases with at least one document. Empty databases do not appear.
Create collection with options
// Simple collection
db.createCollection("customers")
// Capped collection (fixed size, log-like)
db.createCollection("logs", {
capped: true,
size: 1048576, // 1 MB
max: 5000 // max. documents
})createCollection() creates explicitly. capped collections have a fixed size and overwrite the oldest ones — ideal for logs. Most collections do not need this.
Select / create database
use store
// The database is created automatically
// when the first document is inserted
db.products.insertOne({ name: "Test" });use name switches to the given database. If it does not exist, it is created implicitly on the first insert. There is no explicit CREATE DATABASE command.
Drop collection
db.customers.drop()
// Also drops all associated indexes
// Returns true if it existed, false otherwise
// Check first:
if (db.getCollectionNames().includes("temp")) {
db.temp.drop();
}drop() deletes the collection, its documents and all indexes. It is irreversible. To clear data while keeping the structure, use deleteMany({}).
Current database
db // shows the current database name db.getName() // explicit alternative // Statistics db.stats() db.stats().objects // total documents
db returns the reference to the current database. db.stats() shows statistics such the size, number of documents and indexes.
Drop database
// Drops the current database db.dropDatabase() // Confirmation: show dbs // no longer appears
db.dropDatabase() deletes the entire current database — data, collections and indexes. A destructive and irreversible operation. Always confirm with db.getName() first.
List collections
show collections // Or programmatically: db.getCollectionNames() // With details: db.getCollectionInfos()
show collections lists the collections of the current database. getCollectionNames() returns an array. getCollectionInfos() includes options such the capped and validator.
Rename collection
db.oldCustomers.renameCollection("customers")
// With the option to overwrite the target:
db.temp.renameCollection("customers", true)renameCollection() changes the collection name. The second argument true allows overwriting an existing collection with the same name. Indexes are preserved.
Insert Documents
insertOne
const result = db.customers.insertOne({
name: "Anna",
age: 30,
city: "Lisbon",
active: true
});
console.log(result.insertedId);insertOne() inserts a single document. It returns insertedId with the generated ObjectId. If the _id field is not provided, MongoDB creates it automatically.
Arrays in documents
db.customers.insertOne({
name: "Ray",
tags: ["vip", "newsletter"],
phones: [
{ type: "personal", number: "912345678" },
{ type: "work", number: "213456789" }
]
});Fields can contain arrays of simple values or of objects. Arrays of objects are useful for lists with metadata. Limit: a document up to 16 MB.
insertMany
const result = db.customers.insertMany([
{ name: "Ray", age: 25 },
{ name: "Mia", age: 40 },
{ name: "Joe", age: 35 }
]);
console.log(result.insertedCount); // 3
console.log(result.insertedIds);insertMany() inserts multiple documents in an array. insertedCount confirms how many were inserted. By default, it stops at the first error (ordered).
Data types
db.types.insertOne({
text: "string",
integer: NumberInt(42),
long: NumberLong(9999999999),
double: 3.14,
boolean: true,
date: new Date(),
null: null,
objectId: new ObjectId(),
regex: /pattern/i
});BSON supports String, Int32, Int64, Double, Boolean, Date, null, ObjectId, Array, Object and Regex.
insertMany (unordered)
db.customers.insertMany(
[
{ name: "A", _id: 1 },
{ name: "B", _id: 1 }, // duplicate!
{ name: "C", _id: 3 }
],
{ ordered: false }
);
// Inserts A and C, ignores BWith { ordered: false }, MongoDB keeps inserting the rest even if one fails. Faster for bulk inserts. Errors end up in writeErrors.
Custom _id
// Use your own ID instead of ObjectId
db.customers.insertOne({
_id: "anna@email.com",
name: "Anna"
});
// Or UUID
db.customers.insertOne({
_id: UUID(),
name: "Ray"
});The _id field can be any unique value (string, number, UUID). If omitted, MongoDB generates a 12-byte ObjectId automatically.
Nested documents
db.customers.insertOne({
name: "Anna",
address: {
street: "Liberty Ave",
number: 42,
city: "Lisbon",
zip: "1000-001"
}
});MongoDB supports nested objects (embedded documents). Ideal for data accessed together. Maximum depth: 100 levels. Query with dot notation.
Schema validation
db.createCollection("customers", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string" },
age: { bsonType: "int", minimum: 0 }
}
}
}
});$jsonSchema validates documents on insert/update. Fields in required are mandatory. bsonType defines the expected type. It rejects invalid documents.
Queries (find)
find (all documents)
db.customers.find()
// With readable formatting:
db.customers.find().pretty()
// In the Node.js driver:
const docs = await db.collection("customers").find({}).toArray();find() without a filter returns all documents in the collection. It returns a cursor — data is loaded in batches, not all at once.
Sorting (sort)
// Ascending by age
db.customers.find().sort({ age: 1 })
// Descending by age
db.customers.find().sort({ age: -1 })
// Multiple fields
db.customers.find().sort({ city: 1, age: -1 })sort() orders the results. 1 = ascending, -1 = descending. You can combine fields. Without an index, it does an in-memory sort (100 MB limit).
Simple filter
// Exact equality
db.customers.find({ city: "Lisbon" })
// Multiple conditions (implicit AND)
db.customers.find({ city: "Lisbon", active: true })
// By _id
db.customers.find({ _id: ObjectId("65f...") })Filters use equality by default. Multiple fields in the same object work the AND. To compare by _id, use ObjectId() the a wrapper.
Limit and Skip (pagination)
// First 10
db.customers.find().limit(10)
// Page 3 (skip 20, limit 10)
db.customers.find().sort({ name: 1 }).skip(20).limit(10)
// Recommended order: sort → skip → limitlimit() restricts the number of results. skip() skips documents (pagination). For large datasets, prefer pagination by _id instead of skip.
findOne
const customer = db.customers.findOne({ name: "Anna" });
// Returns the document or null
if (customer) {
console.log(customer.age);
}findOne() returns the first matching document (or null). It does not return a cursor — it is direct. Useful when you know there is only one result.
Count documents
// Collection total
db.customers.countDocuments()
// With filter
db.customers.countDocuments({ active: true })
// Fast estimate (uses metadata, does not scan)
db.customers.estimatedDocumentCount()countDocuments() counts accurately (scans documents). estimatedDocumentCount() is instant but approximate — it uses collection metadata.
Projection (fields)
// Include only name and city (exclude _id)
db.customers.find({}, { name: 1, city: 1, _id: 0 })
// Exclude specific fields
db.customers.find({}, { password: 0, notes: 0 })
// Nested field
db.customers.find({}, { "address.city": 1 })The second argument of find() is the projection. 1 includes, 0 excludes. _id always comes by default — use _id: 0 to omit it.
distinct
// Unique values of a field
db.customers.distinct("city")
// ["Lisbon", "Porto", "Braga"]
// With filter
db.customers.distinct("city", { active: true })
// In arrays, it returns unique elements
db.customers.distinct("tags")distinct() returns an array with the unique values of a field. It accepts a filter the the second argument. In array fields, it returns unique individual elements.
Query Operators
Comparison ($gt, $lt, $ne)
{ age: { $gt: 18 } } // greater than
{ age: { $gte: 18 } } // greater or equal
{ age: { $lt: 65 } } // less than
{ age: { $lte: 65 } } // less or equal
{ age: { $ne: 30 } } // not equal to
{ age: { $eq: 30 } } // equal to (explicit)Comparison operators: $gt, $gte, $lt, $lte, $ne, $eq. They work with numbers, strings and dates.
$exists and $type
// Field exists
{ phone: { $exists: true } }
// Field does not exist
{ fax: { $exists: false } }
// Check BSON type
{ age: { $type: "int" } }
{ date: { $type: "date" } }$exists checks the presence of the field (even if null). $type filters by BSON type: "string", "int", "double", "bool", "date", "array", "object", "null".
$in and $nin
// City is in the list
{ city: { $in: ["Lisbon", "Porto", "Braga"] } }
// City is NOT in the list
{ city: { $nin: ["Faro", "Evora"] } }
// Works with arrays: field contains some value
{ tags: { $in: ["vip", "premium"] } }$in matches if the value is in the list. $nin is the inverse. In array fields, it checks whether any element matches. More efficient than multiple $or.
$regex (text search)
// Starts with "Anna" (case-insensitive)
{ name: { $regex: "^Anna", $options: "i" } }
// Contains "smith"
{ name: { $regex: "smith", $options: "i" } }
// Alternative syntax
{ name: /^anna/i }$regex allows regular expression patterns. $options: "i" ignores case. Without an index, it does a collection scan. Prefixes (^) can use an index.
$and and $or
// OR: one of the conditions
{ $or: [
{ age: { $lt: 18 } },
{ vip: true }
]}
// Explicit AND (needed for the same field)
{ $and: [
{ age: { $gt: 18 } },
{ age: { $lt: 65 } }
]}$or requires at least one true condition. Explicit $and is needed when you apply two operators to the same field (e.g. a range with $gt and $lt).
Nested fields and arrays
// Nested field (dot notation)
{ "address.city": "Porto" }
// Array contains value
{ tags: "vip" }
// Array with a specific condition
{ grades: { $elemMatch: { $gt: 15, $lt: 20 } } }
// Array size
{ tags: { $size: 3 } }Dot notation accesses nested fields. In arrays, equality checks whether it contains the value. $elemMatch applies multiple conditions to the same element. $size filters by length.
$not and $nor
// NOT: inverts a condition
{ age: { $not: { $gt: 65 } } }
// NOR: no condition can be true
{ $nor: [
{ city: "Lisbon" },
{ vip: true }
]}$not inverts an operator (includes documents without the field). $nor is the opposite of $or — none can match. Careful: $not includes docs where the field does not exist.
$expr (expressions)
// Compare two fields of the same document
{ $expr: { $gt: ["$spending", "$budget"] } }
// With aggregation inside find
{ $expr: {
$and: [
{ $eq: ["$status", "active"] },
{ $gte: ["$balance", 100] }
]
}}$expr allows using aggregation expressions inside find(). Useful for comparing fields against each other. The $ prefix references field values.
Update Documents
updateOne ($set)
db.customers.updateOne(
{ name: "Anna" },
{ $set: { age: 31, city: "Porto" } }
);
// Result: { matchedCount: 1, modifiedCount: 1 }updateOne() updates the first document that matches the filter. $set defines new values for fields. If the field does not exist, it is created.
$push and $pull (arrays)
// Add to the array
db.customers.updateOne(
{ _id: id },
{ $push: { tags: "premium" } }
);
// Remove from the array
db.customers.updateOne(
{ _id: id },
{ $pull: { tags: "old" } }
);
// Add several
{ $push: { tags: { $each: ["a", "b"] } } }$push adds an element to the array (creates it if it does not exist). $pull removes all matching elements. $each adds multiple at once.
updateMany
db.customers.updateMany(
{ city: "Lisbon" },
{ $set: { region: "Center-South" } }
);
// Result: { matchedCount: 150, modifiedCount: 150 }updateMany() updates all documents that match the filter. It returns matchedCount and modifiedCount. Use an empty filter {} to update all.
Upsert
db.customers.updateOne(
{ email: "anna@mail.com" },
{
$set: { name: "Anna", visits: 1 },
$setOnInsert: { createdAt: new Date() }
},
{ upsert: true }
);upsert: true creates the document if none matches the filter. $setOnInsert is only applied on creation (not on update). A powerful combination for "create or update".
$inc (increment)
// Add 10 to the points
db.customers.updateOne(
{ _id: id },
{ $inc: { points: 10 } }
);
// Decrement
db.customers.updateOne(
{ _id: id },
{ $inc: { stock: -1 } }
);$inc adds a value to the field (creates it with that value if it does not exist). It accepts negatives to decrement. An atomic operation — safe under concurrency.
replaceOne
db.customers.replaceOne(
{ _id: id },
{ name: "New Name", age: 25 }
);
// Replaces EVERYTHING (except _id)
// Fields not included are removed!replaceOne() replaces the entire document with the new object. Fields not included are lost. Use updateOne() with $set for partial updates.
$unset (remove field)
// Remove a field
db.customers.updateOne(
{ _id: id },
{ $unset: { fax: "" } }
);
// Remove a nested field
db.customers.updateOne(
{ _id: id },
{ $unset: { "address.floor": "" } }
);$unset removes a field from the document. The value ("") is irrelevant. It works with nested fields via dot notation. It does not remove the document.
$rename and $mul
// Rename field
db.customers.updateMany(
{},
{ $rename: { "tel": "phone" } }
);
// Multiply value
db.products.updateOne(
{ _id: id },
{ $mul: { price: 1.1 } } // +10%
);$rename changes the name of a field in all documents. $mul multiplies the numeric value. If the field does not exist, $mul creates it with value 0.
Delete Documents
deleteOne
const result = db.customers.deleteOne({ name: "Anna" });
console.log(result.deletedCount); // 1
// Only deletes the FIRST that matches
// even if several have the name "Anna"deleteOne() removes only the first document that matches the filter. It returns deletedCount. To delete by _id, it is always unique.
Delete by _id
// By ObjectId
db.customers.deleteOne({ _id: ObjectId("65f3a...") });
// Multiple IDs
db.customers.deleteMany({
_id: { $in: [ObjectId("..."), ObjectId("...")] }
});Deleting by _id is the most efficient way — it uses the primary index automatically. ObjectId() converts the string. $in allows deleting several at once.
deleteMany
const result = db.customers.deleteMany({ active: false });
console.log(result.deletedCount); // N
// With a complex condition
db.customers.deleteMany({
$and: [
{ lastLogin: { $lt: new Date("2023-01-01") } },
{ plan: "free" }
]
});deleteMany() removes all documents that match. It accepts complex filters with $and, $or, comparison operators, etc.
Bulk delete (bulkWrite)
db.customers.bulkWrite([
{ deleteOne: { filter: { name: "A" } } },
{ deleteOne: { filter: { name: "B" } } },
{ deleteMany: { filter: { active: false } } }
]);bulkWrite() runs multiple operations (delete, insert, update) in a single request. More efficient than individual calls. It reduces round-trips to the server.
Delete all documents
// Removes all (keeps collection and indexes)
db.customers.deleteMany({});
// Faster alternative (removes everything):
db.customers.drop();
db.createCollection("customers");
// ⚠️ drop() also deletes indexes!deleteMany({}) clears data but keeps the collection and the indexes. drop() is faster but removes everything — you will have to recreate the indexes.
TTL (auto-expiration)
// Documents expire after 1 hour
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 }
);
// Insert with a date
db.sessions.insertOne({
userId: "u1",
createdAt: new Date()
});TTL (Time-To-Live) indexes delete documents automatically after X seconds. MongoDB checks every 60s. Ideal for sessions, tokens and temporary data.
findOneAndDelete
const doc = db.customers.findOneAndDelete(
{ email: "anna@mail.com" },
{ projection: { name: 1, email: 1 } }
);
// Returns the deleted document (or null)
console.log("Deleted:", doc.name);findOneAndDelete() removes and returns the document in an atomic operation. Useful for processing queues. It accepts a projection to limit the returned fields.
Care when deleting
// ❌ Dangerous: deletes EVERYTHING without a filter
db.customers.deleteMany()
// ✅ Always with a specific filter
db.customers.deleteMany({ _id: id })
// ✅ Check before deleting
const count = db.customers.countDocuments({ active: false });
print(`Will delete ${count} documents`);Always confirm the filter before deleteMany(). Use countDocuments() first to check how many will be affected. In production, do a backup before mass operations.
Aggregation Pipeline
Basic pipeline
db.customers.aggregate([
{ $match: { active: true } },
{ $sort: { age: -1 } },
{ $limit: 5 },
{ $project: { name: 1, age: 1, _id: 0 } }
]);aggregate() processes documents in a sequence of stages (pipeline). Each stage transforms the result of the previous one. Typical order: $match → $sort → $limit → $project.
$lookup (join)
db.orders.aggregate([
{ $lookup: {
from: "customers",
localField: "customerId",
foreignField: "_id",
the: "customer"
}},
{ $unwind: "$customer" }
]);$lookup does a left outer join with another collection. from is the target collection, localField/foreignField are the keys. The result is an array — use $unwind for a single object.
$lookup (JOIN)
// "JOIN" between collections:
db.orders.aggregate([
{
$lookup: {
from: "customers", // collection
localField: "customer_id", // local field
foreignField: "_id", // field in the other
the: "customer" // result name
}
},
{ $unwind: "$customer" } // optional: 1 object
]);
// Each order ends up with the
// customer document embedded$lookup is the equivalent of a JOIN: it links documents of two collections by localField = foreignField. The result goes into an array (the). Use $unwind to turn it into a single object.
$group (group by)
db.customers.aggregate([
{ $group: {
_id: "$city",
total: { $sum: 1 },
avgAge: { $avg: "$age" },
oldest: { $max: "$age" }
}}
]);$group groups by _id (the grouping expression). $sum: 1 counts. $avg, $min, $max compute statistics. The $ prefix references fields.
$addFields and $set
{ $addFields: {
total: { $multiply: ["$price", "$quantity"] },
vat: { $multiply: ["$price", 0.23] },
formattedDate: {
$dateToString: { format: "%d/%m/%Y", date: "$created" }
}
}}$addFields (alias $set) adds fields without removing the existing ones. Useful for intermediate calculations in the pipeline. $dateToString formats dates.
$unwind
// Document with an array:
// { name: "Anna", tags: ["a", "b"] }
db.products.aggregate([
{ $unwind: "$tags" }
]);
// Generates 1 document per element:
// { name: "Anna", tags: "a" }
// { name: "Anna", tags: "b" }
// With preserveNullAndEmptyArrays
// it keeps docs with an empty array:
{ $unwind: {
path: "$tags",
preserveNullAndEmptyArrays: true
} }$unwind denormalizes an array: it creates one document per element. Essential before $group by array values. preserveNullAndEmptyArrays: true does not discard documents without the array.
$project (reshape)
{ $project: {
name: 1,
isAdult: { $gte: ["$age", 18] },
fullName: { $concat: ["$name", " ", "$surname"] },
birthYear: { $subtract: [2025, "$age"] }
}}$project reshapes documents: it includes, excludes, renames or computes fields. Expressions such the $concat, $subtract, $gte create derived fields.
$bucket and $facet
// Group into ranges
{ $bucket: {
groupBy: "$age",
boundaries: [0, 18, 35, 65, 120],
default: "other",
output: { total: { $sum: 1 } }
}}
// Multiple aggregations in parallel
{ $facet: {
byCity: [{ $group: { _id: "$city", n: { $sum: 1 } } }],
byAge: [{ $group: { _id: "$age", n: { $sum: 1 } } }]
}}$bucket groups into numeric ranges. $facet runs multiple pipelines in parallel over the same data — it returns an object with each result.
$unwind (unfold arrays)
// Before: { name: "Anna", tags: ["a", "b", "c"] }
// After: 3 documents, one per tag
db.customers.aggregate([
{ $unwind: "$tags" },
{ $group: { _id: "$tags", total: { $sum: 1 } } }
]);
// Counts occurrences of each tag$unwind creates one document per array element. Combined with $group, it counts frequencies. preserveNullAndEmptyArrays: true keeps docs without the array.
Available accumulators
// In $group: $sum // sums values (or $sum: 1 to count) $avg // average $min // minimum $max // maximum $first // first value of the group $last // last value of the group $push // array with all the values $addToSet // array with unique values $count // count (MongoDB 5.0+)
Accumulators are used inside $group. $push creates an array with all values; $addToSet only unique values. $count is a shortcut for $sum: 1.
Indices
Create simple index
// Ascending index
db.customers.createIndex({ email: 1 });
// Descending index
db.customers.createIndex({ createdAt: -1 });
// With a custom name
db.customers.createIndex({ email: 1 }, { name: "idx_email" });createIndex() creates an index to speed up queries. 1 = ascending, -1 = descending. Without an index, MongoDB does a collection scan (scans everything).
List and drop indexes
// List all
db.customers.getIndexes();
// Drop by name
db.customers.dropIndex("email_1");
// Drop all (except _id)
db.customers.dropIndexes();getIndexes() shows the name, fields and options of each index. dropIndex() removes it by name (default: field_order). dropIndexes() removes all except _id.
Text index
// Full-text search:
db.articles.createIndex(
{ title: "text", body: "text" }
);
// Search:
db.articles.find(
{ $text: { $search: "mongodb indexes" } }
);
// With a relevance score:
db.articles.find(
{ $text: { $search: "mongodb" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });
// Only 1 text index
// per collectiontext indexes allow word search across several fields. Use $text + $search. $meta: "textScore" gives relevance for sorting. Limit: one per collection.
Unique index
db.customers.createIndex(
{ email: 1 },
{ unique: true }
);
// Allows multiple null (sparse):
db.customers.createIndex(
{ phone: 1 },
{ unique: true, sparse: true }
);unique: true prevents duplicate values (error code 11000). sparse: true ignores documents without the field — it allows multiple null. Essential for emails, usernames.
Partial index
// Only indexes active documents
db.customers.createIndex(
{ email: 1 },
{
unique: true,
partialFilterExpression: { active: true }
}
);partialFilterExpression creates an index only over documents that meet the condition. Smaller and faster than sparse. Useful when only a fraction of the docs is queried.
TTL index
// Deletes documents automatically
// after X seconds:
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 } // 1 hour
);
// Documents with createdAt older
// than 1h are removed
// by a background process
// Useful for: sessions, logs,
// tokens, temporary caches
// The field MUST be a DateTTL (time-to-live) indexes delete documents automatically after expireAfterSeconds. Ideal for sessions, logs and tokens. The indexed field must be a Date; the cleanup runs in the background.
Compound index
db.customers.createIndex({ city: 1, age: -1 });
// Order matters! This index works for:
db.customers.find({ city: "Lisbon" }).sort({ age: -1 });
// But it does NOT work for:
db.customers.find().sort({ age: -1 }); // without cityCompound indexes cover multiple fields. The order follows the ESR rule: Equality → Sort → Range. Queries must use the index prefix to take advantage of it.
explain (query analysis)
db.customers.find({ city: "Porto" })
.explain("executionStats");
// Important fields:
// totalDocsExamined: 0 → uses index ✅
// totalDocsExamined: 50000 → collection scan ❌
// executionTimeMillis: time in ms
// indexName: which index was usedexplain("executionStats") shows how the query is executed. If totalDocsExamined is much larger than nReturned, an index is missing. An essential optimization tool.
Text index (text)
db.articles.createIndex(
{ title: "text", content: "text" }
);
// Search
db.articles.find(
{ $text: { $search: "mongodb tutorial" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });text indexes allow full-text search. $text + $search runs the query. $meta: "textScore" gives relevance. Maximum 1 text index per collection.
ESR rule (index order)
// Query:
db.orders.find({ status: "shipped", total: { $gt: 100 } })
.sort({ date: -1 });
// Ideal index (ESR):
db.orders.createIndex({
status: 1, // Equality (first)
date: -1, // Sort (second)
total: 1 // Range (last)
});ESR rule: equality fields first, sort next, range last. It maximizes index usage. It is not always possible to follow 100% — use explain() to validate.
Tips and Good Practices
mongosh (CLI)
// Connect mongosh mongosh "mongodb://localhost:27017/store" mongosh "mongodb+srv://user:pass@cluster.mongodb.net/db" // Run a file mongosh script.js mongosh --eval "db.customers.countDocuments()"
mongosh is MongoDB's modern shell. It supports full JavaScript, autocomplete and --eval for quick commands. It replaces the old mongo shell.
Multi-document transactions
const session = client.startSession();
session.startTransaction();
try {
await db.collection("accounts").updateOne(
{ _id: "A" }, { $inc: { balance: -50 } }, { session }
);
await db.collection("accounts").updateOne(
{ _id: "B" }, { $inc: { balance: 50 } }, { session }
);
await session.commitTransaction();
} catch (e) {
await session.abortTransaction();
}Transactions guarantee atomicity across multiple documents/collections. They require a replica set or sharded cluster. Use session in all operations. Slower than simple operations.
Backup and restore
# Full backup (binary): mongodump --uri="mongodb://localhost:27017" # Only one database: mongodump --db=store --out=/backups # Restore: mongorestore --uri="mongodb://localhost:27017" /backups # Export/import JSON: mongoexport --db=store --collection=customers \ --out=customers.json mongoimport --db=store --collection=customers \ --file=customers.json # mongodump = binary (fast) # mongoexport = JSON (readable)
mongodump/mongorestore do a binary backup (fast and complete). mongoexport/mongoimport use JSON (readable, for migrations). Do regular dumps and test the restore.
ObjectId
// Generate new
new ObjectId()
// Extract creation date
ObjectId("65f3a...").getTimestamp()
// ISODate("2024-03-14T...")
// Compare
ObjectId("65f...").equals(otherId)ObjectId has 12 bytes: timestamp (4) + machine (3) + PID (2) + counter (3). getTimestamp() extracts the creation date without an extra field. Sortable chronologically.
Performance (tips)
// ✅ Index filter and sort fields
db.c.createIndex({ field: 1 });
// ✅ Projection: fetch only the needed fields
db.c.find({}, { name: 1, _id: 0 });
// ✅ limit() to avoid loading everything
db.c.find().limit(20);
// ❌ Avoid regex without an index
// ❌ Avoid skip() on large datasets
// ❌ Avoid documents > 1 MBIndex find() and sort() fields. Use a projection to reduce transferred data. limit() avoids loading whole collections. Monitor with explain().
Replica set and sharding
// Replica set = copies of the data
// (high availability):
// 1 primary + N secondaries
// automatic failover
// Start replica set:
rs.initiate()
rs.status()
rs.add("host2:27017")
// Sharding = split data across
// machines (horizontal scale):
// shard key defines the split
sh.enableSharding("store")
sh.shardCollection(
"store.orders", { customer_id: 1 }
)
// Replica = availability
// Shard = capacity/scaleReplica set replicates data across several nodes (automatic failover, reads on secondaries). Sharding splits data across machines using a shard key. Replica set for availability, sharding for horizontal scale.
Embed vs Reference
// EMBED (1:few, accessed together)
{
name: "Anna",
address: { street: "X", city: "Porto" }
}
// REFERENCE (1:many, accessed separately)
{
name: "Anna",
orders: [ObjectId("..."), ObjectId("...")]
}Embed for small data accessed together (1:1, 1:few). Reference for large relations or accessed separately (1:many). Limit: a document up to 16 MB.
Replica Set (concepts)
// Start replica set (3 nodes)
mongod --replSet rs0 --port 27017
mongod --replSet rs0 --port 27018
mongod --replSet rs0 --port 27019
// Initialize
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "localhost:27017" },
{ _id: 1, host: "localhost:27018" },
{ _id: 2, host: "localhost:27019" }
]
});A Replica Set has one primary (writes) and secondaries (reads/failover). It ensures high availability. Required for transactions and recommended in production.
Backup and restore
// Full backup mongodump --db store --out /backup/ // Restore mongorestore --db store /backup/store // Export/Import (JSON) mongoexport --db store --collection customers --out customers.json mongoimport --db store --collection customers --file customers.json
mongodump/mongorestore do a binary backup (BSON). mongoexport/mongoimport use JSON/CSV — useful for migrations and debugging. Schedule regular backups.
Administration commands
// Server status
db.serverStatus()
// Operations in progress
db.currentOp()
// Kill a slow operation
db.killOp(opId)
// Profiling (record slow queries)
db.setProfilingLevel(1, { slowms: 100 });
db.getProfilingLevel();serverStatus() gives server metrics. currentOp() shows active operations. setProfilingLevel(1) records queries above X ms in the system.profile collection.