6

I am wondering how do you update a nested array with PyMongo/MongoDB by selecting a document(row) and then going into the nested array and selecting a specific object.

{
    "_id"    : "12345",
    "name"   : "John Doe,
    "mylist" : [
         {
            "nested_id" : "1",
            "data1"     : "lorem ipsum",
            "data2"     : "stackoverflow",
            "data3"     : "james bond"
         },
         {
            "nested_id" : "2",
            "data1"     : "lorem ipsum",
            "data2"     : "stackoverflow",
            "data3"     : "james bond"
         },
         {
            ....
         }     
      ]
}

and then lets say you pass a discretionary with the elements you want to update. In this example only update data1 and data3

data = {
   "data1" : "new lorem",
   "data3" : "goldeneye"       
} 

I have tried with the following syntax, but with no success.

db.testing.find_and_modify(
            query={"_id": "12345", 'mylist.nested_id' : "1"},
            update={"$set": {'mylist' : data}})

what it should look like after the update

{
        "_id"    : "12345",
        "name"   : "John Doe,
        "mylist" : [
             {
                "nested_id" : "1",
                "data1"     : "new lorem",
                "data2"     : "stackoverflow",
                "data3"     : "goldeneye"
             },
             {
                "nested_id" : "2",
                "data1"     : "lorem ipsum",
                "data2"     : "stackoverflow",
                "data3"     : "james bond"
             },
             {
                ....
             }     
          ]
    }
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Sigils
  • 2,492
  • 8
  • 24
  • 36

1 Answers1

9

Use "dot notation" and the positional operator in the update portion. Also transform your input to match the "dot notation" form for the key representation:

# Transform to "dot notation" on explicit field
for key in data:
    data["mylist.$." + key] = data[key]
    del data[key]

# Basically makes
# { 
#      "mylist.$.data1": "new lorem",
#      "mylist.$.data3": "goldeneye"
# }

db.testing.find_and_modify(
    query = {"_id": "12345", 'mylist.nested_id' : "1"},
    update = { "$set": data }
)

So that will transpose $ to the actual matched element position from the query portion of the update. The matched array element will be updated and using "dot notation" only the mentioned fields will be affected.

Have no idea what "service" is supposed to mean in this context and I am just treating it as a "transcribing error" since you are clearly trying to match an array element in position.

That could be cleaner, but this should give you the general idea.

jeffery_the_wind
  • 17,048
  • 34
  • 98
  • 160
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • Hello Neil, thanks for the answer! And yes you are indeed right, that "service" was a typo! I have tried out your suggestion and got some errors. In the loop, it is adding `mylist.$.` multiple times, except index[0]. So it looks like this `mylist.$.mylist.$.mylist.$.data1 : "data"`. But got it working if I create an empty dict and add the "new" items to that instead. But would like to know to know if you can do that without and use your method! :) – Sigils Mar 03 '15 at 11:09
  • @Sigils I'm sorry. What you are saying is you have problems modifying the "dict" content. Why would that be? – Neil Lunn Mar 03 '15 at 11:14
  • Don't be sorry, you have already helped me a lot. But I will try to write the error here. the data dict after the loop is looking like this `data = { "mylist.$.data1" : "new lorem", "mylist.$.mylist.$.mylist.$.data2" : "more data", "mylist.$.mylist.$.mylist.$.data3" : "goldeneye" } ` As you can see it is adding perfectly the first time, but after that it just keeps adding more! – Sigils Mar 03 '15 at 11:19
  • 2
    @Sigils Damn. Did not even test that part and may have stumbled on a critical Python Bug. And apart from contracts that require it I barely touch the language to be honest. The point was to alter the dict to a "dot notation" form. That's the answer, but I'll probably take the time to search for a valid way to do this that does not result in the silly compounded concatenation here as demonstrated. You know, because to solve and explain the basic problem is not acceptable. Only "copy and paste" code works for most people, Right? – Neil Lunn Mar 03 '15 at 11:25
  • 1
    No worres, I did accept your answer as acceptable! :) I just wanted to let you know and perhaps other people who search for this! – Sigils Mar 03 '15 at 11:28