$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 winsPractical 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- $lookup: การเชื่อมคอลเลกชันในไปป์ไลน์
- $unwind: การแยกฟิลด์อาร์เรย์
- $addFields, $replaceRoot และ $mergeObjects
- $out และ $merge: การเขียนผลลัพธ์ของไปป์ไลน์