Structure Stages
Reshape documents — add fields, project subsets, rename, and replace roots.
← Previous | Index | Next: 06 - Relationship Stages →
Structure stages change what each document looks like — they add, remove, rename, or compute fields. The number of documents in the stream stays the same.
$addFields — Add New Fields
Adds new fields to each document while keeping all existing fields.
$addFields is the complement of $project — it only adds, never removes.
db.<collection>.aggregate([
{
$addFields: {
<newField>: <expression>
}
}
]);The $ operator lets you reference existing field values.
Example: Add a constant value
db.projects.aggregate([
{ $match: { type: "MANAGEMENT_PROJECT" } },
{
$addFields: {
verified: true,
keyword: ["initialised"]
}
}
]);Output (all original fields are preserved, plus the new ones):
{
_id: ObjectId("...3"),
title: "Simulation Systems",
type: "MANAGEMENT_PROJECT",
state: "IN_APPROVEMENT",
fundings: [...],
reviews: [4, 5, 4, 5, 5],
keyword: ["initialised"], // ← new
verified: true // ← new
}Example: Copy an existing field’s value
db.projects.aggregate([
{ $match: { type: "MANAGEMENT_PROJECT" } },
{
$addFields: {
description: "$title" // ← uses $ to reference the title field
}
}
]);The resulting document will have description: "Simulation Systems".
Example: Use aggregate operators on arrays
You can apply operators like $sum, $min, $max, $size directly on array fields:
db.projects.aggregate([
{
$addFields: {
voteCount: { $sum: "$reviews" },
minVote: { $min: "$reviews" },
maxVote: { $max: "$reviews" },
groupCount: { $size: "$reviews" }
}
}
]);Output (for the “Simulation Systems” document):
{
// ...all original fields...
reviews: [4, 5, 4, 5, 5],
voteCount: 23, // 4+5+4+5+5
minVote: 4,
maxVote: 5,
groupCount: 5 // array has 5 elements
}$project — Select and Transform Fields
$project limits which fields appear in the output. Only fields you explicitly include (set to 1) will pass through.
$project is the complement of $addFields — it controls exactly which fields survive.
db.<collection>.aggregate([
{ $project: { <fieldname>: (0 | 1) } }
]);How it works
fieldname: 1→ include this fieldfieldname: 0→ exclude this fieldnewName: "$existingField"→ rename a fieldnewField: { <expression> }→ compute a new field
Example: Project, rename, and compute
db.projects.aggregate([
{ $match: { type: "MANAGEMENT_PROJECT" } },
{
$project: {
// Include existing fields
title: 1,
reviews: 1,
// Rename fields
projectType: "$type",
projectState: "$state",
// Compute new fields with aggregate operators
maxVote: { $max: "$reviews" },
minVote: { $min: "$reviews" }
}
}
]);Output:
{
title: "Simulation Systems",
projectType: "MANAGEMENT_PROJECT",
reviews: [4, 5, 4, 5, 5],
maxVote: 5,
minVote: 4
}Notice:
state,type,fundings,_idare gone — only projected fields remain.
$addFields vs $project — When to Use Which?
Use $addFields when… | Use $project when… |
|---|---|
| You want to add fields but keep everything else | You want precise control over which fields appear |
| You don’t want to list every field to keep | You want to exclude fields or rename them |
| Quick enrichment of documents | Final output shaping |
$replaceRoot — Replace the Document Root
Replaces the entire document with one of its sub-documents (or a computed document).
db.<collection>.aggregate([
{
$replaceRoot: {
newRoot: <replacementDocument>
}
}
]);Example: Promote a nested object to root
Suppose you have tweet documents with a nested user object:
db.tweets.aggregate([
{ $match: { lang: "en" } },
{
$replaceRoot: {
newRoot: "$user"
}
}
]);Output — the user sub-document becomes the entire document:
{
name: "Jonas Nagelmaier",
verified: false,
rating: 5,
state: "initialised"
}When to use: After a
$lookupor when working with deeply nested data that you want to “flatten” into the top level.
Next: 06 - Relationship Stages — join data from other collections.