[SERVER-21094] $merge operator for updating nested subdocuments Created: 23/Oct/15  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: New Feature Priority: Major - P3
Reporter: David Henderson Assignee: Backlog - Query Team (Inactive)
Resolution: Won't Do Votes: 12
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to SERVER-14609 Add $setOnMissing update operator Closed
Assigned Teams:
Query
Participants:

 Description   

As noted in mongodb-user (https://groups.google.com/d/msg/mongodb-user/7bt38w9RFFU/VmUJQZ0pld0J) it would be useful to have a server-side $merge operator for use when updating documents with nested sub-documents.

From the group:

Say I have this object:

> obj = { "subobj" : { "name": "mysub", "mymap" : { "a" : { "id": "a_obj" },
                                                    "b" : { "id" : "b_obj" },
                                                    "c": {"id": "c_obj" } } } }

And I insert it into my collection:

> db.xtest.insert(obj)

And now I want to update the "id" of the "b" field in the embedded "mymap".
I know I can do this with dot notation:

> db.xtest.update({"_id" : ObjectId("55146d3e497e7087b30da8cf") },
                  { "$set" : { "subobj.mymap.b.id" : "new_b_id" } } )

The dot notation becomes tedious pretty quickly, as you need to write code to flatten the sub-objects to generate the update values. When subobj is complex, the query can become large very quickly.

This would be a win for consistency and efficiency (presumably a smaller update query would go out over the wire)

Suggested syntax:

 db.xtest.update({"_id" : ObjectId("55146d3e497e7087b30da8cf") },
                  { "$merge" : { "subobj": {"mymap" : { "a" : { "id": "a_obj" },
                                                    "b" : { "id" : "b_obj" },
                                                    "c": {"id": "c_obj" } } )

Behaviour:

  • Existing values not referenced in the $merge document would be unaffected
  • Values that do not exist in the DB version of the document would be created
  • Values that do exist in the DB version of the document would be updated to the value in the $merge document
  • Atomic operation


 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.

This can be done now by setting the object to its new value using $mergeObjects expression with existing subdocument and one that contains fields that need to be set to new value.

The more complex "upsert to array" that was mentioned:

db.c.update({}, [
      {$set:{"b":{$mergeObjects:[
            "$b",
            { id: <newId> }
      ]}}}
]);

Comment by Ali Rahbari [ 19/Dec/17 ]

This can be so much helpful when working with $[<identifier>] and updating multiple field of a document in array:

obj = {_id: 1, arr: [

{a:1, b:2, c:3, d:4}

]}

db.update({_id: 1},
{$set: {'arr.$[e].b': 5, 'arr.$[e].c': 6, 'arr.$[e].d': 7}},
{arrayFilters: [{'e.a': 1}]})

db.update({_id: 1},
{$merge: {'arr.$[e]': {b: 5, c:6, d:7}},
{arrayFilters: [{'e.a': 1}]})

Comment by Jon McKenzie [ 23/Dec/15 ]

+1 to this. It seems pretty unsafe that a client has to generate key names (especially where those key names come from untrusted user inputs – it opens the possibility to a lot of security problems). At the very least, it would be nice if there were library support for performing this conversion automatically.

Although JSON in Javascript isn't really completely equivalent to a hash/dict in other languages, it's treated similarly in the respective drivers. As a Python or Ruby user, it's natural to want to Hash#update or dict#update to merge one hash into another. That a user has to generate Mongo-specific signalling to accomplish this in Mongo seems both prone to error and unnatural.

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