Navigation

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:

$addFields $set
$project $unset
$replaceRoot $replaceWith

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

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. For the current datetime, the operation uses the aggregation variable NOW for the (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

You can try out the examples 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 an $mergeObjects expression to set default values for the quiz1, quiz2, test1 and test2 fields. The aggregation variable ROOT 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 the modified field to the current datetime. For the current datetime, the operation uses the aggregation variable NOW for the (to access the variable, prefix with $$ and enclose in quotes).

To verify the update, you can query the collection:

db.students2.find()

Example 3

You can try out the examples 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.insert([
   { "_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 the tests array elements and to update the modified field to the current datetime. To calculate the truncated average, the stage uses the $avg and $trunc expressions. For the current datetime, the operation uses the aggregation variable NOW for the (to access the variable, prefix with $$ and enclose in quotes)
  • a $set stage to add the grade field based on the average using the $switch expression.

To verify the update, you can query the collection:

db.students3.find()

Example 4

You can try out the examples 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

You can try out the examples 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()