MongoDB
Overview
MongoDB is a document-oriented NoSQL database that stores data in flexible, JSON-like BSON documents. It excels at rapid iteration on schemas, horizontal scaling via sharding, and handling semi-structured data. This reference covers user management, database operations, CRUD patterns, indexing, and administration.
Connection
MongoDB is accessed interactively with mongosh. You can start a local session with no arguments, pass a URI that includes host, port, and authentication credentials, or switch databases from inside the shell with use.
# Local connection
mongosh
mongosh "mongodb://localhost:27017"
mongosh --username admin --password secret --authenticationDatabase admin
# Connect to specific database
mongosh "mongodb://localhost:27017/mydb"
# Connection string with replica set
mongosh "mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=rs0"
# Inside mongosh, switch database
use mydb
User management
Creating users
Users are created with db.createUser() and must be given one or more roles. Create the root/admin user in the admin database, and scope application and read-only users to just the databases they actually need.
// Switch to admin database to manage users
use admin
// Create a root/admin user with full access
db.createUser({
user: "admin",
pwd: "super_secure_password",
roles: ["root"],
})
// Create application user with per-database access
use mydb
db.createUser({
user: "appuser",
pwd: "app_password",
roles: [
{ role: "readWrite", db: "mydb" },
{ role: "read", db: "analytics" },
],
})
// Create read-only user
db.createUser({
user: "readonly",
pwd: "readonly_password",
roles: [{ role: "read", db: "mydb" }],
})
Managing users
Existing users can be listed, have their passwords changed, and be granted or stripped of roles without being deleted. The commands below show each of these maintenance operations.
// List all users
db.getUsers()
// In admin database: shows all users across databases
use admin
db.system.users.find().pretty()
// Change a user's password
db.changeUserPassword("appuser", "new_password")
// Grant additional roles
db.grantRolesToUser("appuser", [{ role: "readWrite", db: "logs" }])
// Revoke roles
db.revokeRolesFromUser("appuser", [{ role: "read", db: "analytics" }])
// Remove user
db.dropUser("readonly")
Built-in roles
| Role | Permissions |
|---|---|
read | Read all non-system collections |
readWrite | Read + write to non-system collections |
dbAdmin | Administrative tasks (indexes, stats, not user management) |
userAdmin | Create/modify users and roles on current database |
dbOwner | Combines readWrite, dbAdmin, and userAdmin |
clusterAdmin | Cluster-wide admin (replica sets, sharding) |
root | Superuser — full access to all resources |
Database management
Databases are created lazily — they only appear on disk after the first write. These commands show existing databases, switch context, drop a database, and report the current database's name and stats.
// Show databases
show dbs
db.adminCommand({ listDatabases: 1 })
// Switch to / create database (created on first write)
use mydb
// Drop database
use mydb
db.dropDatabase()
// Current database
db.getName()
// Database stats (optionally reported in KB)
db.stats()
db.stats(1024) // sizes in KB
Collection operations
Collections are MongoDB's equivalent of tables. They are also created on first insert, but createCollection is useful when you need options such as a capped collection. The examples below create, list, drop, and rename collections.
// Create collection (optional — created on first insert)
db.createCollection("users")
db.createCollection("users", { capped: true, size: 5242880, max: 5000 })
// List collections
show collections
db.getCollectionNames()
// Drop collection
db.users.drop()
// Rename collection
db.users.renameCollection("accounts")
Basic CRUD
Create (Insert)
insertOne() inserts a single document, while insertMany() inserts several documents in one call and is more efficient for bulk loads. You can also supply your own _id instead of letting MongoDB generate an ObjectId.
// Insert one document
db.users.insertOne({
name: "Alice",
email: "alice@example.com",
role: "admin",
active: true,
tags: ["engineering", "admin"],
createdAt: new Date(),
})
// Insert multiple documents in one call
db.users.insertMany([
{ name: "Bob", email: "bob@example.com", role: "user", active: true },
{ name: "Carol", email: "carol@example.com", role: "user", active: false },
])
// Insert with custom _id
db.users.insertOne({
_id: "user_alice",
name: "Alice",
email: "alice@example.com",
})
Read (Query)
find() returns a cursor of matching documents, refined by a filter, projection, sort, and skip/limit. The examples cover basic filters, comparison and regex operators, nested field and array queries, the aggregation pipeline, and counting documents.
// Find all
db.users.find()
db.users.find().pretty()
// Find with a filter (equality match on fields)
db.users.find({ role: "admin" })
db.users.find({ active: true, role: "user" })
// Find one
db.users.findOne({ email: "alice@example.com" })
// Projection: return only listed fields (1 = include, 0 = exclude)
db.users.find({}, { name: 1, email: 1, _id: 0 })
// Comparison operators
db.users.find({ age: { $gt: 18 } }) // greater than
db.users.find({ age: { $gte: 18, $lte: 65 } }) // between
db.users.find({ name: { $in: ["Alice", "Bob"] } })
db.users.find({ email: { $regex: /@example\.com$/ } })
// Nested field queries
db.users.find({ "address.city": "New York" })
// Array queries
db.users.find({ tags: "admin" }) // contains "admin"
db.users.find({ tags: { $all: ["eng", "admin"] } }) // contains both
db.users.find({ tags: { $size: 2 } }) // array has 2 elements
// Sorting, limiting, skipping
db.users.find().sort({ createdAt: -1 }).limit(10)
db.users.find().sort({ name: 1 }).skip(20).limit(10)
// Aggregation pipeline
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$userId", total: { $sum: "$amount" }, count: { $sum: 1 } } },
{ $sort: { total: -1 } },
{ $limit: 5 },
])
// Count documents matching a filter
db.users.countDocuments({ active: true })
Update
updateOne() and updateMany() modify the first or all matching documents using update operators such as $set. The examples also show replaceOne() for whole-document swaps, upsert to insert when nothing matches, and the array operators $push and $pull.
// Update one
db.users.updateOne(
{ email: "bob@example.com" },
{ $set: { role: "manager", updatedAt: new Date() } },
)
// Update many
db.users.updateMany(
{ active: false },
{ $set: { deactivatedAt: new Date() } },
)
// Replace the whole document (the _id field is preserved)
db.users.replaceOne(
{ email: "carol@example.com" },
{ name: "Carol", email: "carol@newdomain.com", role: "user", active: true },
)
// Upsert (insert if not found)
db.users.updateOne(
{ email: "dave@example.com" },
{ $setOnInsert: { createdAt: new Date() }, $set: { name: "Dave", active: true } },
{ upsert: true },
)
// Increment
db.users.updateOne({ _id: "user_alice" }, { $inc: { loginCount: 1 } })
// Add an element to an array field
db.users.updateOne({ _id: "user_alice" }, { $push: { tags: "verified" } })
// Remove an element from an array field
db.users.updateOne({ _id: "user_alice" }, { $pull: { tags: "temp" } })
Delete
deleteOne() and deleteMany() remove documents permanently. As with PostgreSQL, a soft delete — flipping a flag like active: false and stamping deletedAt — is usually preferred so the data can be recovered later.
// Delete one
db.users.deleteOne({ email: "bob@example.com" })
// Delete many
db.users.deleteMany({ active: false, createdAt: { $lt: new Date("2020-01-01") } })
// Soft delete pattern (preferred)
db.users.updateOne(
{ email: "bob@example.com" },
{ $set: { active: false, deletedAt: new Date() } },
)
Indexes
Indexes dramatically speed up queries and can enforce uniqueness. These examples create single-field, compound, and text indexes, list and drop them, and use explain("executionStats") to verify that a query is actually using an index.
// Create a unique index (enforces no duplicate emails)
db.users.createIndex({ email: 1 }, { unique: true })
db.users.createIndex({ role: 1, active: 1 }) // compound index for common queries
// Text index for full-text search
db.articles.createIndex({ title: "text", body: "text" })
db.articles.find({ $text: { $search: "mongodb performance" } })
// List indexes
db.users.getIndexes()
// Drop index
db.users.dropIndex("email_1")
// Drop all non-_id indexes
db.users.dropIndexes()
// Verify index usage for a given query
db.users.find({ email: "test" }).explain("executionStats")
Configuration
mongod.conf
The mongod.conf file configures the MongoDB server: storage and caching, logging, network binding, authentication, and replication. On Debian/Ubuntu it lives at /etc/mongod.conf and takes effect after a restart.
# /etc/mongod.conf
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 2
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
net:
port: 27017
bindIp: 127.0.0.1
security:
authorization: enabled
# Replication (for replica sets)
replication:
replSetName: "rs0"
Apply changes:
systemctl restart mongod
Backup and restore
mongodump and mongorestore create binary backups of a database or individual collections, while mongoexport and mongoimport move data to and from human-readable JSON files — handy for smaller datasets or migrations.
# Binary dump of a database into a timestamped directory
mongodump --db mydb --out /backup/mydb_$(date +%Y%m%d)
mongodump --host localhost --port 27017 --username admin --password secret \
--authenticationDatabase admin --out /backup/
# Dump specific collection
mongodump --db mydb --collection users --out /backup/
# Restore
mongorestore --db mydb /backup/mydb/
mongorestore --db mydb --collection users /backup/mydb/users.bson
# Export a collection to JSON (for smaller datasets)
mongoexport --db mydb --collection users --out users.json
mongoexport --db mydb --collection users --query '{"active": true}' --out active_users.json
# Import from JSON
mongoimport --db mydb --collection users --file users.json
Monitoring
MongoDB exposes live metrics through shell commands and the db object. These examples check server status and connection counts, inspect and kill long-running operations, and view collection-level statistics.
// Server status
db.serverStatus()
db.serverStatus().connections
db.serverStatus().opcounters
// Current operations
db.currentOp()
db.currentOp({ active: true, secs_running: { $gt: 5 } })
// Kill operation
db.killOp(<opid>)
// Collection stats
db.users.stats()
// Top operations
db.adminCommand({ top: 1 })
See also
- PostgreSQL Reference — relational database operations
- Databases Overview — quick start and configuration locations