0Pricing
MongoDB Academy · درس

‏$addFields و$replaceRoot و$mergeObjects

سيضيف المتعلمون حقولًا محسوبة، ويرقّون المستندات الفرعية المتداخلة إلى مستوى الجذر، ويدمجون الكائنات داخل المسار.

‏$addFields و$replaceRoot و$mergeObjects درس مجاني في MongoDB Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة MongoDB Academy، انتقل إلى CoddyKit PRO. تتضمن دورة MongoDB Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏$addFields و$replaceRoot و$mergeObjects»؟

سيضيف المتعلمون حقولًا محسوبة، ويرقّون المستندات الفرعية المتداخلة إلى مستوى الجذر، ويدمجون الكائنات داخل المسار. تتمرن على MongoDB Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ MongoDB Academy؟

لا تُشترط خبرة سابقة. MongoDB Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «‏$addFields و$replaceRoot و$mergeObjects»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس MongoDB Academy هذا؟

نعم. كل درس في MongoDB Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ‏$lookup: ربط المجموعات في المسار
  2. ‏$unwind: تفكيك حقول المصفوفات
  3. ‏$addFields و$replaceRoot و$mergeObjects
  4. ‏$out و$merge: كتابة نتائج المسار
← العودة إلى MongoDB Academy