Mastering MongoDB CRUD Operations: A Practical Command Guide
Unlock the power of MongoDB with this practical command guide to essential CRUD operations. Learn to efficiently manage your data using `insert`, `find`, `update`, and `delete` commands. This article provides clear explanations, real-world examples, and best practices for creating, reading, updating, and removing documents in your MongoDB collections. Perfect for developers and administrators, it’s your go-to resource for mastering MongoDB data manipulation.
Mastering MongoDB CRUD Operations: A Practical Command Guide
MongoDB, a popular NoSQL document database, forms the backbone for countless modern applications due to its flexibility, scalability, and performance. At the heart of interacting with any database are the fundamental Create, Read, Update, and Delete (CRUD) operations. Mastering these commands is essential for anyone working with MongoDB, from developers building new features to administrators managing data.
This guide uses one small users collection and shows the MongoDB CRUD commands you reach for during real work: adding a record from a signup form, finding the document behind a support ticket, changing a field without overwriting the rest of the document, and deleting data without accidentally wiping the collection. The examples use mongosh, but the same filters and update operators carry over to application drivers.
Prerequisites
Before diving into the commands, ensure you have:
- MongoDB installed and running: You can download it from the official MongoDB website or use a cloud service like MongoDB Atlas.
mongosh(MongoDB Shell) installed: This is the interactive JavaScript interface for MongoDB.
Connecting to MongoDB and Selecting a Database
To begin, open your terminal or command prompt and connect to your MongoDB instance using mongosh:
mongosh
Once connected, you'll be in the default test database. To switch to or create a new database, use the use command:
use myDatabase;
If myDatabase doesn't exist, MongoDB will create it implicitly when you insert your first document into a collection within it.
Create Operations (Insert)
Create operations involve adding new documents to a collection. MongoDB provides methods to insert single or multiple documents.
1. db.collection.insertOne()
This method inserts a single document into a collection. If the collection does not exist, MongoDB creates it.
// Select a collection named 'users'
db.users.insertOne({
name: "Alice Smith",
age: 30,
city: "New York",
email: "[email protected]",
interests: ["reading", "hiking"]
});
The output will show the acknowledged status and the insertedId of the new document.
2. db.collection.insertMany()
Use this method to insert multiple documents into a collection in a single operation. It takes an array of documents.
db.users.insertMany([
{
name: "Bob Johnson",
age: 24,
city: "Los Angeles",
email: "[email protected]",
interests: ["coding", "gaming"]
},
{
name: "Charlie Brown",
age: 35,
city: "New York",
email: "[email protected]",
interests: ["cooking", "photography"]
},
{
name: "Diana Prince",
age: 29,
city: "London",
email: "[email protected]",
interests: ["fitness", "travel"]
}
]);
This will return an array of insertedIds for all the documents added.
Tip: MongoDB automatically adds an
_idfield (a unique ObjectId) to each document if you don't provide one.
Read Operations (Find)
Read operations involve querying documents from a collection. The find() method is your primary tool for this.
1. db.collection.find()
a. Find All Documents
To retrieve all documents in a collection, call find() without any arguments:
db.users.find();
b. Find Documents with a Query Filter
Pass a query document to find() to specify criteria for selecting documents. This acts as a WHERE clause in SQL.
// Find users from New York
db.users.find({ city: "New York" });
// Find users older than 25
db.users.find({ age: { $gt: 25 } });
// Find users with age between 25 and 35 (exclusive of 35)
db.users.find({ age: { $gt: 25, $lt: 35 } });
// Find users whose interests include 'coding'
db.users.find({ interests: "coding" });
// Find users whose interests include BOTH 'reading' and 'hiking'
db.users.find({ interests: { $all: ["reading", "hiking"] } });
// Find users named Alice Smith OR from London
db.users.find({
$or: [
{ name: "Alice Smith" },
{ city: "London" }
]
});
Common query operators:
$eq: Equal to (default if no operator is specified)$ne: Not equal to$gt: Greater than$gte: Greater than or equal to$lt: Less than$lte: Less than or equal to$in: Matches any of the values specified in an array$nin: Does not match any of the values specified in an array$and,$or,$not,$nor: Logical operators
c. Projection: Selecting Specific Fields
To return only a subset of fields, pass a projection document as the second argument to find(). A value of 1 includes the field, 0 excludes it. The _id field is included by default unless explicitly excluded.
// Return only the name and email, exclude _id
db.users.find({ city: "New York" }, { name: 1, email: 1, _id: 0 });
d. Sorting, Limiting, and Skipping
Chaining methods allows for more complex queries:
// Sort by age in descending order (1 for ascending, -1 for descending)
db.users.find().sort({ age: -1 });
// Limit results to 2 documents
db.users.find().limit(2);
// Skip the first 2 documents and return the rest
db.users.find().skip(2);
// Combine operations: Find users from New York, sort by age, skip 1, limit 1
db.users.find({ city: "New York" }).sort({ age: 1 }).skip(1).limit(1);
2. db.collection.findOne()
This method returns a single document that matches the query criteria. If multiple documents match, it returns the first one found.
db.users.findOne({ name: "Alice Smith" });
Update Operations
Update operations modify existing documents in a collection. You can update a single document, multiple documents, or even replace an entire document.
1. db.collection.updateOne()
Updates a single document that matches the filter criteria.
// Update Alice's city to 'San Francisco'
db.users.updateOne(
{ name: "Alice Smith" },
{ $set: { city: "San Francisco", lastUpdated: new Date() } }
);
2. db.collection.updateMany()
Updates all documents that match the filter criteria.
// Increment the age of all users in New York by 1
db.users.updateMany(
{ city: "New York" },
{ $inc: { age: 1 } }
);
// Add a new interest 'reading' to users who don't already have it
db.users.updateMany(
{}, // Apply to all documents
{ $addToSet: { interests: "reading" } }
);
// Remove 'gaming' from interests for Bob Johnson
db.users.updateOne(
{ name: "Bob Johnson" },
{ $pull: { interests: "gaming" } }
);
Common Update Operators:
$set: Sets the value of a field in a document. Creates the field if it doesn't exist.$inc: Increments the value of a field by a specified amount.$unset: Removes a field from a document.$push: Appends a value to an array field.$pull: Removes all instances of a value or values that match a specified query from an array.$addToSet: Adds a value to an array only if the value is not already present.
3. db.collection.replaceOne()
Replaces a single document that matches the filter criteria with a new document. The _id field of the document being replaced cannot be changed.
// Replace Bob Johnson's document entirely
db.users.replaceOne(
{ name: "Bob Johnson" },
{
name: "Robert Johnson",
occupation: "Software Engineer",
status: "active",
email: "[email protected]"
}
);
Upsert Option
Both updateOne() and updateMany() support an upsert option. If set to true, and no document matches the filter, MongoDB will insert a new document based on the query and update operations.
db.users.updateOne(
{ name: "David Lee" },
{ $set: { age: 28, city: "Seattle" } },
{ upsert: true } // If David Lee doesn't exist, create him
);
Delete Operations
Delete operations remove documents from a collection. Be extremely careful with delete operations, as they are irreversible.
1. db.collection.deleteOne()
Deletes at most one document that matches the specified filter.
// Delete the user named 'Robert Johnson' (previously 'Bob Johnson')
db.users.deleteOne({ name: "Robert Johnson" });
2. db.collection.deleteMany()
Deletes all documents that match the specified filter.
// Delete all users from London
db.users.deleteMany({ city: "London" });
Warning: To delete all documents in a collection, use an empty filter
{}. Be extremely cautious as this operation cannot be undone:db.users.deleteMany({}); // Deletes all documents in the 'users' collection
3. db.collection.drop()
This method permanently removes an entire collection from the database, including all its documents and indexes.
db.users.drop(); // Deletes the entire 'users' collection
Warning: Dropping a collection is a highly destructive operation. Ensure you have proper backups or are absolutely certain before executing this command.
Best Practices for MongoDB CRUD
- Indexing: For frequently queried fields, create indexes to significantly speed up read operations.
db.collection.createIndex({ fieldName: 1 }). - Projections: Only retrieve the data you need. Using projections (
{ field: 1 }) reduces network bandwidth and memory usage. - Batch Operations: When inserting, updating, or deleting many documents, use
insertMany(),updateMany(), anddeleteMany()instead of individual operations to reduce overhead. - Understand Operators: Familiarize yourself with MongoDB's rich set of query and update operators. They offer powerful ways to manipulate your data.
- Error Handling: In a production application, always implement robust error handling for your database operations.
- Schema Design: While MongoDB is schema-less, thoughtful schema design is crucial for efficient queries and data consistency.
A Safer Way to Practice CRUD in a Real Database
The dangerous part of MongoDB CRUD is not the syntax. It is running a broad filter in the wrong database, especially with updateMany() or deleteMany(). I like to use a three-step habit for any write that touches existing data.
First, run the filter as a read:
db.users.find(
{ city: "New York", status: "inactive" },
{ name: 1, email: 1, city: 1, status: 1 }
).limit(20);
If the preview returns documents you did not expect, stop there. Fix the filter before you think about the update. If it returns nothing, make sure you are in the right database with db.getName() and that the field names match the actual documents.
Second, count the matching documents:
db.users.countDocuments({ city: "New York", status: "inactive" });
This gives you a rough blast radius. If you expected 12 users and the count says 12,000, the command is telling you something is wrong. A count is not a substitute for a backup, but it is a cheap guardrail.
Third, run the write with the narrowest command that fits the job. Use updateOne() when a unique email, account id, or _id should match one document. Use updateMany() only when changing a whole segment is intentional.
db.users.updateMany(
{ city: "New York", status: "inactive" },
{
$set: {
marketingEmailEnabled: false,
updatedBy: "ops-maintenance-2025-11-04"
}
}
);
Adding an updatedBy, updatedAt, or maintenance note is not required by MongoDB, but it helps later when someone asks why a field changed.
Common Mistakes That Cause Real Bugs
The first mistake is replacing a document when you meant to update a field. This command looks harmless, but it replaces the entire matching document with only the fields shown:
db.users.updateOne(
{ email: "[email protected]" },
{ city: "Boston" }
);
Modern MongoDB expects update operators for update documents unless you are using replacement-style methods, so this pattern may fail depending on the command and version. The safer mental model is simple: if you are changing fields in place, use $set, $unset, $inc, $push, $pull, or another update operator.
db.users.updateOne(
{ email: "[email protected]" },
{ $set: { city: "Boston" } }
);
The second mistake is querying arrays as if order always matters. { interests: ["reading", "hiking"] } matches an array exactly. { interests: "reading" } matches documents where the array contains that value. { interests: { $all: ["reading", "hiking"] } } matches arrays that contain both values, regardless of order. Those are three different questions.
The third mistake is assuming findOne() returns "the right one" when the filter is not unique. If you run:
db.users.findOne({ city: "New York" });
MongoDB returns one matching document, but without a sort you should not treat that document as the newest, oldest, most important, or most representative. If order matters, say so:
db.users.find({ city: "New York" }).sort({ createdAt: -1 }).limit(1);
The fourth mistake is skipping indexes until the app is already slow. A collection with a few thousand documents can hide an inefficient query. The same query can become painful when the collection grows. If the application frequently finds users by email, create a unique index when the data model allows it:
db.users.createIndex({ email: 1 }, { unique: true });
That protects both performance and data quality. If duplicate emails already exist, the command will fail, which is exactly the kind of problem you want to discover before relying on email as an identifier.
Checking What a Write Actually Did
MongoDB write results are worth reading. After an update, look at matchedCount and modifiedCount. matchedCount tells you how many documents matched the filter. modifiedCount tells you how many were actually changed.
If matchedCount is 1 and modifiedCount is 0, the command may still be fine. Maybe the field already had the requested value. If matchedCount is 0, your filter did not match anything. That is common when an _id is passed as a string instead of an ObjectId.
db.users.findOne({ _id: ObjectId("6650f1e59d0a41a37c2d8011") });
For deletes, check deletedCount. If you expected one deleted document and the result says deletedCount: 0, do not immediately run a broader delete. Recheck the database, collection, and filter.
When CRUD Is Not Enough
Basic CRUD commands cover most day-to-day data work, but some tasks need stronger tools. If you are updating several collections that must stay consistent together, look at transactions on replica sets or sharded clusters. If you are reshaping data across many documents, an aggregation pipeline may be clearer than a long series of client-side loops. If you are migrating production data, use a script with logging, dry-run mode, backups, and a rollback plan.
For one-off operations in mongosh, keep the commands readable. A clever one-liner is harder to review and harder to recover from. In production, boring commands are usually better.
MongoDB CRUD commands are straightforward once you get used to documents, filters, and update operators. The skill that matters in real work is being deliberate: preview the filter, count the impact, choose the narrowest write command, read the result, and leave enough context that the next person can understand what changed.