40

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 :)

5 Answers 5

63

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

Sign up to request clarification or add additional context in comments.

7 Comments

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?
What happens if 'tags' field doesn't exist?
If tags field doesn't exist, it is created. @GauravOjha
Will it delete the old record?
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?
|
11

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.

2 Comments

pymongo.errors.WriteError: Unknown modifier: $pushAll
$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}}}
9

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)

2 Comments

what is ref here?
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/…
5

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)

Comments

-1

You can use $push

collection_name.update_one({'ref': ref}, {'$push': {'tags': new_tag}})

You can update multiple arrays in a single query

collection_name.update_one({'ref': ref}, {'$push': {'field1': value1, 'filed2': value2}})

Values can be pushed to push like below.

{ $push: { <field1>: <value1>, ... } }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.