What is the difference between
updateOne,updateManyandreplaceOne?updateOnechanges the first matching document andupdateManyevery match; both take update operators such as$set, so fields you do not name stay.replaceOneswaps the whole document for a new one, keeping only_id: fields you leave out are gone. The driver refuses an update document without operators, which stops you from replacing a document by accident.Which update operators should you know?
$setand$unset(add, change or remove a field),$incand$mul(arithmetic),$minand$max(keep the smaller or larger value),$rename,$currentDate, and for arrays$push,$addToSet,$pulland$pop. One update can use several operators, and the whole update of one document is atomic.$incon a missing field creates it.Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const items = client.db('shop').collection('items'); await items.insertOne({ sku: 'pen', stock: 10, price: 20, promo: true }); await items.updateOne( { sku: 'pen' }, { $inc: { stock: -3, sold: 3 }, $min: { price: 15 }, $unset: { promo: '' } } ); const pen = await items.findOne({ sku: 'pen' }); console.log(pen.stock, pen.sold, pen.price, pen.promo); } main().finally(() => client.close());What is the difference between
$pushand$addToSet?Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const posts = client.db('blog').collection('posts'); await posts.insertOne({ _id: 1, pushed: ['db'], added: ['db'] }); for (const tag of ['db', 'node']) { await posts.updateOne({ _id: 1 }, { $push: { pushed: tag }, $addToSet: { added: tag } }); } const post = await posts.findOne({ _id: 1 }); console.log(post.pushed.join(','), '|', post.added.join(',')); } main().finally(() => client.close());It prints
db,db,node | db,node.$pushalways appends, sopushedgets a seconddb.$addToSetappends only values not already in the array, soaddedskipsdband gainsnode.$addToSetdoes not remove duplicates that are already there. Both take$eachto add several values, and$pullremoves every element equal to a value.What does an upsert do when no document matches?
Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const carts = client.db('shop').collection('carts'); const update = { $inc: { items: 2 }, $setOnInsert: { createdBy: 'web' } }; const first = await carts.updateOne({ user: 'asha' }, update, { upsert: true }); const second = await carts.updateOne({ user: 'asha' }, update, { upsert: true }); console.log(first.matchedCount, first.upsertedCount, second.matchedCount, second.modifiedCount); const cart = await carts.findOne({ user: 'asha' }); console.log(cart.items, cart.createdBy); } main().finally(() => client.close());It prints
0 1 1 1and4 web. Withupsert: true, no match means insert: MongoDB builds the document from the filter's equality fields (user: 'asha'), then applies the update, soitemsstarts at 2.$setOnInsertfields are written only on that insert. The second call matches and modifies. Two concurrent upserts can both insert unless a unique index covers the filter field.What is the difference between
matchedCountandmodifiedCount?Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const users = client.db('shop').collection('users'); await users.insertMany([ { name: 'Asha', active: true }, { name: 'Ravi', active: false }, { name: 'Meera', active: true }, ]); const result = await users.updateMany({}, { $set: { active: true } }); console.log(result.matchedCount, result.modifiedCount); } main().finally(() => client.close());It prints
3 1.matchedCountcounts the documents the filter selected;modifiedCountcounts the ones that actually changed. Two users already hadactive: true, and MongoDB does not rewrite a document when the update would leave it the same. CheckmatchedCountto know a record exists,modifiedCountto know something changed.What does
findOneAndUpdatereturn by default?Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const counters = client.db('shop').collection('counters'); await counters.insertOne({ _id: 'order', seq: 41 }); const a = await counters.findOneAndUpdate({ _id: 'order' }, { $inc: { seq: 1 } }); const b = await counters.findOneAndUpdate( { _id: 'order' }, { $inc: { seq: 1 } }, { returnDocument: 'after' } ); console.log(a.seq, b.seq); } main().finally(() => client.close());It prints
41 43.findOneAndUpdatefinds, updates and returns one document atomically, and by default it returns the document as it was before the update;returnDocument: 'after'returns the new version. That atomicity is why it is the standard way to hand out sequence numbers: two callers never get the same value. Driver 6 returns the document itself; older drivers wrapped it in{ value }.How do you update one element inside an array?
With the positional operators.
$updates the first element the query matched (the array field must be in the filter),$[]updates every element, and$[name]witharrayFiltersupdates the elements that match a condition.// add 1 to the qty of the item with sku 'b' db.orders.updateOne({ _id: 1, 'items.sku': 'b' }, { $inc: { 'items.$.qty': 1 } }) // mark every item as shipped db.orders.updateOne({ _id: 1 }, { $set: { 'items.$[].shipped': true } }) // set qty to 0 for items cheaper than 5 db.orders.updateOne( { _id: 1 }, { $set: { 'items.$[cheap].qty': 0 } }, { arrayFilters: [{ 'cheap.price': { $lt: 5 } }] } )How do you delete documents, and how is that different from
drop?deleteOne(filter)removes the first match anddeleteMany(filter)every match; both returndeletedCount.deleteMany({})empties a collection but keeps its indexes and options, whiledrop()removes the collection with its indexes and is much faster for clearing everything. Many apps soft delete instead (adeletedAtfield), so records can be restored and audited.Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const logs = client.db('app').collection('logs'); await logs.insertMany([{ level: 'info' }, { level: 'error' }, { level: 'info' }]); const one = await logs.deleteOne({ level: 'info' }); const many = await logs.deleteMany({ level: 'info' }); console.log(one.deletedCount, many.deletedCount, await logs.countDocuments()); } main().finally(() => client.close());What happens when one document in
insertManyfails?insertManyis ordered by default: it inserts in sequence and stops at the first error, so the documents before it stay inserted and the rest are skipped. With{ ordered: false }the server tries every document and reports all failures together, which is also faster for bulk loads. Either way there is no rollback; use a transaction if the batch must be all or nothing.try { await users.insertMany(docs, { ordered: false }); } catch (err) { // MongoBulkWriteError console.log(err.insertedCount, err.writeErrors.length); }What is the difference between
countDocumentsandestimatedDocumentCount?countDocuments(filter)counts matching documents by running a query: exact, can use an index, but costs a scan.estimatedDocumentCount()takes no filter and reads the total from collection metadata: instant, but it can be off after an unclean shutdown, and on a sharded cluster it can include orphaned documents. The oldcount()is deprecated in the drivers.Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const orders = client.db('shop').collection('orders'); await orders.insertMany([{ status: 'paid' }, { status: 'new' }, { status: 'paid' }]); console.log(await orders.countDocuments({ status: 'paid' })); console.log(await orders.estimatedDocumentCount()); } main().finally(() => client.close());
MongoDB CRUD Interview Questions
Changing data safely: update operators, upserts, the counts an update returns, array updates, bulk inserts and deletes.