CRUD Operations
insertOne/insertMany, find with query operators, updateOne/updateMany, and deleteOne.
All examples in this section use a products collection, queried and modified from mongosh.
insertOne / insertMany
db.products.insertOne({
name: "Wireless Mouse",
category: "Electronics",
price: 24.99,
stock: 150,
tags: ["electronics", "accessories"]
});
db.products.insertMany([
{ name: "Mechanical Keyboard", category: "Electronics", price: 89.00, stock: 60 },
{ name: "USB-C Hub", category: "Electronics", price: 39.00, stock: 0 },
{ name: "Desk Lamp", category: "Home", price: 19.50, stock: 200 }
]);
Each call returns an acknowledgment including the generated _id(s):
{
acknowledged: true,
insertedId: ObjectId("65f1a2b3c4d5e6f7a8b9c0d1")
}
find and query operators
find() with no arguments (or {}) returns every document in the collection:
db.products.find();
Pass a filter object to narrow the results — matching an exact field value:
db.products.find({ category: "Electronics" });
Query operators (always prefixed with $) express comparisons beyond plain equality:
// $gt: greater than
db.products.find({ price: { $gt: 30 } });
// $gte, $lt, $lte work the same way
db.products.find({ price: { $gte: 20, $lte: 90 } });
// $in: value is one of a list
db.products.find({ category: { $in: ["Electronics", "Home"] } });
// $ne: not equal
db.products.find({ stock: { $ne: 0 } });
$and and $or combine multiple conditions explicitly (though for simple field-level AND, listing multiple fields in one object already implies AND):
// Implicit AND: category is Electronics AND price > 30
db.products.find({ category: "Electronics", price: { $gt: 30 } });
// Explicit $and (needed when combining conditions on the same field, or nesting $or)
db.products.find({
$and: [
{ category: "Electronics" },
{ $or: [{ price: { $lt: 30 } }, { stock: { $gt: 100 } }] }
]
});
Projections (the second argument to find) limit which fields come back, which matters for both bandwidth and clarity once documents get large:
// 1 = include only these fields (plus _id, unless explicitly excluded)
db.products.find({ category: "Electronics" }, { name: 1, price: 1, _id: 0 });
updateOne / updateMany
Updates require an update operator — $set is by far the most common, replacing only the named fields rather than the whole document:
db.products.updateOne(
{ name: "Wireless Mouse" },
{ $set: { price: 22.99 } }
);
// Updates every matching document, not just the first
db.products.updateMany(
{ category: "Electronics" },
{ $set: { onSale: true } }
);
Other common update operators:
// $inc: increment/decrement a numeric field atomically
db.products.updateOne({ name: "Wireless Mouse" }, { $inc: { stock: -1 } });
// $push: append a value to an array field
db.products.updateOne({ name: "Wireless Mouse" }, { $push: { tags: "bestseller" } });
// $unset: remove a field entirely
db.products.updateOne({ name: "Wireless Mouse" }, { $unset: { onSale: "" } });
Omitting an update operator and passing a plain document (updateOne({name: "..."}, {price: 22.99})) replaces the entire document with just {price: 22.99} — almost never what's intended. Always use $set (or another operator) unless a full replacement is genuinely the goal.
deleteOne / deleteMany
db.products.deleteOne({ name: "USB-C Hub" });
db.products.deleteMany({ stock: 0 });
Like find and update, the filter argument determines which document(s) are affected — deleteOne removes only the first document matching the filter (in whatever order the database encounters them), deleteMany removes all matches. deleteMany({}) with an empty filter removes every document in the collection — a common and dangerous typo.
Common mistakes
- Calling
updateOne/updateManywithout$setand accidentally replacing the entire document instead of updating one field. - Running
deleteMany({})(orupdateMany({}, ...)) by accident when a specific filter was intended — an empty filter object matches everything. - Assuming
updateOne/deleteOneoperate on "the newest" or "the most relevant" matching document — without an explicit sort, they act on whichever matching document the database happens to encounter first, which isn't guaranteed to be meaningful without an_idor other unique filter. - Forgetting that
find()returns a cursor, not an array — code that expects an array back (e.g., calling.length) needs to convert it first, typically with.toArray()in driver code (inmongosh, the shell prints cursor results directly for convenience).