[SERVER-2476] New $update atomic modifier for changing array elements Created: 03/Feb/11 Updated: 06/Dec/22 Resolved: 11/Aug/17 |
|
| Status: | Closed |
| Project: | Core Server |
| Component/s: | Write Ops |
| Affects Version/s: | None |
| Fix Version/s: | None |
| Type: | New Feature | Priority: | Major - P3 |
| Reporter: | Keith Branton | Assignee: | Backlog - Query Team (Inactive) |
| Resolution: | Duplicate | Votes: | 10 |
| Labels: | None | ||
| Remaining Estimate: | Not Specified | ||
| Time Spent: | Not Specified | ||
| Original Estimate: | Not Specified | ||
| Environment: |
All |
||
| Issue Links: |
|
||||||||||||||||
| Assigned Teams: |
Query
|
||||||||||||||||
| Backwards Compatibility: | Fully Compatible | ||||||||||||||||
| Participants: | |||||||||||||||||
| Description |
|
The proposal: new $update atomic modifier to identify objects in an array using a query criteria and update them. I've tried to keep the syntax of this as close to update() as possible. $update: {field:[query, update, <upsert> ,<multi>]}In the update the following special tokens can be used: This may or may not duplicate some of the virtual collections ticket: http://jira.mongodb.org/browse/SERVER-142 but that ticket doesn't really contain any more than an idea that clearly a lot of people like. I'm hoping that this ticket can start a discussion that can end up in a useful specification. Some examples to follow.... |
| Comments |
| Comment by Asya Kamsky [ 11/Aug/17 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The feature requested in this ticket (ability to independently manipulate multiple elements of embedded array(s)) is now available as part of Closing as a duplicate. The last comment talks about upsert and probably belongs with | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Glenn Maynard [ 09/Aug/13 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
The above proposals are too different from the upsert mechanism Mongo already has. A version of the original proposal makes a lot more sense. It makes array upserting exactly equivalent to document upserting. You can think of them as the same thing: a collection is an array of documents, and this is doing the same thing for arrays of subdocuments. This way, you don't have to define two separate mechanisms, and it's easy to reuse code for performing updates, whether you're updating a document or a subdocument. Here's an adjusted proposal that I think fits the query language better.
All operators that are valid for an update would be valid inside the $upsert's "update" field. There are no "$" or "$$" operators; the update is scoped implicitly. This example would increment the existing 2013-01-02 item, and create a new 2013-01-04 item. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Alex Young [ 01/Apr/13 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I strongly agree with Keith Branton and really like the way he suggested. Like if a user has a book list which has 10 books to read, and now we want to change the priority, I have to do 10 times update with the position operator, it's awful especially when the list is in a multilevel subdocument. data:
It will be greate if we can locate each operation like $set, $push, $pull,
Also, it's easy to modify array in deeplevel subdocument.
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Kurt Agius [ 11/Feb/13 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I've had this use case to update the last item in an array which was previously pushed. I had a look but couldn't found anything useful. Can this thread be started again or it's just too old ? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Keith Branton [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Thinking a bit more about my last message, while it's terse, putting queries into field name strings would cause loss of bson type-safety. Maybe if we used an array for a field name instead? Instead of "arr. {x:1}.y" use ["arr", {x:1},"y"] The 831 example would become .update( { "_id":1}, {$set:{["medications",{_id:23},"prescriptions",{_id:77},"quantity"]:30}) instead of the proposed .update( { "_id":1, "medications._id":23, "medications.prescriptions._id":77 }, This has got to be easier to read and way more flexible. Would it be much harder to do this as the solution to 831? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Keith Branton [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
And with your example .update( {"arr.x":1},{$set:{"arr. {x:1}.y":3}}, false, true), so not a lot different but I almost always perform updates by _id anyway so I could do .update({_id:1234},{$set:{"arr. {x:1}.y":3}}) which is arguably clearer than .update({_id:1234,"arr.x":1},{$set:{"arr.$.y":3}}) and I could also do .update({_id:1234},{$set:{"arr. {x:1}.y:3, "arr. {x.2}.y":4}}) which you can't with positional operator. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Keith Branton [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
I wasn't aware of http://jira.mongodb.org/browse/SERVER-831. I think that combined with multi-array flag would actually accomplish my example use cases. However, in my system I frequently make modifications to several nested collections at the same time and the positional operator can only be used once per update AFAIK - so it would not actually accomplish my real use-cases. What my proposal would do (and I should have provided some examples showing this too) is allow several of these operations in a single atomic update - where each $update is self contained (within the context of the field) and independent of the overall query used to select the document. The positional operator has always felt a bit weak and limiting because you can only use it for one array in an update. I did have an alternative idea for syntax which is a lot simpler and more terse, but still more powerful than the positional operator and 831 but is a bit less consistent with the rest of mongo - but may be easier to implement - I don't know: using the same examples... to delete all the comments with by=3 db.a.update({}, {$pull:{"photos.comments":{$elemMatch:{by:3}}}}, false, true) to add some "extra stuff" to all comments with by=6 db.a.update({}, {$set:{"photos.comments. {by:6}.extra":"stuff"}}, false, true) to add a new comment: "Comment 6" with by=7 at the end of the comments array on the first photo 3.jpg in the collection db.a.update({}, {$push:{"photos. {url:'3.jpg'}.comments": {text:"Comment 6", by:7}}}, false, false) to delete all the comments with by=3 on photo 1.jpg db.a.update( {"photos.url":"1.jpg"}, {$pull:{"photos. {url:'1.jpg'}.comments":{$elemMatch: {by:3}}}, false, true) add "read=false" to all comments on all photos - imagine adding a new feature db.a.update({},{$set:{"photos.comments.{}.read":false}}, false, true) So essentially allowing use of the $elemMatch operator to identify groups of fields to be operated on, and supporting queries embedded in field names - thoughts? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Eliot Horowitz (Inactive) [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Well there are really 2 different things in here. 1 is being able to nested positional, which there is a case for: The other is to be able to do $set on multiple array items. So I would do something like: , { x : 2 , y : 1 }, { x : 1 , y : 1 }] } .update( { "arr.x" : 1 }, { $set : { "arr.$.y" : 3 }} , false , true , true /* this is new and means multi-array update */ ) | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Keith Branton [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@Eliot - I'm not really sure what you mean by that. Could you please give an example showing how it might look wrt some of the above use-cases? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Eliot Horowitz (Inactive) [ 04/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Would just adding another flag like multi access multi_per_array satisfy this? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Comment by Keith Branton [ 03/Feb/11 ] | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A few use-cases based on the following data...
to delete all the comments with by=3
to add some "extra stuff" to all comments with by=6
to add a new comment: "Comment 6" with by=7 at the end of the comments array on the first photo 3.jpg in the collection
to delete all the comments with by=3 on photo 1.jpg
add "read=false" to all comments on all photos - imagine adding a new feature
|