Updateone to $inc a number returns "modifiedCount": 1 but the value never changes in document

Hi everyone,
I been working trying to solve this for two days but haven’t find why the document is not updated.

My query is:

payment.updateOne({ “contratct”: ${value1} }, { $inc: { “Balance”: value2 } }, {upsert: true})

The query returns:

{ “acknowledged”: true,
“modifiedCount”: 1,
“upsertedId”: null,
“upsertedCount”: 0,
“matchedCount”: 1 }

But when checking the document the fields have the same old value. if the updateone changes a STRING type field, the modification works but with numbers it isn`t working.

The field Balance is type: Double

Same thing happens if I want to $push a new element to an empty array inside the document. The query returns modifiedCount: 1 but there’s no change in the collection document.

Please if somebody can tell me what is happening, because in other collections I’m updating strings without any problem, but numbers doesn’t work.

If modifiedCount is 1, then one document has definitively been modified.

I see to possibilities where a modification is not visible.

  1. In the case above if value is 0, then the modification will not be visible because the value will not change.
  2. You are not looking at the modified document when you are verifying the change. It could be wrong server, wrong database or wrong collection. Or all those 3 are corrects but because you have a typo in your field name you are updating the wrong document. Since you have upsert:true the wrong document was created and now it is being updated. Check contratct, that looks like your typo. See

I checked the query for typos and still doesn’t work, if I change $inc for $set then it works but increment doesn’t.

This is what I’m sending:
{ ‘$inc’: { Balance: -1118 } }

The field Balance keep it’s value of: 4872 if I use $inc. With $set it changes the value to -1118.

Right now the collection only has one document so the upsert: true doesn’t adds a new document and still I get the modifiedCount: 1

You must be doing something else somewhere because that should work.

You might need to share the code before and after your call to updateOne().

Any reason why value1 is between ${} and value2 is not.

Post a screenshot of running the same code in the shell with calls to payment.find() before and after the call to updateOne().

The only cases I can think of where $inc will fail but $set will work is if value2 or Balance are not numbers. But that should generate errors:

MongoServerError: Cannot increment with non-numeric argument: {Balance: "10"}
or
MongoServerError: Cannot apply $inc to a value of non-numeric type. {_id: 1} has the field 'Balance' of non-numeric type string

Hi @Alejandro_Chavero,

As @steevej suggests this should either work or return an error.

In addition to the further info he has asked for, please provide some more details on your environment:

  • version of MongoDB server
  • version of MongoDB driver

Thanks,
Stennie

Thanks for the help @steevej and Stennie, I was with the mongo support chat for this same problem, because they told me to do the same stuff.

This is the mongoDb information:
MongoDB 4.4.11 Enterprise
The driver is the nodejs driver version: 4.1.1

I changed my code and use aggregation and that work fine. But the normal updateOne didn’t. I ran the code from the shell and had the same results with updateOne command. The modified flag says the change was made but when I refresh in Compass the information isn’t change.

In my react app the code is:

import clientPromise from "../../../lib/mongodb";

const client = await clientPromise

const database = client.db('db_name');
await database.collection('payments')
   .updateOne({ “contract”: 'C-7806' }, { $inc: { “Balance”: -200 } }, {upsert: true})
   .then(result => {
       res.json(result);
   })

In the mongodb shell:
db.payments.updateOne({ “contract”: 'C-7806' }, { $inc: { “Balance”: -200 } }, {upsert: true})

The query returns:

{ “acknowledged”: true,
“modifiedCount”: 1,
“upsertedId”: null,
“upsertedCount”: 0,
“matchedCount”: 1 }

To make sure I wasn’t adding extra documents, I had only one document in the collection and same result. No new document and the one existing had modifiedcount: 1 but still no change in the information.

Now with aggregation it is working, but I’m seeing that sometimes the $inc works and other times it doesn’t and I don’t understand why.

This is my aggregation code:

{
   '$set': {
       'Balance': {
              '$subtract': [
                   '$Balance', amount
               ]
        }
    }
}

I run in Compass and again sometimes work and sometimes not… and also if I edit the information in Compass, then original value of Balance is set instead of keeping the value set by the aggregation query??

Could be a bug?

There is a bug or mishandling somewhere. But certainly not in the implementation of $inc. Do you refresh the document when you look in Compass. Compass does not monitor the changes and update what you see on the screen automatically. You have to refresh. That is why I want to

Something like:

Atlas rent-shard-0 [primary] test> c.find()
[ { _id: 1, contract: 'C-7806', Balance: 3333 } ]
Atlas rent-shard-0 [primary] test> c.updateOne({ 'contract' : 'C-7806' }, { '$inc' : { 'Balance': -200 } }
... , { upsert : true } )
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
Atlas rent-shard-0 [primary] test> c.find()
[ { _id: 1, contract: 'C-7806', Balance: 3133 } ]

Here’s the screenshots, thanks for the help steevej:


It is 2 different screenshots. There is no way to make sure of the order they were taken. I want the first find, the update one and the second find in the same screenshot.

If you only have 1 document you do not need to specify a query just db.payments.find() is sufficient.

It is a little bit frustrating that your field name changes, it started with contratct which was apparently a typo, then it was contract and now it is NumContrato. What else do you redact between the real data and real codet? That might stops us from finding the real issue when working on fake data.

Hi @Alejandro_Chavero,

A few further items to confirm:

  • What type of deployment is this (standalone, replica set, or sharded cluster)?

  • Do you have a unique index on the contract (or NumContrato) field?

  • What is the output of db.payments.find({"contract": 'C-7806'}).count()?

If your query criteria does not uniquely identify a document, your updateOne() may be succeeding on a different document than the one you are subsequently retrieving.

I haven’t been able to reproduce this issue, but perhaps there is a difference in some details you have changed or redacted.

Regards,
Stennie

I created the unique index in the collection and run again the querys, same result the information doesn’t change. Here’s the screenshots and I’m using a sharded cluster.


thanks for the help. Sorry for changing the name of the fields, it was because I have them in spanish so I put them in english so you can help me easier.

How are you connected? Via mongos or directly to one of the shard replica set? Do you have connection options about secondary read perferences? Is that particular collection shared or not?

Could you repeat the same find(), updateOne() and find() sequence but also do a $set on a dummy field at the same time as the $inc.

Do you have a validation schema for that collection?

Do you have a change stream handler, or a trigger, for that collection?

Hi @Alejandro_Chavero,

Thanks for confirming the deployment type. A few more questions:

  • Is this collection sharded? If so, what is the shard key?

  • Can you share the output of the find(...) query with explain() appended?

Thanks,
Stennie

Thanks Stennie, sorry but I got wrong the sharded concept. What I’m using is the sandbox cluster free tier. I thought the concept was “shared” because of the sandbox. My mistake for reading badly.

@Alejandro_Chavero, still awaiting for

You wrote that updating string worked, so it would be interesting to see both operation in the same update.

Thanks Steevej
How are you connected?
I been running the querys in my react application using the nodejs driver. But the same problem happens when I run the query using Compass

Do you have connection options about secondary read perferences?
No, I’m new with mongodb so I used the free tier and the basic configuration

Could you repeat the same find(), updateOne() and find() sequence but also do a $set on a dummy field at the same time as the $inc.

Didn’t work, I $set the field Empresa and didn’t change also the $inc didn’t work

Do you have a change stream handler, or a trigger, for that collection?
I have a database trigger but for a different collection that inserts/updates a subset of values in this one

Thanks for your help

The $inc worked as the Balance went from 4872 to 4672.

As for Empresa, your syntax is wrong. The $set and $inc have to be in the same document. Rather than

... , { '$inc' : { 'Balance' :  -200 } }, { '$set' : { 'Empresa' : 'CENTRO' } } , ...

it should be

... , { '$inc' : { 'Balance' :  -200 } , '$set' : { 'Empresa' : 'CENTRO' } } , ...

Please disable

or publish it to make sure it is not it that overwrite your update.

@Alejandro_Chavero, the discussion has abruptly terminated, so I guess you find the issue in your code.

Please share what was the issue so that others facing the same could start investigating their issue using the same solution.

This will help keep this forum useful and efficient.