
While you might be familiar with MongoDB’s Aggregation Framework pipelines (primarily used for crafting intricate query results), a lesser-known capability is their application in updating documents.
This article will delve into the utilization of aggregation pipelines for efficient document updates.
Understanding the Basics
Let’s start with an example document representing a breakfast recipe:
{
"_id": {
"$oid": "recipe:toast"
},
"title": "Toast",
"calories_per_serving": 75,
"prep_time": 1,
"cook_time": 4,
"ingredients": [
{
"name": "bread",
"quantity": {
"amount": 4,
"unit": "slice"
},
"vegetarian": true
},
{
"name": "butter",
"quantity": {
"amount": 2,
"unit": "tablespoon"
},
"vegetarian": true
}
],
"directions": [
"Toast bread.",
"When both sides are an even golden brown, butter one side, care being taken to butter the edges.",
"Melt butter.",
"Serve hot."
],
"rating": [5,1,5,2,4,4,5,3,2,5,3,4,1,2,5,3],
"servings": 4,
"tags": ["bread", "quick", "vegetarian"],
"type": "Breakfast",
"vegetarian_option": true
}
Adding Calculated Fields
One powerful use case is the creation of pre-calculated fields. For example, what if we want to show our users a recipe’s “average rating” based on the user ratings in our rating field.
To calculate and add a new rating_avg field to a document (this also works for an update), you can send a aggregation query to updateOne() like this:
db.examples.updateOne(
{ "_id": "recipe:toast" },
[{
$set: {
rating_avg: {
$round: [ { $avg: "$rating" }, 2]
}
}
}]
)
This will add a new rating_avg field to the document:
{
...
"title": "Toast",
...
"rating_avg": 3.38
}
Here we used the $set pipeline stage and two other operators to accomplish the average calculation: $avg and $round.
The $avg operator is used to compute the average of numerical values within an array, such as the rating field in this context. It calculates the “arithmetic mean” by summing up all the values and dividing the result by the total count of elements, providing the average rating.
In tandem with $avg, the $round operator is applied to round numerical values to a specified number of decimal places. Here, $round is used on the outcome of $avg to round the average rating to two decimal places, ensuring a more concise and standardized representation in the rating_avg field.
To update this field for all documents in a collection that match a certain criteria, we can use updateMany():
db.examples.updateMany(
{ type: "Dessert" },
[{
$set: {
rating_avg: {
$round: [ { $avg: "$rating" }, 2]
}
}
}]
)
Handling Null Values
To avoid adding the rating_avg field with a value of null for documents without a rating field, you can modify the query to use an $exists query:
db.examples.updateMany(
{ rating: { $exists: true } },
[{
$set: {
rating_avg: {
$round: [ { $avg: "$rating" }, 2]
}
}
}]
)
Expressive Updates with $replaceWith
For more expressive update statements, another option is to use the $replaceWith stage:
db.examples.updateOne(
{ _id: "recipe:toast" },
[{
$replaceWith: {
$setField: {
field: "rating_avg",
input: "$$ROOT",
value: {
$round: [ { $avg: "$rating" }, 2]
}
}
}
}]
)
This offers a clear and concise way to update fields.
Exploring Advanced Stages
MongoDB pipelines offer various stages for more advanced updates. For example, $lookup enables SQL-like joins between documents in different collections (if they are in the same database). Another stage, $merge, allows writing results directly to a collection. Note that $merge must be the last stage in a pipeline.
Whether you’re adding pre-calculated fields or performing expressive updates, mastering MongoDB pipelines can enhance the efficiency of your document updates. Explore the different stages available and tailor them to your specific use cases for a seamless document update experience!

