[SERVER-13478] Resolve ambiguity of positional array updates when the path does not exist Created: 03/Apr/14  Updated: 06/Dec/22

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

Type: Improvement Priority: Major - P3
Reporter: Gerry F Assignee: Backlog - Query Optimization
Resolution: Unresolved Votes: 2
Labels: mql-semantics
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by SERVER-46695 Allow arrays creation during new reco... Closed
Related
related to SERVER-4753 array indexes and object subfields sh... Backlog
is related to SERVER-39446 $push/$pushAll to honor $position pas... Closed
Assigned Teams:
Query Optimization
Participants:

 Description   

The upsert behaviour in update does not handle arrays the same way for insert and update.

Upsert is only useful in the case that you don't know if the object already exists.

I need a way to call upsert with arrays that creates the same document shape for both insert and update. This will allow efficient manipulation of array contents.

The example below is a simple demonstration of the problem, using the mongo shell.

Simplest version, mongo shell code. The first upsert adds to the 'arr' array, the second creates an 'arr' document, the third creates an 'arr' array.

 
db.onearray.insert({_id:"oa1",arr: [1]}) 
db.onearray.update({_id:"oa1"},{$set: {"arr.1":2}},true) 
db.onearray.update({_id:"oa2"},{$set: {"arr.1":3}},true) 
db.onearray.update({_id:"oa3"},{$set: {"arr":[4]}},true) 
db.onearray.find() 
{ "_id" : "oa1", "arr" : [ 1, 2 ] } 
{ "_id" : "oa2", "arr" : { "1" : 3 } } 
{ "_id" : "oa3", "arr" : [ 4 ] } 

Slightly more complex version.
The first upsert modifies the 'bottom' array. The second upsert creates a 'bottom' document with key "0". The third upsert creates a 'bottom' array.

 
db.twoarrays.insert({_id:"ta1",rnumber: 0,top:[{tnumber:1,bottom:[{a:1,b:2}]}]}) 
db.twoarrays.update({_id:"ta1"},{$set:{"top.0.bottom.0": {a:2,b:2}}},true) 
db.twoarrays.update({_id:"ta1"},{$set:{"top.1.bottom.0": {a:3,b:3}}},true) 
db.twoarrays.update({_id:"ta1"},{$set:{"top.2.bottom": [{a:4,b:4}]}},true) 
db.twoarrays.find() 



 Comments   
Comment by Andrei Stepanov [ 09/Jul/21 ]

@asya, could you please give an example hot to `getting around this ambiguity` for the example that stays in this question:

db.onearray.update({_id:"oa2"},{$set: {"arr.1":3}},true) 

That it creates an array. Thank you.

Comment by Asya Kamsky [ 11/Sep/19 ]

I believe in 4.2 the ability to express update portion using aggregation syntax allows getting around this ambiguity.  You can see examples in this comment and in the docs

 

Comment by David Storch [ 20/Feb/18 ]

asya, yep, done.

Comment by Asya Kamsky [ 15/Feb/18 ]

david.storch this should probably be linked to language semantics or array syntax epic?

Comment by David Storch [ 03/Feb/16 ]

Hi GerryF,

Thanks for filing this ticket. This looks like an unfortunate consequence of the semantics of a $set with a numerical path component. Consider the following example:

db.c.drop();
db.c.insert({_id: 1});
db.c.update({_id: 1}, {$set: {"a.1": "foo"}});

The resulting document is { "_id" : 1, "a" : { "1" : "foo" } }. The update system does not consider numerical path components to have special array semantics unless there is an existing array along the update path. Note that the application of the update modifiers when {upsert: true} results in an insert is identical to the application of modifiers when a matching document is found (both in terms of semantics and implementation).

I don't think we can safely change the semantics of the update language, so to resolve this problem we may have to create a syntax that is specific to position-based array updates. For instance, we could introduce syntax like {$set: {"a.$3": "foo"}} which unambiguously means

  1. update the fourth array element to "foo", if array a exists, or
  2. create an array a: [null, null, null, "foo"] if a does not exist.

I am keeping this ticket open in order to represent a feature request of this nature.

Best,
Dave

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