[SERVER-2643] Allow Field Name Duplication with Modifiers Created: 28/Feb/11  Updated: 06/Dec/22  Resolved: 29/Jun/19

Status: Closed
Project: Core Server
Component/s: Write Ops
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Major - P3
Reporter: Tom Wardrop Assignee: Backlog - Query Team (Inactive)
Resolution: Won't Do Votes: 24
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to SERVER-10711 $setOnInsert mods should not conflict... Closed
related to SERVER-13578 add $setOnUpdate to update operation Closed
related to SERVER-28789 Add a 'initial value' parameter to $inc Closed
related to SERVER-6566 Support conditional updates: $updates Closed
related to SERVER-1050 not allowed to $push and $pop to same... Closed
Assigned Teams:
Query
Participants:

 Description   

Currently, if a field is referenced twice within two different modifiers, Mongo errors out, even if their is actually conflict. In other words, the error check is dumb. It's understandable why this was done, but I think this behavior deserves further consideration as Mongo matures. Take the following example:

{$set :

{age: 20}

, $inc : {age: 2}}

Running this as part of an update will generate a "Field name duplication not allowed with modifiers" error. Any human reading this however can easily determine the intention, and that such an operation could have succeeded without error. The result of which should have been setting the field "age" to the value 22.

A relatively easy first step solution here, would be to allow name duplication between the $set modifiers and the other modifiers. The rule should be that the $set modifier operation should always happen first, followed by the rest of the modifiers, in order. In other words, any other modifier operations should be applied to the result of $set.

Of course I'm unaware of how easy or difficult this would be to implement in Mongo, given it's atomic nature and all, but it's worth considering whether implementing this is possible or not.



 Comments   
Comment by Asya Kamsky [ 29/Jun/19 ]

SERVER-40381 implemented support for aggregation expressions to specify update for 4.2. 

You can see some examples here.

The aggregation can have multiple stages which are well ordered and can reference the document as it is at the beginning of that stage.

You can also test for a condition and do a different update/transformation depending on result.

Comment by Asya Kamsky [ 15/Jan/15 ]

Linking to related tickets.

Comment by Roni [ 04/Apr/14 ]

Exactly the same issue for me, trying to either Insert a new value or to increase the same value, got the same duplication error.
I'm using the C# driver.

Please fix it guys thanks

Comment by Jaap Taal [ 02/Apr/14 ]

Another motivation is a system where I want to keep track of a user's credit per (let's say) level of a game. Each level I get 20 points and I can score or spend points.
I'd prefer not to insert the 20 in a seperate call but inject it with $setOnInsert. This way I can avoid having to make sure that the insert is always done before the up/down.

Something along the lines of:

db.foo.findAndModify({
  query: {_id: 1},
  update: { "$setOnInsert": {"bar": "value", "baz": 10}, $inc: {baz: 5}},
  upsert:true,
  new:true
})

Especially cool would be to inject some post validation before the modify happens. In my example it would be very cool to be able to restrict that baz doesn't get below 0.

Comment by Aaron Blankenship [ 23/Oct/13 ]

+1 for this. I had a similar issue using update() for an upsert operation using:

//obj DOES contain updatedAt.
{
    $set:{updatedAt: obj.updatedAt},
    $setOnInsert:obj
}

to either update an existing docuement's updatedAt field, or insert a new document which also contained the updatedAt value. I was able to work around this by not creating that field until an update occurs for the first time but this may not always be acceptable.

//obj DOES NOT contain updatedAt.
{
    $set:{updatedAt: moment().format(dateFormat)},
    $setOnInsert:obj
}

Comment by Thorsten Möller [ 08/Oct/13 ]

Just to encourage you to add this feature, there is a very practical use case where this is needed: implementing a number sequence that has a start value (which is actually how I came across this issue). The following query does not work, currently:

db.SEQUENCE.findAndModify({
query: { _id:"mySeq"},
update:{
   $inc: { value: new NumberLong(1)},
   $setOnInsert: {value: new NumberLong(100)}}
new:true, upsert:true})

What the query should do, if there is no record for "mySeq" yet, is to insert the start value (100) and then increment it; or vice versa as the order is actually irrelevant in this case.

(An alternative to implement this use case would be something like an $incOnUpdate (that does increment the field only if its an update) or a $setOnUpdate that allows using the $inc operator in its body.)

Comment by Tom Wardrop [ 01/Mar/11 ]

As I see it, you have two options. Either create a rule like I suggested, where $set gets applied first no matter what the order of the update is. Or alternatively, respect modifier order and allow duplicate field names, as long as they don't conflict. For example, the following should set age to 22...

{$set :

{age: 20}

, $inc : {age: 2}}

Where as the following example should by default, result in a conflict error, given that the increment will always be overwritten by the $set that follows it.

{$inc :

{age: 2}

, $set : {age: 20}}

However, if this approach were to be taken, I'd like to see an option introduced, to continue on conflict, so {$inc :

{age: 2}

, $set : {age: 20}} would succeed, and simply set "age" to 20. So when the "continue on conflict" option is enabled, the rule becomes, the last operation wins. The only thing that can cause an error when "continue on conflict" is enabled, is if you try and do something illegal, such as using $push on a non-array, or trying to increment a string, so all of the ordinary rules would still apply.

The logic is simple and predictable, and I can't think of any circumstance which would result in unexpected behavior.

Comment by Eliot Horowitz (Inactive) [ 01/Mar/11 ]

There is no natural ordering to this, so its somewhat undefined.
I'm not so sure all humans would agree what the output of that should be given most things people do aren't ordered.

Generated at Thu Feb 08 03:00:46 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.