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

Javascript
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):

Javascript
{
  acknowledged: true,
  insertedId: ObjectId("65f1a2b3c4d5e6f7a8b9c0d1")
}

find and query operators

find() with no arguments (or {}) returns every document in the collection:

Javascript
db.products.find();

Pass a filter object to narrow the results — matching an exact field value:

Javascript
db.products.find({ category: "Electronics" });

Query operators (always prefixed with $) express comparisons beyond plain equality:

Javascript
// $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):

Javascript
// 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:

Javascript
// 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:

Javascript
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:

Javascript
// $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

Javascript
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/updateMany without $set and accidentally replacing the entire document instead of updating one field.
  • Running deleteMany({}) (or updateMany({}, ...)) by accident when a specific filter was intended — an empty filter object matches everything.
  • Assuming updateOne/deleteOne operate 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 _id or 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 (in mongosh, the shell prints cursor results directly for convenience).