MongoDB: Java

Sefism.

Chapter 02

Reading and writing documents

Inserting, finding, updating and deleting documents: filters and query operators, projections, sorting and paging, update operators, upserts and cursors.

insertOne and insertMany

Also called insert, create, insertedId

What
insertOne adds one document and returns its _id; insertMany adds an array of documents in one round trip. Missing _id values are generated for you.
Why
Bulk inserts with insertMany are much faster than a loop of insertOne calls, which pays a network round trip each time.
JavaScript
db.products.insertOne({
  name: "Desk lamp", price: 25, tags: ["home"]
})
db.products.insertMany(
  [{ name: "Mug", price: 8 }, { name: "Pen", price: 2 }],
  { ordered: false }   // keep going after an error
)

Watch out

Assuming a failed insertMany inserted nothing: with ordered inserts, documents before the failing one are already saved.

find and query filters

Also called find, findOne, query, filter document

What
find takes a filter document and returns every matching document; findOne returns the first match. Fields listed in one filter are combined with AND.
Why
Every read starts here, and filtering in the database sends back only what you need and lets it use indexes.
JavaScript
db.products.find({ category: "books", inStock: true })
db.users.findOne({ email: "sara@example.com" })
db.products.find({})     // every document

Watch out

Repeating a key in one filter object, such as two price conditions: the second silently replaces the first.

Query operators

Also called $gt, $lt, $in, $ne, $or, $exists, $regex, comparison operators

What
Operators starting with $ express conditions: $eq, $ne, $gt, $gte, $lt, $lte, $in and $nin compare values, $and, $or and $not combine conditions, $exists and $type check fields.
Why
They turn a filter from exact matches into ranges, lists and alternatives, the MongoDB version of a SQL WHERE clause.
JavaScript
db.products.find({
  price: { $gte: 10, $lt: 50 },
  category: { $in: ["books", "games"] },
  $or: [{ onSale: true }, { stock: { $gt: 100 } }],
  discontinuedAt: { $exists: false }
})

Watch out

Expecting { field: null } to match only nulls: it also matches documents where the field is missing.

Projection

Also called fields, select fields, exclude fields

What
The second argument to find, listing the fields to return (1) or leave out (0). _id is returned unless you exclude it.
Why
Returning only the fields you need saves bandwidth and memory, and it is what makes covered queries possible.
JavaScript
db.users.find(
  { country: "PK" },
  { name: 1, email: 1, _id: 0 }
)

Watch out

Mixing 1 and 0 in one projection: it is an error, apart from excluding _id.

sort, skip and limit

Also called sort, limit, skip, pagination, offset

What
Cursor methods that order results by one or more fields (1 ascending, -1 descending), skip a number of documents and cap how many come back.
Why
Lists, feeds and "top N" queries all depend on a defined order. Without sort, the order of results is not defined.
JavaScript
db.products.find({ category: "books" })
  .sort({ price: -1, _id: 1 })
  .skip(20)
  .limit(10)

Watch out

Paging deep with skip: skip(100000) still walks past 100,000 documents on every request.

Update operators

Also called $set, $inc, $unset, $rename, $currentDate, updateOne, updateMany

What
updateOne and updateMany take a filter and an update document of operators: $set sets fields, $unset removes them, $inc adds to a number, $currentDate stamps the time.
Why
Operators change only the named fields atomically on the server, so two users incrementing a counter at once both count.
JavaScript
db.products.updateOne(
  { _id: 42 },
  {
    $inc: { stock: -1 },
    $set: { lastSoldAt: new Date() }
  }
)

Watch out

Reading a document, changing it in code and saving it back, which loses concurrent changes that $inc would have kept.

Upsert

Also called upsert: true, $setOnInsert, insert or update

What
An update with upsert: true inserts a new document when nothing matches the filter, built from the filter and the update. $setOnInsert sets fields only on that insert.
Why
Counters, "save settings" actions and syncing data need insert-or-update in one call, without a race between checking and writing.
JavaScript
db.pageViews.updateOne(
  { page: "/pricing", day: "2026-09-28" },
  {
    $inc: { views: 1 },
    $setOnInsert: { firstSeenAt: new Date() }
  },
  { upsert: true }
)

Watch out

Upserting without a unique index on the filter fields, so two concurrent requests can both insert a document.

replaceOne versus updateOne

Also called replaceOne, replace document, full update

What
replaceOne swaps the whole document, apart from _id, for the one you pass. updateOne changes only the fields named by its operators and refuses a plain document.
Why
Choosing the wrong one either deletes fields you meant to keep or makes you rebuild whole documents for a one-field change.
JavaScript
// keeps every other field
db.users.updateOne({ _id: 7 }, { $set: { name: "Ali" } })
// the document becomes exactly this
db.users.replaceOne({ _id: 7 }, { name: "Ali" })

Watch out

Using replaceOne to change one field and wiping the email, roles and everything else in the document.

deleteOne and deleteMany

Also called delete, remove, drop, deleteMany

What
deleteOne removes the first matching document and deleteMany removes every match. drop removes the whole collection with its indexes.
Why
They are the commands most likely to cause real damage, so run the same filter through find or countDocuments first.
JavaScript
const filter = { status: "draft",
  updatedAt: { $lt: ISODate("2026-01-01") } }
db.posts.countDocuments(filter)   // check first
db.posts.deleteMany(filter)

Watch out

Calling deleteMany({}) with an empty filter, which deletes every document in the collection.

Cursor

Also called batch, it, toArray, getMore

What
find returns a cursor, a pointer to the result that fetches documents from the server in batches as you iterate. mongosh prints the first 20; type it for more.
Why
Cursors let you stream results far larger than memory, one batch at a time, instead of loading everything at once.
JavaScript
const cur = db.orders.find({ status: "paid" })
while (cur.hasNext()) {
  const o = cur.next()
  print(o._id, o.total)
}

Watch out

Calling toArray on a huge result, which loads every document into memory, or leaving a cursor idle until the server times it out.

02 · Reading and writing documents2 / 9