DPKG
A step-by-step guide to building, installing, and managing Debian packages using dpkg.
MongoDB is a popular NoSQL document database that stores data in flexible, JSON-like documents (BSON). Unlike relational databases, MongoDB doesn’t require a predefined schema, making it well-suited for applications with evolving data models. This post is a quick reference for the most common operations using the MongoDB shell.
Every MongoDB server can host multiple databases. Each database is an isolated namespace that contains its own set of collections. You can switch between databases freely — and if the target database doesn’t exist yet, MongoDB will create it automatically the first time you write data to it.
// Show all databases
show dbs
// Show the current database
db
// Create or switch to a database
use acme
// Drop the current database
db.dropDatabase()
Collections are the MongoDB equivalent of tables in relational databases. Each collection holds a set of documents that can have different structures — there’s no enforced schema by default. You can create a collection explicitly, but MongoDB will also create one automatically the first time you insert a document into it.
// Create a collection explicitly
db.createCollection('posts')
// Show all collections in the current database
show collections
// Drop a collection and all its documents
db.posts.drop()
Documents in MongoDB are stored as BSON (Binary JSON). Each document automatically gets a unique _id field if you don’t provide one. You can insert a single document or multiple documents at once. The insertOne() and insertMany() methods are the modern API — the older insert() method is deprecated.
db.posts.insertOne({
title: 'Post One',
body: 'Body of post one',
category: 'News',
tags: ['news', 'events'],
user: {
name: 'John Doe',
status: 'author'
},
date: Date()
})
When inserting multiple documents, insertMany() is more efficient than calling insertOne() in a loop because it sends all documents in a single network request.
db.posts.insertMany([
{
title: 'Post Two',
body: 'Body of post two',
category: 'Technology',
date: Date()
},
{
title: 'Post Three',
body: 'Body of post three',
category: 'News',
date: Date()
},
{
title: 'Post Four',
body: 'Body of post four',
category: 'Entertainment',
date: Date()
}
])
The find() method is the primary way to retrieve documents from a collection. Without any arguments, it returns all documents. You can pass a filter object to match specific criteria, and an optional projection object to control which fields are included in the results. Using projection is a good practice when you only need a few fields — it reduces the amount of data transferred from the database.
// Get all documents
db.posts.find()
// Get all documents (formatted output)
db.posts.find().pretty()
// Find by field value
db.posts.find({ category: 'News' })
// Find a single document (returns the first match)
db.posts.findOne({ category: 'News' })
// Return only specific fields (projection)
// 1 = include, 0 = exclude
db.posts.find({ title: 'Post One' }, {
title: 1,
author: 1,
_id: 0
})
MongoDB provides logical operators that let you build complex queries by combining multiple conditions. $and requires all conditions to match, $or requires at least one, and $not inverts a condition. When you pass multiple fields in a single filter object, MongoDB implicitly treats them as an AND condition, so you only need $and explicitly when applying multiple conditions to the same field.
// AND — all conditions must match
db.posts.find({
$and: [
{ category: 'News' },
{ tags: 'events' }
]
})
// OR — at least one condition must match
db.posts.find({
$or: [
{ category: 'News' },
{ category: 'Technology' }
]
})
// NOT — invert a condition
db.posts.find({
category: { $not: { $eq: 'News' } }
})
These operations let you control the order, quantity, and offset of results returned from a query. Sorting uses 1 for ascending and -1 for descending. limit() caps the number of results, and skip() allows you to jump past a number of documents — together, they form the basis for pagination. These methods can be chained together in any order; MongoDB optimizes the execution regardless.
// Sort ascending (1) or descending (-1)
db.posts.find().sort({ title: 1 }).pretty()
db.posts.find().sort({ title: -1 }).pretty()
// Count documents
db.posts.countDocuments()
db.posts.countDocuments({ category: 'News' })
// Limit results
db.posts.find().limit(2).pretty()
// Skip results (useful for pagination)
db.posts.find().skip(2).limit(2).pretty()
// Chain operations
db.posts.find().limit(2).sort({ title: 1 }).pretty()
A common pattern for paginating results in an application. You calculate how many documents to skip based on the current page number and the page size. Keep in mind that skip() can become slow on very large collections — for high-performance pagination, consider using range-based queries on an indexed field instead.
const pageSize = 10
const pageNumber = 3
db.posts.find()
.sort({ date: -1 })
.skip(pageSize * (pageNumber - 1))
.limit(pageSize)
The forEach() method lets you run a JavaScript function against each document in a cursor. This is useful for quick scripts and data inspection in the MongoDB shell, but for production application code, you would typically use a driver’s cursor API which provides better control over batching and error handling.
db.posts.find().forEach(function(doc) {
print("Blog Post: " + doc.title)
})
MongoDB offers several ways to update documents. The key distinction is between replacing an entire document and updating specific fields within it. replaceOne() swaps the entire document content, while updateOne() with operators like $set modifies only the targeted fields. Always prefer $set over full replacement unless you intentionally want to remove all other fields.
This replaces the entire document (except _id). Use with caution — any fields not included in the replacement object will be permanently removed from the document.
db.posts.replaceOne(
{ title: 'Post Two' },
{
title: 'Post Two',
body: 'New body for post 2',
date: Date()
}
)
The $set operator updates only the specified fields, leaving all other fields intact. This is the safest and most common way to update documents.
db.posts.updateOne(
{ title: 'Post Two' },
{
$set: {
body: 'Body for post 2',
category: 'Technology'
}
}
)
An upsert is a combination of update and insert. If a document matching the filter exists, it gets updated. If no match is found, a new document is created with the filter criteria and the update values merged together. This is useful when you want to ensure a document exists without checking first.
db.posts.updateOne(
{ title: 'Post Five' },
{
$set: {
body: 'Body of post five',
category: 'Science'
}
},
{ upsert: true }
)
By default, updateOne() only modifies the first matching document. Use updateMany() when you need to apply the same change across all documents that match a filter.
db.posts.updateMany(
{ category: 'News' },
{
$set: { featured: true }
}
)
The $inc operator atomically increments a numeric field by a given value. If the field doesn’t exist, it will be created with the increment value. This is useful for counters, scores, and other numeric tracking without needing a read-modify-write cycle.
db.posts.updateOne(
{ title: 'Post Two' },
{ $inc: { likes: 5 } }
)
The $rename operator changes the name of a field. This is helpful during schema migrations when you want to rename fields across your documents without rewriting them entirely.
db.posts.updateOne(
{ title: 'Post Two' },
{ $rename: { likes: 'views' } }
)
The $unset operator removes a field from a document. The value you pass to $unset doesn’t matter — it’s the key that determines which field gets removed.
db.posts.updateOne(
{ title: 'Post Two' },
{ $unset: { views: '' } }
)
Deleting documents is straightforward. deleteOne() removes the first document that matches the filter, while deleteMany() removes all matches. Passing an empty filter {} to deleteMany() will delete every document in the collection — use it carefully. If you need to remove all documents but keep the collection and its indexes, deleteMany({}) is preferred over drop().
// Delete a single document
db.posts.deleteOne({ title: 'Post Four' })
// Delete multiple documents
db.posts.deleteMany({ category: 'News' })
// Delete all documents in a collection
db.posts.deleteMany({})
One of MongoDB’s key advantages is its ability to store nested documents (sub-documents) and arrays directly within a document. This lets you model one-to-many relationships without needing separate collections and joins. For example, a blog post can contain an array of comments as a nested field. This pattern works well when the nested data is always accessed together with the parent document and doesn’t grow unboundedly.
db.posts.updateOne(
{ title: 'Post One' },
{
$set: {
comments: [
{
body: 'Comment One',
user: 'Mary Williams',
date: Date()
},
{
body: 'Comment Two',
user: 'Harry White',
date: Date()
}
]
}
}
)
The $push operator appends an element to an array field. If the field doesn’t exist yet, it creates the array with the new element. For adding multiple elements at once, you can combine $push with the $each modifier.
db.posts.updateOne(
{ title: 'Post One' },
{
$push: {
comments: {
body: 'Comment Three',
user: 'Jane Doe',
date: Date()
}
}
}
)
The $pull operator removes all elements from an array that match a specified condition. This is useful for removing specific items without needing to know their index position.
db.posts.updateOne(
{ title: 'Post One' },
{
$pull: {
comments: { user: 'Jane Doe' }
}
}
)
When querying documents that contain arrays of sub-documents, $elemMatch ensures that all your conditions are applied to the same array element. Without it, MongoDB might match a document where different array elements satisfy different parts of your query — which is usually not what you want.
db.posts.find({
comments: {
$elemMatch: {
user: 'Mary Williams'
}
}
})
Indexes are data structures that store a small portion of the collection’s data in a sorted format, allowing the database engine to quickly locate documents without scanning every entry. Without indexes, every query triggers a full collection scan, which becomes increasingly slow as your data grows. MongoDB automatically creates an index on the _id field, but you should create additional indexes on fields that you frequently query, sort, or filter by.
// Create a single-field index
db.posts.createIndex({ title: 1 })
// Create a compound index (useful when filtering by category and sorting by date)
db.posts.createIndex({ category: 1, date: -1 })
// Create a text index (enables full-text search)
db.posts.createIndex({ title: 'text', body: 'text' })
// List all indexes on a collection
db.posts.getIndexes()
// Drop an index you no longer need
db.posts.dropIndex({ title: 1 })
MongoDB’s text search feature lets you search for words and phrases within string fields. It requires a text index on the fields you want to search. Text search supports word stemming (so “running” matches “run”), stop word removal, and phrase matching using quotes. Note that a collection can only have one text index, but that index can cover multiple fields.
// Search for an exact phrase
db.posts.find({
$text: {
$search: "\"Post O\""
}
})
// Search for documents containing any of these words
db.posts.find({
$text: {
$search: "post technology"
}
})
Comparison operators let you filter documents based on how a field’s value relates to a given value. These are used inside the filter object alongside field names. $gt and $lt are strict comparisons, while $gte and $lte include the boundary value. $ne matches everything except the specified value, and $in matches any value from a provided array — making it a concise alternative to chaining multiple $or conditions on the same field.
db.posts.find({ views: { $gt: 2 } }) // greater than
db.posts.find({ views: { $gte: 7 } }) // greater than or equal
db.posts.find({ views: { $lt: 7 } }) // less than
db.posts.find({ views: { $lte: 7 } }) // less than or equal
db.posts.find({ views: { $ne: 5 } }) // not equal
db.posts.find({ views: { $in: [5, 10] } }) // in array
The aggregation pipeline is MongoDB’s most powerful tool for data analysis and transformation. It processes documents through a sequence of stages, where each stage transforms the data and passes the result to the next. Think of it like a Unix pipeline: each stage does one thing, and you chain them together to build complex transformations. The pipeline runs server-side, making it much more efficient than fetching all documents and processing them in your application code.
db.posts.aggregate([
// Stage 1: Filter to only News posts
{ $match: { category: 'News' } },
// Stage 2: Group by category and calculate aggregates
{ $group: {
_id: '$category',
totalPosts: { $sum: 1 },
avgViews: { $avg: '$views' }
}},
// Stage 3: Sort by total posts descending
{ $sort: { totalPosts: -1 } },
// Stage 4: Return only the top 5 results
{ $limit: 5 }
])
Common aggregation stages:
$match — filters documents, similar to find(). Place it early in the pipeline to reduce the number of documents subsequent stages need to process.$group — groups documents by a specified key and computes aggregates like sum, average, min, and max.$sort — reorders documents by one or more fields.$project — reshapes each document by including, excluding, or renaming fields. Also supports computed fields.$limit / $skip — controls how many documents pass through, useful for pagination.$unwind — deconstructs an array field so each element becomes its own document. Essential when you need to aggregate or filter by array contents.$lookup — performs a left outer join with another collection, similar to SQL’s LEFT JOIN.