0Pricing
MongoDB Academy · Lekcja

updateOne i updateMany z $set i $unset

Zmodyfikują Państwo określone pola w jednym lub wielu dokumentach bez zastępowania całego dokumentu.

updateOne i updateMany z $set i $unset to bezpłatna lekcja MongoDB Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej MongoDB Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs MongoDB Academy zawiera 4 lekcji w sumie.

Części tej lekcji nie zostały jeszcze przetłumaczone i są wyświetlane po angielsku.

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 nested

The 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 it

Combining $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 application

The 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 days

findOneAndUpdate: 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 trip

Quick 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.

Często zadawane pytania

Czy lekcja „updateOne i updateMany z $set i $unset” jest bezpłatna?

Tak — pełny tekst „updateOne i updateMany z $set i $unset” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu MongoDB Academy, przejdź na CoddyKit PRO. Kurs MongoDB Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „updateOne i updateMany z $set i $unset”?

Zmodyfikują Państwo określone pola w jednym lub wielu dokumentach bez zastępowania całego dokumentu. Ćwiczysz MongoDB Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć MongoDB Academy?

Nie wymagamy żadnego doświadczenia. MongoDB Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.

Ile czasu zajmuje lekcja „updateOne i updateMany z $set i $unset”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji MongoDB Academy?

Tak. Każda lekcja MongoDB Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. updateOne i updateMany z $set i $unset
  2. Operatory inkrementacji, mnożenia oraz min/max
  3. Operatory aktualizacji tablic: $push, $pull, $addToSet
  4. Bezpieczne usuwanie dokumentów za pomocą deleteOne i deleteMany
← Powrót do MongoDB Academy