80

I've a mongodb collection in this form:

{id=ObjectId(....),key={dictionary of values}}
where dictionary of values is {'a':'1','b':'2'.....}

Let dictionary of values be 'd'. I need to update the values of the key in the 'd'. i.e I want to change 'a':'1' to 'a':'2' How can do I this in pymongo?

Code goes something like this:

productData is a collection in mongoDB
for p in productData.find():
     for k,v in p.iteritems():
         value=v['a']
         value=value+1
         v['a']=value

Now reflect the new value in the productData.

This is what I've tried and it introduces a new key-value pair instead of updating the

for p in productData.find():
    for k,v in p.iteritems():
         value=v['a']
         value=value+1
         v['a']=value
         productData.update({'_id':mongoId},{"$set":{'d.a':'100'}},upsert=False)
gizgok
  • 7,303
  • 21
  • 79
  • 124
  • 1
    Duplicate of http://stackoverflow.com/questions/4372797/how-do-i-update-a-mongo-document-after-inserting-it – jdotjdot Dec 04 '12 at 19:56
  • Does the same thing apply for update of an existing field? – gizgok Dec 04 '12 at 19:58
  • Pretty sure it does, yes. Try it and see what happens. – jdotjdot Dec 04 '12 at 20:05
  • 1
    For one you should be doing "for k,v in p['d'].iteritems()" - also, Mark's answer below does work without creating a new key/value pair. You should post more code so we can see what you are doing wrong. – IamAlexAlright Dec 04 '12 at 23:13

4 Answers4

136

You can use the $set syntax if you want to set the value of a document to an arbitrary value. This will either update the value if the attribute already exists on the document or create it if it doesn't. If you need to set a single value in a dictionary like you describe, you can use the dot notation to access child values.

If p is the object retrieved:

existing = p['d']['a']

For pymongo versions < 3.0

db.ProductData.update({
  '_id': p['_id']
},{
  '$set': {
    'd.a': existing + 1
  }
}, upsert=False, multi=False)

For pymongo versions >= 3.0

db.ProductData.update_one({
  '_id': p['_id']
},{
  '$set': {
    'd.a': existing + 1
  }
}, upsert=False)

However if you just need to increment the value, this approach could introduce issues when multiple requests could be running concurrently. Instead you should use the $inc syntax:

For pymongo versions < 3.0:

db.ProductData.update({
  '_id': p['_id']
},{
  '$inc': {
    'd.a': 1
  }
}, upsert=False, multi=False)

For pymongo versions >= 3.0:

db.ProductData.update_one({
  '_id': p['_id']
},{
  '$inc': {
    'd.a': 1
  }
}, upsert=False)

This ensures your increments will always happen.

Mark Unsworth
  • 3,027
  • 2
  • 20
  • 21
10

With my pymongo version: 3.2.2 I had do the following

from bson.objectid import ObjectId
import pymongo

client = pymongo.MongoClient("localhost", 27017)
db = client.mydbname

db.ProductData.update_one({
  '_id': ObjectId(p['_id']['$oid'])
},{
  '$set': {
    'd.a': existing + 1
  }
}, upsert=False)
user6039980
  • 3,108
  • 8
  • 31
  • 57
Ahsan
  • 3,845
  • 2
  • 36
  • 36
  • faced with TypeError Exception Value: Object of type 'ObjectId' is not JSON serializable on django1.11 – Yuseferi Nov 10 '17 at 19:58
2

Something I did recently, hope it helps. I have a list of dictionaries and wanted to add a value to some existing documents.

for item in my_list:
    my_collection.update({"_id" : item[key] }, {"$set" : {"New_col_name" :item[value]}})
Jack
  • 394
  • 1
  • 15
  • 1
    `DeprecationWarning: update is deprecated. Use replace_one, update_one or update_many instead.` – alper Oct 11 '21 at 13:50
0

In Python, the operators (e.g., $set) should be in quotes:

db.ProductData.update(
    {'fromAddress': 'http://localhost:7000/'},
    {'$set': {'fromAddress': 'http://localhost:5000/'}},
    {'multi': True}
)
heilala
  • 770
  • 8
  • 19
Adarsh R
  • 65
  • 1
  • 1
  • 5