diegoaguilar
diegoaguilar

Reputation: 8376

How to properly update a document with upsert behaviour? (Mongo bug SERVER-10711)

I'm trying to update a document having the update if found, insert otherwise.

This is something of what I'm trying (using sails waterline ORM which uses monodb node driver):

var roundPoints = 93;
var lfPoints = 10 + roundPoints;
var lineUpPointsGeneralRecord = {
  round: 0,
  teamId: "real-madrid-9248",
  totalPoints: roundPoints,
  teamName: "minuto93",
  userId: "bbc1902",
  userName: "Risas Pizza",
  signupPoints: 10,
  lfPoints: lfPoints
};

LineupPointsRecord.native(function (err,collection) {
collection.update(
  {teamId: lineUpPointsGeneralRecord.teamId, round: 0},
  {
    $setOnInsert: lineUpPointsGeneralRecord,
    $inc: {lfPoints: roundPoints},
    $push: {roundPoints: roundPoints}
  },
  {upsert: true},
  function (err,updateResult) {
    sails.log.debug(err,updateResult);
  });
});

But that fails complaining:

code: 16836,
err: 'Cannot update \'lfPoints\' and \'lfPoints\' at the same time' } null

What am I doing wrong?

Edit

This seems to be a known issue. But I don't really want to endup implementing a workaround. How can I cope it?

Upvotes: 3

Views: 202

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50406

The error occurs because when an "upsert" occurs both of the $setOnInsert and the $inc as well as the $push operations are all attempting to set items in the document. As the error reports you cannot modify the same property of a document with two different operators in a single update.

The solution then is to "seperate" the updates, so that only one operation is "only" doing the $setOnInsert and the other will perform the other changes where the document was matched.The best way to do that is to use Bulk Operations so that all requests are sent to the server at once:

LineupPointsRecord.native(function (err,collection) {

    var bulk = collection.initializeOrderedBulOp();

    // Match and update only. Do not attempt upsert
    bulk.find({
        "teamId": lineUpPointsGeneralRecord.teamId,
        "round": 0
    }).updateOne({
        "$inc": { "lfPoints": roundPoints },
        "$push": { "roundPoints": roundPoints }
    });

    // Attempt upsert with $setOnInsert only
    bulk.find({
        "teamId": lineUpPointsGeneralRecord.teamId,
        "round": 0
    }).upsert().updateOne({
        "$setOnInsert": lineUpPointsGeneralRecord
    });

    bulk.execute(function (err,updateResult) {
        sails.log.debug(err,updateResult);
    });
});

Since the second operation there will only attempt the upsert where the document was not matched, then there is no conflict as there are no other operations. In the first operation, this will "only" make changes where the document "did match", and since there is no upsert attempted here there is also no conflict.

Make sure your sails-mongo is a latest version supporting the Bulk operations properly be the inclusion of a recent node native driver. The most recent supports the v2 driver, which is fine for this.

Upvotes: 2

Related Questions