Updates with Aggregation Pipeline
Starting in MongoDB 4.2, you can use the aggregation pipeline for update operations. With the update operations, the aggregation pipeline can consist of the following stages:
Using the aggregation pipeline allows for a more expressive update statement, such as expressing conditional updates based on current field values or updating one field using the value of another field(s).
Example 1
Tip
You can try out the example in the provided shell. Click inside the shell to connect. Once connected, you can run the examples in the shell.
Create an example students
collection (if the collection does
not currently exist, insert operations will create the collection):
db.students.insertMany( [ { _id: 1, test1: 95, test2: 92, test3: 90, modified: new Date("01/05/2020") }, { _id: 2, test1: 98, test2: 100, test3: 102, modified: new Date("01/05/2020") }, { _id: 3, test1: 95, test2: 110, modified: new Date("01/04/2020") } ] )
To verify, query the collection:
db.students.find()
The following db.collection.updateOne()
operation uses an
aggregation pipeline to update the document with _id: 3
:
db.students.updateOne( { _id: 3 }, [ { $set: { "test3": 98, modified: "$$NOW"} } ] )
Specifically, the pipeline consists of a $set
stage
which adds the test3
field (and sets its value to 98
) to the
document and sets the modified
field to the current datetime.
The operation uses the aggregation variable NOW
for the
current datetime. To access the variable, prefix with $$
and enclose
in quotes.
To verify the update, you can query the collection:
db.students.find().pretty()
Example 2
Tip
You can try out the example in the provided shell. Click inside the shell to connect. Once connected, you can run the examples in the shell.
Create an example students2
collection (if the collection does not
currently exist, insert operations will create the collection):
db.students2.insertMany( [ { "_id" : 1, quiz1: 8, test2: 100, quiz2: 9, modified: new Date("01/05/2020") }, { "_id" : 2, quiz2: 5, test1: 80, test2: 89, modified: new Date("01/05/2020") }, ] )
To verify, query the collection:
db.students2.find()
The following
db.collection.updateMany()
operation uses an aggregation
pipeline to standardize the fields for the documents (i.e. documents
in the collection should have the same fields) and update the
modified
field:
db.students2.updateMany( {}, [ { $replaceRoot: { newRoot: { $mergeObjects: [ { quiz1: 0, quiz2: 0, test1: 0, test2: 0 }, "$$ROOT" ] } } }, { $set: { modified: "$$NOW"} } ] )
Specifically, the pipeline consists of:
a
$replaceRoot
stage with a$mergeObjects
expression to set default values for thequiz1
,quiz2
,test1
andtest2
fields. The aggregation variableROOT
refers to the current document being modified. To access the variable, prefix with$$
and enclose in quotes. The current document fields will override the default values.a
$set
stage to update themodified
field to the current datetime. The operation uses the aggregation variableNOW
for the current datetime. To access the variable, prefix with$$
and enclose in quotes.
To verify the update, you can query the collection:
db.students2.find()
Example 3
Tip
You can try out the example in the provided shell. Click inside the shell to connect. Once connected, you can run the examples in the shell.
Create an example students3
collection (if the collection does not
currently exist, insert operations will create the collection):
db.students3.insertMany( [ { "_id" : 1, "tests" : [ 95, 92, 90 ], "modified" : ISODate("2019-01-01T00:00:00Z") }, { "_id" : 2, "tests" : [ 94, 88, 90 ], "modified" : ISODate("2019-01-01T00:00:00Z") }, { "_id" : 3, "tests" : [ 70, 75, 82 ], "modified" : ISODate("2019-01-01T00:00:00Z") } ] );
To verify, query the collection:
db.students3.find()
The following db.collection.updateMany()
operation uses an
aggregation pipeline to update the documents with the calculated
grade average and letter grade.
db.students3.updateMany( { }, [ { $set: { average : { $trunc: [ { $avg: "$tests" }, 0 ] }, modified: "$$NOW" } }, { $set: { grade: { $switch: { branches: [ { case: { $gte: [ "$average", 90 ] }, then: "A" }, { case: { $gte: [ "$average", 80 ] }, then: "B" }, { case: { $gte: [ "$average", 70 ] }, then: "C" }, { case: { $gte: [ "$average", 60 ] }, then: "D" } ], default: "F" } } } } ] )
Specifically, the pipeline consists of:
a
$set
stage to calculate the truncated average value of thetests
array elements and to update themodified
field to the current datetime. To calculate the truncated average, the stage uses the$avg
and$trunc
expressions. The operation uses the aggregation variableNOW
for the current datetime. To access the variable, prefix with$$
and enclose in quotes.a
$set
stage to add thegrade
field based on theaverage
using the$switch
expression.
To verify the update, you can query the collection:
db.students3.find()
Example 4
Tip
You can try out the example in the provided shell. Click inside the shell to connect. Once connected, you can run the examples in the shell.
Create an example students4
collection (if the collection does
not currently exist, insert operations will create the collection):
db.students4.insertMany( [ { "_id" : 1, "quizzes" : [ 4, 6, 7 ] }, { "_id" : 2, "quizzes" : [ 5 ] }, { "_id" : 3, "quizzes" : [ 10, 10, 10 ] } ] )
To verify, query the collection:
db.students4.find()
The following db.collection.updateOne()
operation uses an
aggregation pipeline to add quiz scores to the document with _id:
2
:
db.students4.updateOne( { _id: 2 }, [ { $set: { quizzes: { $concatArrays: [ "$quizzes", [ 8, 6 ] ] } } } ] )
To verify the update, query the collection:
db.students4.find()
Example 5
Tip
You can try out the example in the provided shell. Click inside the shell to connect. Once connected, you can run the examples in the shell.
Create an example temperatures
collection that contains
temperatures in Celsius (if the collection does not currently exist,
insert operations will create the collection):
db.temperatures.insertMany( [ { "_id" : 1, "date" : ISODate("2019-06-23"), "tempsC" : [ 4, 12, 17 ] }, { "_id" : 2, "date" : ISODate("2019-07-07"), "tempsC" : [ 14, 24, 11 ] }, { "_id" : 3, "date" : ISODate("2019-10-30"), "tempsC" : [ 18, 6, 8 ] } ] )
To verify, query the collection:
db.temperatures.find()
The following db.collection.updateMany()
operation uses an
aggregation pipeline to update the documents with the corresponding
temperatures in Fahrenheit:
db.temperatures.updateMany( { }, [ { $addFields: { "tempsF": { $map: { input: "$tempsC", as: "celsius", in: { $add: [ { $multiply: ["$$celsius", 9/5 ] }, 32 ] } } } } } ] )
Specifically, the pipeline consists of an $addFields
stage to add a new array field tempsF
that contains the
temperatures in Fahrenheit. To convert each celsius temperature in
the tempsC
array to Fahrenheit, the stage uses the
$map
expression with $add
and
$multiply
expressions.
To verify the update, you can query the collection:
db.temperatures.find()
Additional Examples
See also the various update method pages for additional examples: