Search code examples
node.jsmongodbmongoose

MongoDB: Updating the path 'Y' would create a conflict at 'Y'


I have the following query being executed:

const myData = await this.findOneAndUpdate({
  myId,
  color,
}, {
  $setOnInsert: {
    myId,
    color,
    date: now.format('YYYY-MM-DD'),
    counter: myObj.counter - amount,
  },
  $inc: {
    counter: -amount
  },
}, {
  new: true,
  upsert: true,
});

I get the error:

"Updating the path 'count' would create a conflict at 'count'"

First I thought the error was happening because of the version of mongoose, but I don't think that is the case.
Now I understand this is happening because I have color in both $setOnInsert and $inc, but I don't understand why.

Also: This code works on MongoDB 3.4.24 Community but does NOT WORK on MongoDB 5.0.11 Community

So my questions are:

  • why this error is happening exactly? Could this be a BUG?
  • Why this works in older version of MongoDB?
  • What would be the best approach to refactor this?

Solution

  • You are getting the above error from MongoDB, because of the way $inc works, with upsert: true, new: true, $inc will insert a new document. Check this playground.

    In your case, you have $setOnInsert and $inc, in case your matching document is not found, both the operators will try to set the value of key counter, which will create a conflict, hence you see the error. To fix it, you can use the pipeline form of updates, something like this:

    const myData = await this.findOneAndUpdate({
      myId,
      color,
    }, [
       {
         $set: {
            myId,
            color,
            date: {
               $cond: {
                 if: {$eq: ["$$ROOT", null]},
                 then: now.format('YYYY-MM-DD'),
                 else: "$$ROOT.date"
               }
            },
            counter: {
               $cond: {
                 if: {$eq: ["$$ROOT", null]},
                 then: myObj.counter - amount,
                 else: {$substract: ["$$ROOT.counter", amount]}
               }
            }
         }
       }
    ], {
      new: true,
      upsert: true,
    });