Aggregation With Update In MongoDB


Answer :

After a lot of trouble, experimenting mongo shell I've finally got a solution to my question.

Psudocode:

# To get the list of customer whose score is greater than 2000 cust_to_clear=db.col.aggregate(     {$match:{$or:[{status:'A'},{status:'B'}]}},     {$group:{_id:'$cust_id',total:{$sum:'$score'}}},     {$match:{total:{$gt:500}}})  # To loop through the result fetched from above code and update the clear cust_to_clear.result.forEach (    function(x)    {       db.col.update({cust_id:x._id},{$set:{clear:'Yes'}},{multi:true});     } ) 

Please comment, if you have any different solution for the same question.


With Mongo 4.2 it is now possible to do this using update with aggregation pipeline. The example 2 has example how you do conditional updates:

db.runCommand(    {       update: "students",       updates: [          {            q: { },            u: [                  { $set: { average : { $avg: "$tests" } } },                  { $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"                  } } } }            ],            multi: true          }       ],       ordered: false,       writeConcern: { w: "majority", wtimeout: 5000 }    } ) 

Another example:

db.c.update({}, [   {$set:{a:{$cond:{     if: {},    // some condition       then:{} ,   // val1       else: {}    // val2 or "$$REMOVE" to not set the field or "$a" to leave existing value   }}}} ]); 

You need to do this in two steps:

  1. Identify customers (cust_id) with a total score greater than 200
  2. For each of these customers, set clear to Yes

You already have a good solution for the first part. The second part should be implemented as a separate update() calls to the database.

Psudocode:

# Get list of customers using the aggregation framework cust_to_clear = db.col.aggregate(     {$match:{$or:[{status:'A'},{status:'B'}]}},     {$group:{_id:'$cust_id', total:{$sum:'$score'}}},     {$match:{total:{$gt:2000}}}     )  # Loop over customers and update "clear" to "yes" for customer in cust_to_clear:     id = customer[_id]     db.col.update(         {"_id": id},         {"$set": {"clear": "Yes"}}     ) 

This isn't ideal because you have to make a database call for every customer. If you need to do this kind of operation often, you might revise your schema to include the total score in each document. (This would have to be maintained by your application.) In this case, you could do the update with a single command:

db.col.update(     {"total_score": {"$gt": 2000}},     {"$set": {"clear": "Yes"}},     {"multi": true}     ) 

Comments

Popular posts from this blog

Are Regular VACUUM ANALYZE Still Recommended Under 9.1?

Can Feynman Diagrams Be Used To Represent Any Perturbation Theory?