$addFields, $replaceRoot, $mergeObjects
학습자는 계산된 필드를 추가하고, 중첩된 하위 문서를 루트 수준으로 승격하며, 파이프라인 내부에서 객체를 병합합니다.
$addFields, $replaceRoot, $mergeObjects은(는) CoddyKit의 무료 MongoDB Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 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” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 MongoDB Academy 강의 전체를 잠금 해제할 수 있습니다. MongoDB Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“$addFields, $replaceRoot, $mergeObjects”에서 뭘 배우나요?
학습자는 계산된 필드를 추가하고, 중첩된 하위 문서를 루트 수준으로 승격하며, 파이프라인 내부에서 객체를 병합합니다. 브라우저에서 직접 실행하는 실습 코드로 MongoDB Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
MongoDB Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 MongoDB Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“$addFields, $replaceRoot, $mergeObjects” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 MongoDB Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 MongoDB Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- $lookup: 파이프라인에서 컬렉션 조인하기
- $unwind: 배열 필드 분해하기
- $addFields, $replaceRoot, $mergeObjects
- $out과 $merge: 파이프라인 결과 기록하기