Perform an insert-or-update operation in MongoDB using the 'upsert' option with updateOne, replaceOne, or updateMany methods, which automatically inserts a new document if no match is found
An upsert operation follows a simple decision flow. When you execute an update command with upsert: true, MongoDB first searches for a document matching the query filter. If a match is found, it performs the specified update (using operators like $set, $inc, etc.) on that document. If no match is found, MongoDB creates a new document by combining fields from the query filter and the update document, then inserts it. This all happens as a single atomic operation, ensuring data consistency.
When performing upserts, the $setOnInsert operator is particularly useful. It specifies fields that should only be set when the operation results in an insertion. If the operation updates an existing document, $setOnInsert fields are ignored. This is perfect for setting default values like creation timestamps, initial counters, or default permissions that shouldn't be modified on subsequent updates.
The findOneAndUpdate method with upsert: true is useful when you need to retrieve the document after the operation. Combined with returnNewDocument: true, you can get the document as it appears after the upsert—either the updated existing document or the newly inserted one. This is particularly valuable when you need to work with the document's data immediately after the operation.
Unique indexes: If you have multiple identical upsert operations running concurrently without a unique index on the query fields, you may end up with duplicate documents. Always create unique indexes on fields used in upsert queries to prevent this.
$expr limitation: You cannot use the $expr operator in the query filter when performing an upsert. MongoDB will throw an error if you attempt this.
Query filter inclusion: When an upsert inserts a new document, it includes fields from the equality conditions in the query filter. For example, updateOne({ email: 'x' }, { $set: { name: 'X' } }, { upsert: true }) will insert a document with both email and name fields.
updateMany with upsert: When using updateMany with upsert: true, if no documents match the filter, only a single document is inserted, not one for each potential variation.
replaceOne behavior: With replaceOne, the entire document is replaced on update. Any fields not included in the replacement document will be lost, so ensure your replacement document includes all necessary fields.
A common real-world use case for upserts is updating user profile information. When a user logs in, you want to update their last login timestamp. If this is their first login and they don't exist in your users collection, you want to create a profile for them. Using an upsert with $set and $setOnInsert elegantly handles both scenarios in a single database operation, eliminating race conditions and simplifying your application code.