0Pricing
MongoDB Academy · บทเรียน

$addFields, $replaceRoot และ $mergeObjects

ผู้เรียนจะเพิ่มฟิลด์ที่คำนวณแล้ว ยกระดับเอกสารย่อยที่ซ้อนอยู่ขึ้นเป็นระดับราก และรวมออบเจ็กต์ภายในไปป์ไลน์

$addFields, $replaceRoot และ $mergeObjects เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

Three Reshaping Stages

While $project is the primary document-reshaping tool, three additional stages offer more targeted transformations: $addFields adds new fields while preserving all existing ones, $replaceRoot promotes a sub-document to become the new root document, and $mergeObjects merges multiple objects into one. Together they cover reshaping patterns that would be verbose or impossible with $project alone.

$addFields: Adding Without Dropping

$addFields (also available as its alias $set) passes through all existing document fields and adds or overwrites only the specified fields. This is the key difference from $project in inclusion mode, where you must explicitly list every field you want to keep. Use $addFields whenever you want to enrich a document with computed fields without listing every existing field.

// $addFields preserves all existing fields
db.products.aggregate([{
  $addFields: {
    // Add computed fields; all original fields (name, price, etc.) are kept
    totalWithTax: { $multiply: ['$price', 1.1] },
    priceLabel: { $concat: ['$', { $toString: '$price' }] },
    isExpensive: { $gt: ['$price', 1000] }
  }
}]);
// All original product fields PLUS the three new computed fields

$set: The Modern Alias for $addFields

MongoDB 4.2 introduced $set as an alias for $addFields in both aggregation pipelines and update operations. The two are completely interchangeable in pipeline context. $set is the preferred modern spelling because it is more intuitive and mirrors the $set update operator used in updateOne(). Use whichever you find more readable.

// $set (modern) and $addFields (classic) are identical
db.orders.aggregate([{
  $set: {  // same as $addFields
    totalWithShipping: { $add: ['$amount', '$shippingFee'] },
    daysToDeliver: {
      $divide: [
        { $subtract: ['$deliveredAt', '$createdAt'] },
        1000 * 60 * 60 * 24
      ]
    }
  }
}]);

Overwriting Existing Fields With $addFields

When you specify a field name that already exists in the document, $addFields overwrites the existing value with the new computed value. This is useful for normalising data—for example, converting a string field to lowercase in the pipeline output without modifying the stored document.

// Normalise email to lowercase in pipeline output
db.users.aggregate([{
  $addFields: {
    email: { $toLower: '$email' }  // overwrites existing email field
  }
}]);
// The stored document is unchanged; only the pipeline output is normalised

// Promote a nested field to the top level by overwriting
db.orders.aggregate([{
  $addFields: {
    city: '$shippingAddress.city'  // copy nested field to top level
  }
}]);

$replaceRoot: Making a Sub-Document the Root

$replaceRoot replaces the entire root document with a specified sub-document or expression. The new root must be an object. This is useful after a $lookup + $unwind when you want the joined document to be the primary shape, or when you want to surface a nested sub-document as the output document while discarding the parent fields.

// Document: { order: { id: 1, amount: 100, customer: { name: 'Alice' } } }

// Make the nested 'order' sub-document the new root
db.transactions.aggregate([{
  $replaceRoot: { newRoot: '$order' }
}]);
// Output: { id: 1, amount: 100, customer: { name: 'Alice' } }
// The outer 'order' wrapper is gone; order's fields are now at the root level

$replaceWith: Alias for $replaceRoot

MongoDB 4.2 introduced $replaceWith as a more concise alias for $replaceRoot. Instead of { $replaceRoot: { newRoot: expr } }, you can write { $replaceWith: expr }. The two are completely equivalent. $replaceWith is often used after $lookup/$unwind to surface the joined document as the primary result.

// After lookup + unwind, make the joined product the root
db.orders.aggregate([
  { $lookup: { from: 'products', localField: 'productId', foreignField: '_id', as: 'product' } },
  { $unwind: '$product' },
  // Promote product to root, but keep orderId
  { $replaceWith: {
    $mergeObjects: ['$product', { orderId: '$_id', orderAmount: '$amount' }]
  }}
]);

$mergeObjects: Combining Two Objects

$mergeObjects merges multiple objects into a single object. When two objects have the same field, the last one wins. It is commonly used inside $replaceWith or $addFields to flatten a joined document and add extra fields at the same time. $mergeObjects can accept an array of objects or be used as a $group accumulator.

// Merge two objects - last key wins on conflict
db.users.aggregate([{
  $replaceWith: {
    $mergeObjects: [
      { defaultRole: 'viewer', isActive: false },  // defaults first
      '$$ROOT',                                     // actual doc overrides defaults
      { processedAt: '$$NOW' }                      // add computed field last
    ]
  }
}]);

$mergeObjects as a $group Accumulator

When used as a $group accumulator, $mergeObjects merges all documents in a group into a single object. This is useful for combining multiple partial documents that together form a complete record—for example, gathering all update events for an entity and merging them into a single 'current state' document.

// Merge all update events per entity into one current state
db.events.aggregate([
  { $sort: { timestamp: 1 } },  // process oldest first
  { $group: {
    _id: '$entityId',
    // Merge all event deltas into one object; later events override earlier
    currentState: { $mergeObjects: '$delta' }
  }}
]);
// Each group's 'delta' objects are merged sequentially, last wins

Practical Pattern: Flatten $lookup Result

A very common pattern is to use $replaceWith + $mergeObjects to flatten a looked-up document into the parent document's root, adding the parent's relevant fields alongside the joined document's fields. This produces a clean, flat output object that's easy to serialise and send to the client.

// Flatten order + product into a single output document
db.orders.aggregate([
  { $lookup: {
    from: 'products',
    localField: 'productId',
    foreignField: '_id',
    as: 'product'
  }},
  { $unwind: '$product' },
  { $replaceWith: {
    $mergeObjects: [
      '$product',                            // all product fields at root
      { orderId: '$_id', qty: '$qty', total: { $multiply: ['$product.price', '$qty'] } }
    ]
  }}
]);
// Clean flat output: { name, category, price, orderId, qty, total }

$unset: Removing Fields

$unset (available since MongoDB 4.2) removes specific fields from documents in the pipeline. It's the inverse of $addFields: while $addFields adds fields without touching others, $unset removes specified fields while passing through everything else. This is cleaner than using $project with all fields set to 1 just to exclude one field.

// Remove sensitive fields from output
db.users.aggregate([{
  $unset: ['password', 'apiKey', '__v']  // array of field names to remove
}]);
// All fields except password, apiKey, and __v are passed through

// Remove a single field
db.orders.aggregate([{ $unset: 'internalNotes' }]);

// Remove nested field with dot notation
db.users.aggregate([{ $unset: 'profile.ssn' }]);

Combining $addFields, $replaceRoot, and $mergeObjects

These three stages work together in sophisticated reshaping pipelines. A typical sequence is: use $addFields to compute derived fields, use $lookup to join related data, use $unwind to flatten the join, then use $replaceWith + $mergeObjects to produce a clean output shape. This combination covers most API response shaping needs without application-side transformation.

db.invoices.aggregate([
  { $addFields: { taxAmount: { $multiply: ['$subtotal', 0.08] } } },
  { $lookup: { from: 'clients', localField: 'clientId', foreignField: '_id', as: 'client' } },
  { $unwind: '$client' },
  { $replaceWith: {
    $mergeObjects: [
      { invoiceId: '$_id', subtotal: '$subtotal', taxAmount: '$taxAmount' },
      { clientName: '$client.name', clientEmail: '$client.email' }
    ]
  }},
  { $unset: ['_id'] }
]);

Quick Check

Test your understanding of $addFields, $replaceRoot, and $mergeObjects.

Lesson Recap

In this lesson you learned: $addFields/$set adds fields while preserving all existing ones, $replaceRoot/$replaceWith promotes a sub-document to the root, and $mergeObjects combines multiple objects with last-key-wins semantics. Next up we explore $out and $merge for writing pipeline results to collections.

คำถามที่พบบ่อย

บทเรียน “$addFields, $replaceRoot และ $mergeObjects” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “$addFields, $replaceRoot และ $mergeObjects” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “$addFields, $replaceRoot และ $mergeObjects”

ผู้เรียนจะเพิ่มฟิลด์ที่คำนวณแล้ว ยกระดับเอกสารย่อยที่ซ้อนอยู่ขึ้นเป็นระดับราก และรวมออบเจ็กต์ภายในไปป์ไลน์ คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “$addFields, $replaceRoot และ $mergeObjects” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม

ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. $lookup: การเชื่อมคอลเลกชันในไปป์ไลน์
  2. $unwind: การแยกฟิลด์อาร์เรย์
  3. $addFields, $replaceRoot และ $mergeObjects
  4. $out และ $merge: การเขียนผลลัพธ์ของไปป์ไลน์
← กลับไปที่ MongoDB Academy