deleteOne removes the first document the filter matches, deleteMany removes all of them, and both report only a deletedCount. A delete filter takes query operators but never update operators, and there is no undo.
const d = (l, r) => console.log(l, r.deletedCount);
d("one 2017 :", db.books.deleteOne({ year: 2017 }));
d("pre-2019 :", db.books.deleteMany({ year: { $lt: 2019 } }));
d("no match :", db.books.deleteOne({ title: "Nothing Like This" }));
console.log("taken :", db.books.findOneAndDelete({ title: "Eloquent JavaScript" },
{ projection: { _id: 0, title: 1, year: 1 } }));
console.log("left :", db.books.countDocuments());one 2017 : 1
pre-2019 : 2
no match : 0
taken : { title: 'Eloquent JavaScript', year: 2024 }
left : 1A filter that matches nothing is not an error: deletedCount: 0 is a normal reply, so a route deleting a missing id must check the count itself to answer 404 rather than 204. findOneAndDelete returns the document it removed and is atomic, which suits a queue where exactly one worker may claim an item.
The empty filter is the dangerous one. db.books.deleteMany({}) empties the collection document by document, writing every deletion to the oplog, with no confirmation prompt; db.books.drop() removes the collection, its indexes and its metadata in one operation and is far faster. Beware a {} that came from a request parameter that turned out to be undefined.
Documents also leave by expiring: a TTL index on a date field (Reading an explain Plan) deletes stale sessions, carts or logs with no application code, on the interval in the server parameter ttlMonitorSleepSecs — 60 seconds on a default MongoDB 8.3 1,815 server.