Append item to MongoDB document array in PyMongo without re-insertion

42,943

Solution 1

You don't need to use to retrieve the document first just use the .update method with the $push operator.

def update_tags(ref, new_tag):
    coll.update({'ref': ref}, {'$push': {'tags': new_tag}})

Since update is deprecated you should use the find_one_and_update or the update_one method if you are using pymongo 2.9 or newer

Solution 2

Just to add to @ssytvane answer,and to answer @Guarav: you can add "upsert = True" if it does not exist:

def update_tags(ref, new_tag):
    coll.update({'ref': ref}, {'$push': {'tags': new_tag}}, upsert = True)

or

def update_tags(ref, new_tag):
    coll.update_one({'ref': ref}, {'$push': {'tags': new_tag}}, upsert = True)

Solution 3

You can simply do

1) If you want to append single entry

def update_tags(ref, new_tag):
    coll.update({'ref': ref}, {'$push': {'tags': new_tag}})

eg:

{
    "_id" : ObjectId("561c199e038e42b10956e3fc"),
    "tags" : [ "tag1", "tag2", "tag3" ],
    "ref" : "4780"
}
>> update_tags("4780", "tag4")
{'updatedExisting': True, u'nModified': 1, u'ok': 1, u'n': 1}
>> coll.find_one({"ref":"4780"})
{
    "_id" : ObjectId("561c199e038e42b10956e3fc"),
    "tags" : [ "tag1", "tag2", "tag3" , "tag4" ],
    "ref" : "4780"
}

2) If you want to append multiple entries

def update_tags(ref, new_tag):
    coll.update({'ref': ref}, {'$pushAll': {'tags': new_tag}}) #type of new_tag is list

eg:

{
    "_id" : ObjectId("561c199e038e42b10956e3fc"),
    "tags" : [ "tag1", "tag2", "tag3" ],
    "ref" : "4780"
}
>> update_tags("4780", ["tag5", "tag6", "tag7"])
{'updatedExisting': True, u'nModified': 1, u'ok': 1, u'n': 1}
>> coll.find_one({"ref":"4780"})
{
    "_id" : ObjectId("561c199e038e42b10956e3fc"),
    "tags" : [ "tag1", "tag2", "tag3" , "tag4" , "tag5", "tag6", "tag7" ],
    "ref" : "4780"
}

Note: If the key is not already present, then mongo will create new key.

Solution 4

There had been some good answers that are correct but in my opinion writing update_tags this way is better and more usable:

def update_tags(ref, *args):
    coll.update_one(ref, {'$push': {'tags': {'$each': args}}})

this way you can do both appending one tag or appending many tags:

>> update_tags(ref, 'tag5')
>> update_tags(ref, 'tag5', 'tag6')
>> list_of_new_tags = do_something_that_returns_list_of_tags()
>> update_tags(ref, *list_of_new_tags)
Share:
42,943

Related videos on Youtube

deadbits
Author by

deadbits

Updated on January 08, 2022

Comments

  • deadbits
    deadbits over 2 years

    I am using MongoDB as the back-end database for Python web application (PyMongo + Bottle). Users can upload files and optionally 'tag' these files during upload. The tags are stored as a list within the document, per below:

    {
        "_id" : ObjectId("561c199e038e42b10956e3fc"),
        "tags" : [ "tag1", "tag2", "tag3" ],
        "ref" : "4780"
    }
    

    I am trying to allow users to append new tags to any document. I came up with something like this:

    def update_tags(ref, new_tag)
        # fetch desired document by ref key as dict
        document = dict(coll.find_one({'ref': ref}))
        # append new tag
        document['tags'].append(new_tag)
        # re-insert the document back into mongo
        coll.update(document)
    

    (fyi; ref key is always unique. this could easily be _id as well.) It seems like there should be a way to just update the 'tags' value directly without pulling back the entire document and re-inserting. Am I missing something here?

    Any thoughts are greatly appreciated :)

  • langlauf.io
    langlauf.io over 7 years
    What is the difference between the two? Just the return value (the document vs an UpdateResult object) ? When do you use one or the other?
  • Gaurav Ojha
    Gaurav Ojha about 7 years
    What happens if 'tags' field doesn't exist?
  • styvane
    styvane over 6 years
    If tags field doesn't exist, it is created. @GauravOjha
  • Rajiv Sharma
    Rajiv Sharma over 5 years
    pymongo.errors.WriteError: Unknown modifier: $pushAll
  • Navi
    Navi almost 4 years
    what is ref here?
  • Navi
    Navi almost 4 years
    Will it delete the old record?
  • Navi
    Navi almost 4 years
    must be an array but is of type object in document this error pops up
  • A.N.Tanvir
    A.N.Tanvir almost 4 years
    $pushAll won't work for appending multiple values to an array. Use $push with the $each modifier to append multiple values to the array field. {'$push': {'tags': { '$each': new_tags_array}}}
  • Zaraki Kenpachi
    Zaraki Kenpachi over 2 years
    upsert (optional): When True, inserts a new document if no document matches the query. Defaults to False. api.mongodb.com/python/2.9/api/pymongo/…
  • Belly Buster
    Belly Buster over 2 years
    Don't add questions as an answer; start a new question of you're own.
  • Flair
    Flair over 2 years
    If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review
  • Jason117
    Jason117 over 2 years
    what happens if the new_tag already exits?
  • Rylan Schaeffer
    Rylan Schaeffer about 2 years
    When I try appending a dictionary to a list, I get {TypeError}unhashable type: 'dict'. Why is PyMongo trying to hash the dictionary, and how do I append a dictionary to the list?
  • ray
    ray almost 2 years
    If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review