Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aggregation with update in mongoDB

Tags:

I've a collection with many similar structured document, two of the document looks like

Input:

{      "_id": ObjectId("525c22348771ebd7b179add8"),      "cust_id": "A1234",      "score": 500,      "status": "A"     "clear": "No" }  {      "_id": ObjectId("525c22348771ebd7b179add9"),      "cust_id": "A1234",      "score": 1600,      "status": "B"     "clear": "No" } 

By default the clear for all document is "No",

Req: I have to add the score of all documents with same cust_id, provided they belong to status "A" and status "B". If the score exceeds 2000 then I have to update the clear attribute to "Yes" for all of the document with the same cust_id.

Expected output:

{      "_id": ObjectId("525c22348771ebd7b179add8"),      "cust_id": "A1234",      "score": 500,      "status": "A"     "clear": "Yes" }  {     "_id": ObjectId("525c22348771ebd7b179add9"),      "cust_id": "A1234",      "score": 1600,      "status": "B"     "clear": "Yes" } 

Yes because 1600+500 = 2100, and 2100 > 2000.


My Approach: I was only able to get the sum by aggregate function but failed at updating

db.aggregation.aggregate([     {$match: {         $or: [             {status: 'A'},             {status: 'B'}         ]     }},     {$group: {         _id: '$cust_id',         total: {$sum: '$score'}     }},     {$match: {         total: {$gt: 2000}     }} ]) 

Please suggest me how do I proceed.

like image 683
Sam Avatar asked Oct 15 '13 15:10

Sam


People also ask

Can we use $set in aggregation?

You can include one or more $set stages in an aggregation operation. To add field or fields to embedded documents (including documents in arrays) use the dot notation.

How do I update multiple values in MongoDB?

Update Multiple Fields of a Single Document. We can use $set and $inc operators to update any field in MongoDB. The $set operator will set the newly specified value while the $inc operator will increase the value by a specified value.

Is MongoDB good for aggregation?

As with many other database systems, MongoDB allows you to perform a variety of aggregation operations. These allow you to process data records in a variety of ways, such as grouping data, sorting data into a specific order, or restructuring returned documents, as well as filtering data as one might with a query.


2 Answers

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.

like image 92
Sam Avatar answered Oct 11 '22 23:10

Sam


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   }}}} ]); 
like image 20
Mitar Avatar answered Oct 12 '22 00:10

Mitar