updateOne и updateMany с $set и $unset
Вы измените отдельные поля в одном или нескольких документах, не заменяя документ целиком.
«updateOne и updateMany с $set и $unset» — бесплатный урок MongoDB Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения MongoDB Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс MongoDB Academy содержит 4 уроков всего.
Части этого урока еще не переведены и отображаются на английском.
The Two Main Update Methods
MongoDB provides two primary methods for modifying existing documents:
- updateOne(filter, update, options) — modifies the first document matching the filter. If multiple documents match, only one is updated.
- updateMany(filter, update, options) — modifies all documents matching the filter.
Both methods return a result object with matchedCount (how many documents matched the filter) and modifiedCount (how many were actually changed—some matching documents may already have the target value). Always verify modifiedCount in your application logic.
// updateOne: modify a specific document
const result = await db.collection('users').updateOne(
{ email: 'alice@example.com' }, // filter
{ $set: { lastLoginAt: new Date() } } // update
);
console.log(result.matchedCount); // 1
console.log(result.modifiedCount); // 1
// updateMany: modify all matching documents
const bulkResult = await db.collection('users').updateMany(
{ emailVerified: false },
{ $set: { needsVerification: true } }
);$set: Modifying Specific Fields
The $set operator sets the value of one or more fields. If a field does not exist, $set creates it. If it already exists, $set replaces its value. Only the specified fields are changed—all other fields in the document remain untouched.
This is the most important update operator and the one you will use in the vast majority of update operations. Never pass a plain document as the update argument without an operator—doing so replaces the entire document, losing all other fields.
// $set updates only the specified fields
db.users.updateOne(
{ _id: userId },
{ $set: { name: 'Alice Smith', updatedAt: new Date() } }
);
// Only 'name' and 'updatedAt' change; all other fields are preserved
// $set creates the field if it doesn't exist:
db.users.updateOne(
{ _id: userId },
{ $set: { newsletterOptIn: true } } // Added to doc even if it wasn't there
);
// $set on a nested field:
db.users.updateOne(
{ _id: userId },
{ $set: { 'address.city': 'New York' } } // Dot notation for nestedThe Whole-Document Replacement Trap
A critical MongoDB beginner mistake: if you pass a plain document as the update argument—without any update operators—MongoDB treats it as a full document replacement. The matched document is replaced entirely with the new document (preserving only _id). All other fields are lost.
This behavior is actually intentional and useful for replacing an entire document, but it is a common source of accidental data loss. Always use update operators ($set, $inc, etc.) unless you explicitly intend to replace the full document.
// DANGER: This REPLACES the entire document!
db.users.updateOne(
{ _id: userId },
{ name: 'Alice Smith' } // No $set operator!
);
// Before: { _id: ..., name: 'Alice', email: 'alice@...', age: 28 }
// After: { _id: ..., name: 'Alice Smith' } <-- email and age are GONE!
// CORRECT: Use $set to modify only name
db.users.updateOne(
{ _id: userId },
{ $set: { name: 'Alice Smith' } } // Only name changes
);$unset: Removing Fields
The $unset operator removes one or more fields from a document. The value you assign in $unset is ignored—conventionally set to '' or 1. The field is completely removed from the document, not set to null.
Use $unset when you want to clean up legacy fields during a schema migration, remove sensitive data (like a password reset token after it is used), or strip fields that should no longer be stored on certain documents.
// Remove specific fields from a document
db.users.updateOne(
{ _id: userId },
{ $unset: { passwordResetToken: '', passwordResetExpiry: '' } }
);
// Both fields are completely removed from the document
// $unset during schema migration: remove legacy field from all docs
db.users.updateMany(
{ legacyField: { $exists: true } },
{ $unset: { legacyField: '' } }
);
// Removes 'legacyField' from every document that has itCombining $set and $unset
You can combine multiple update operators in a single updateOne or updateMany call. They are applied atomically in a single operation—there is no window where the document is in a partially updated state.
This is important for schema migrations where you simultaneously add a new field and remove an old one. Doing it in one atomic operation prevents any client from reading a document in the transitional state where both old and new fields exist.
// Atomic schema migration: rename 'displayName' to 'username'
// (set new field, copy value from old field, remove old field)
db.users.updateMany(
{ displayName: { $exists: true } },
{
$rename: { displayName: 'username' } // Atomic rename
// $rename is equivalent to $set + $unset in one operator
}
);
// Or manually with $set + $unset + $currentDate:
db.orders.updateOne(
{ _id: orderId },
{
$set: { status: 'shipped', trackingNumber: 'UPS123' },
$unset: { processingNote: '' },
$currentDate: { shippedAt: true }
}
);$rename: Renaming Fields
The $rename operator atomically renames a field within a document. It is equivalent to $seting the new name with the old value and then $unseting the old name—but done in a single atomic step.
Use $rename when refactoring your schema and changing field names. It works correctly even when the destination field does not exist. If the source field does not exist, $rename does nothing (no error). It cannot rename fields across embedded documents.
// Rename 'fname' and 'lname' to 'firstName' and 'lastName'
db.users.updateMany(
{}, // All documents
{ $rename: { fname: 'firstName', lname: 'lastName' } }
);
// Before: { _id: ..., fname: 'Alice', lname: 'Smith' }
// After: { _id: ..., firstName: 'Alice', lastName: 'Smith' }
// If source field doesn't exist, $rename silently skips
// No error thrown$currentDate: Setting Timestamps
The $currentDate operator sets a field to the current date/time at the moment the update is executed on the server. Using server-side timestamps is more reliable than passing a date from the application, because it avoids clock skew between multiple application servers.
Set the value to true to use the BSON Date type, or { $type: 'timestamp' } for the BSON Timestamp type (used internally by the replication system). In application code, BSON Date is almost always what you want.
// Set multiple timestamp fields atomically
db.orders.updateOne(
{ _id: orderId, status: 'pending' },
{
$set: { status: 'processing' },
$currentDate: {
processingStartedAt: true, // BSON Date
lastModified: true
}
}
);
// processingStartedAt = server-side current time
// More reliable than Date.now() from applicationThe upsert Option
Setting { upsert: true } in the options tells MongoDB: if no document matches the filter, insert a new document that combines the filter fields and the update. This is the upsert (update-or-insert) pattern—useful for 'insert if not exists, update if exists' semantics without a separate read operation.
The inserted document contains the fields from both the filter and the $set. Check result.upsertedId to see if an insert occurred. Upserts are atomic—no race condition between checking existence and inserting.
// Upsert: update if exists, insert if not
const result = await db.collection('userPreferences').updateOne(
{ userId: userId }, // filter
{
$set: { theme: 'dark', language: 'en' }, // update
$setOnInsert: { createdAt: new Date() } // only on insert
},
{ upsert: true } // option
);
if (result.upsertedId) {
console.log('New preferences created:', result.upsertedId);
} else {
console.log('Existing preferences updated');
}updateMany: Bulk Field Changes
updateMany applies the same update to all matching documents in one operation. This is how you perform bulk schema migrations, add default values to missing fields, or update a batch of records based on a condition.
Be careful with updateMany({}, ...) (empty filter)—it modifies every document in the collection. Always include a filter that limits the scope. Consider adding a condition like { newField: { $exists: false } } to only process documents that need the change.
// Add default value to all existing docs missing the field
await db.collection('products').updateMany(
{ featured: { $exists: false } }, // Only docs without the field
{ $set: { featured: false } } // Set default
);
// Batch status update
await db.collection('orders').updateMany(
{ status: 'processing', createdAt: { $lt: new Date(Date.now() - 86400000 * 7) } },
{ $set: { status: 'stalled', stalledAt: new Date() } }
);
// Flag orders processing for more than 7 daysfindOneAndUpdate: Atomic Read-Modify
findOneAndUpdate(filter, update, options) atomically finds a document, updates it, and returns it in a single operation. This eliminates the race condition that exists when you do a separate findOne followed by an updateOne.
By default it returns the document before the update. Set { returnDocument: 'after' } to get the updated version. Combine with upsert: true to find-or-create patterns. This is ideal for claiming queue items, implementing counters, and any pattern where you need to both modify and read the document atomically.
// Claim the next pending job atomically
const job = await db.collection('jobs').findOneAndUpdate(
{ status: 'pending' }, // filter
{
$set: { status: 'claimed', workerId: workerId, claimedAt: new Date() }
},
{
sort: { priority: -1, createdAt: 1 }, // Pick highest priority, oldest
returnDocument: 'after' // Return updated document
}
);
if (job) {
await processJob(job); // job is fully updated in DB already
}Update Operators Reference Summary
MongoDB provides many update operators beyond and . Here is a quick reference of the most important ones you will encounter:
- : Set field value (create if absent)
- : Remove a field completely
- : Rename a field atomically
- : Increment/decrement a numeric field
- : Multiply a numeric field
- /: Conditionally update to smaller/larger value
- /: Add/remove array elements
- : Add to array only if unique
- : Set to server-side current date/time
// Multiple operators in one atomic update
db.userStats.updateOne(
{ userId: userId },
{
: { lastSeen: new Date(), status: 'active' },
: { pageViews: 1, sessionCount: 1 },
: { longestSession: sessionDuration },
: { visitedPages: currentPage },
: { updatedAt: true }
},
{ upsert: true }
);
// All six operators apply atomically in one round tripQuick Check
Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.
Lesson Recap
In this lesson you learned: $set modifies specific fields without touching others—always use it rather than passing a bare document to avoid accidental full-document replacement, $unset removes fields entirely and is essential for schema migrations and cleaning up sensitive temporary data like password reset tokens, and upsert: true combined with $setOnInsert enables atomic find-or-create patterns without race conditions. Next up we explore numeric mutation operators: $inc, $mul, $min, and $max.
Часто задаваемые вопросы
Урок «updateOne и updateMany с $set и $unset» бесплатный?
Да — полный текст урока «updateOne и updateMany с $set и $unset» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс MongoDB Academy, подпишись на CoddyKit PRO. Курс MongoDB Academy содержит 4 уроков всего.
Чему я научусь в уроке «updateOne и updateMany с $set и $unset»?
Вы измените отдельные поля в одном или нескольких документах, не заменяя документ целиком. Ты практикуешь MongoDB Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать MongoDB Academy?
Предыдущий опыт не требуется. MongoDB Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «updateOne и updateMany с $set и $unset»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке MongoDB Academy?
Да. Каждый урок MongoDB Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- updateOne и updateMany с $set и $unset
- Операторы увеличения, умножения, минимума и максимума
- Операторы обновления массивов: $push, $pull, $addToSet
- Безопасное удаление документов с deleteOne и deleteMany