Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB, conditional upserts or updates

When using MongoDB I'm currently doing a conditional upsert as a part of an aggregation process, on the form (simplified alot):

db.dbname.update({attr1 : value1, attr2 : value2},
                 {"$inc" : { avg : current_value, nr : 1}},
                 false (multi), true (upsert))

But I want to able to keep a maximum (and minimum) value as well, without having to retrieve the document. Something along the lines of:

db.dbname.update({ attr1 : value1, attr2 : value2},
                 {"$inc" : { avg : current_value, nr : 1},
                  "$setIfBigger" : { max : current_value}},
                 false (multi), true (upsert))

Is this possible in an efficient way?

My current, extremely inefficient, solution is that I check for a current aggregation document, and if it exists I update the values accordingly, and if it doesn't I create a new document. Example (again, simplified alot, but the essence is there):

var obj = db.dbname.findOne({attr1 : value1, attr2 : value2},{_id:1});
if (obj != null) {
   db.dbname.update({attr1 : value1, attr2 : value2},
                    {"$inc" : { avg : current_value, nr : 1},
                     "$set" : { max : (obj.max > current_value ? obj.max : current_value}},
                    false (multi), true (upsert));
} else {
   db.dbname.save({attr1 : value1, attr2 : value2, 
                   avg : current_value, nr : 1,
                   max : current_value});
}

The actual program is written in Java and uses the mongo-API, and the aggregation process is quite complex and uses composition techniques way beyond Javascript to communicate with other servers, ergo mapreduce is not an option. Finally the end result is quite a humongous set of simple values which I want to store in the most effecient way and also store precalculated averages, maximums and minimums of certain combinations.

One solution is creating unique function-objects in JS for every single update, which I believe is not an efficient way?

The main objective is to decrease the time taken to perform an aggregation of this type, bandwidth usage is secondary.

like image 214
flindeberg Avatar asked Apr 04 '11 14:04

flindeberg


1 Answers

This can now be done much more easily in the MongoDB 2.6 release which includes Insert and Update Improvements. Specifically there are new $min and $max operators that perform a conditional update depending on the relative size of the specified value and the current value of a field.

So for example this update:

db.scores.update( { _id: 1 }, { $max: { highScore: 950 } } )

would conditionally update the specified document if 950 is greater than the current value of highScore.

like image 57
helmy Avatar answered Sep 18 '22 16:09

helmy