Field update operators are the building blocks of any MongoDB update. They are placed inside an update document
(alongside updateOne, updateMany, findOneAndUpdate, etc.) and tell
MongoDB how to modify each field — as opposed to replacing the whole document.
| Operator | Purpose | Creates if Missing | Errors on Wrong Type |
|---|---|---|---|
$set |
Assign value (create or overwrite) | Yes | No |
$unset |
Remove field entirely | No (no-op) | No |
$inc |
Add / subtract a numeric value | Yes — at increment value | Yes (non-numeric) |
$mul |
Multiply numeric field by a factor | No — SILENT no-op! | Yes (non-numeric) |
$rename |
Rename a field, preserving its value | No | No |
$min |
Update only if new value is LOWER than current | Yes | Depends on comparison |
$max |
Update only if new value is HIGHER than current | Yes | Depends on comparison |
$currentDate |
Set to current server timestamp | Yes | No |
$setOnInsert |
Set fields only when an upsert causes an insert (not an update) | N/A | No |
{ $set: { age: 30 }, $inc: { age: 1 } } throws a conflict error at the server.
If you need both effects, split into two separate update operations.
// Combining multiple operators in a single atomic update — all or nothing db.users.updateOne( { _id: ObjectId("64a1f3e2b5c0d12345678901") }, { $set: { status: "active", "profile.verified": true }, $inc: { loginCount: 1 }, $currentDate: { lastLogin: true } // All three changes are applied atomically — no partial writes } );
// $setOnInsert — only fires during the INSERT half of an upsert db.users.updateOne( { email: "bob@example.com" }, // filter { $set: { lastSeen: new Date() }, // always runs $setOnInsert: { createdAt: new Date(), // only on new document creation role: "viewer" } }, { upsert: true } ); // If doc exists → only $set runs (lastSeen updated) // If doc missing → $set + $setOnInsert both run (new doc with createdAt + role)