Learn Python Series (#19) - PyMongo Part 2

in #utopian-io7 years ago (edited)

Learn Python Series (#19) - PyMongo Part 2

python_logo.png

What Will I Learn?

  • You will learn the remainder of the basic CRUD operations in MongoDB (update_one(), update_many(), replace_once(), delete_one());
  • about some useful Query Operators;
  • and hopefully understand the Query Operator syntax as well;
  • and as a result you can pinpoint and "slice" more specific queries for data retrieval, document updating and deletion.

Requirements

  • A working modern computer running macOS, Windows or Ubuntu
  • An installed Python 3(.6) distribution, such as (for example) the Anaconda Distribution
  • The ambition to learn Python programming

Difficulty

Intermediate

Curriculum (of the Learn Python Series):

Learn Python Series (#19) - PyMongo Part 2

In the previous Learn Python Series episode about PyMongo Part 1, we learned - in essence - the difference between SQL and NoSQL databases, and we learned how to setup and interact with MongoDB via Python's PyMongo iwth respect to inserting one or more documents to a collection, finding one or more documents stored in a collection, based on a certain - or empty - search query, and we learned how to count the number of those documents found.

In this episde, we'll expand our knowledge regarding PyMongo with a number of techniques. Let's get started!

Updating one document using update_one() and the $set update operator

As a brief reminder, and/or for the people "jumping in" on this PyMongo Part 2 episode, let's first import pymongo again, establish the connection, assign the database and collection, and take it from there:

import pymongo

# Importing the pretty print module,
# you don't have to, it just formats better
# for readability on Utopian.io / Steemit.com
from pprint import pprint

client = pymongo.MongoClient('mongodb://localhost:27017')
db = client.test_mongo
coll = db.accounts

Let's first print the current state the scipio document is in, so we clearly know which effect updating it wil have:

scipio = coll.find_one({"account": "scipio"})

# print() instead of pprint() works just fine as well
pprint(scipio)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'slogan': "Does it matter who's right, or who's left?"}

Now, when looking at https://steemd.com/@scipio I see my Steem Id is 422033 so let's add that to the scipio account document, by updating it with update_one():

result = coll.update_one(
    {"account": "scipio"}, 
    {"$set": {"account_id": 422033}}
)

scipio = coll.find_one({"account": "scipio"})
print('Updated Scipio document: {}'.format(scipio))
Updated Scipio document: {'_id': ObjectId('5ae46285dd58330cd666056f'), 'account': 'scipio', 'slogan': "Does it matter who's right, or who's left?", 'account_id': 422033}

Nota bene 1: the update_one() method first expects a key:value pair argument to find the target document (to be updated) with, then it expects the update query as a second argument.

Nota bene 2: the $set operator creates the field "account_id" if it doesn't exist yet, otherwise it updates it.

Updating multiple documents using update_many()

Suppose we want to update all (currently: 4) documents in the accounts collection with the key:value pair {"active": True}. The update_many() method is perfectly suited to do that. It works as follows:

result = coll.update_many({}, {"$set": {"active": True}})
all_accounts = coll.find()
for each_account in all_accounts:
    pprint(each_account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660570'),
 'account': 'stoodkev',
 'active': True}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}
{'_id': ObjectId('5ae46693dd58330cd6660572'),
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'}

Updating a single document by replacing its content using replace_one()

Now let's again have a look at the jedigeiss account. Being German, @jedigeiss without a doubt is a huge fan of Matthias Reim's song "Verdammt ich lieb dich" (published in 1994), so let's change his slogan using replace_once() like so:

# PS: pymongo (relatively) recently changed the location of
# the objectid class from pymongo to bson.
# If you want to query using ObjectIds, import this as well:
from bson.objectid import ObjectId

new_slogan = "Verdammt ich lieb dich, Ich lieb dich nicht."
result = coll.replace_one({'_id': ObjectId('5ae46693dd58330cd6660572')}, 
                          {"slogan": new_slogan})

jedigeiss = coll.find_one({'_id': ObjectId('5ae46693dd58330cd6660572')})
print(jedigeiss)
{'_id': ObjectId('5ae46693dd58330cd6660572'), 'slogan': 'Verdammt ich lieb dich, Ich lieb dich nicht.'}

Hmmmm... the "slogan" value did change to the famous Matthias Rein song, but now the "account" and "active" fields (key:value pairs) are gone!

This is of course not a bug but a feature of the replace_one() method: as opposed to the update_one() method that leaves the "other" document fields in tact, the replace_one() method replaces the entire document data with the data passed to it as an argument. Please keep this in mind when using replace_one()!

Deleting a single document with delete_one()

Since the accounts document previously containing the data from @jedigeiss now only contains an ObjectId and a slogan from Matthias Rein, we might as well delete that document alltogether. We can do this, like so:

result = coll.delete_one({'_id': ObjectId('5ae46693dd58330cd6660572')})
num_accounts = coll.find().count()
print(num_accounts)
3

And as a result, our accounts collection now consists of only 3 accounts.

But... this tutorial episode cannot continue before bringing back my friend and fellow Utopian Advisor @jedigeiss to the database, so let's do so via:

jedigeiss = {
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'
}

result = coll.insert_one(jedigeiss)
all_accounts = coll.find()
for each_account in all_accounts:
    pprint(each_account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660570'),
 'account': 'stoodkev',
 'active': True}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}
{'_id': ObjectId('5ae4bc73dd58332709589486'),
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'}

... better!

Recap: What have we learned thus far regarding PyMongo?

We can now do so-called basic CRUD operations using PyMongo:

  • C (Create): insert_one(), insert_many()
  • R (Read): find_one(), find_many()
  • U (Update): update_one(), update_many(), replace_one()
  • D (Delete): delete_one()

Using Query Operators

Up until now, we've been using very simple queries, either none (to find or update all documents in the collection) or so specific that only one document was found (either by "account": name or by ObjectId). But one of the big advantages of MongoDB is its ability to use Query Operators, allowing you to define more complex queries for finding, updating and/or deleting documents or fields. Let's review a few well-known and useful Query Operators.

$exists

$exists matches documents containing / having a specific field, for example the "slogan" field, like so:

result = coll.find({"slogan": {"$exists": True}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae4bc73dd58332709589486'),
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'}

or, the other way around, to find all accounts documents not having the "slogan" field in them:

result = coll.find({"slogan": {"$exists": False}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46693dd58330cd6660570'),
 'account': 'stoodkev',
 'active': True}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}

$gt and $gte, $lt and $lte

$gt matches values greater than ( > ) a specified value, $gte matches values greater than or equal to ( >= ) a specified value:

result = coll.find({"account_id": {"$gt": 261379}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
result = coll.find({"account_id": {"$gte": 261379}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}

$lt matches values smaller than ( < ) a specified value, $lte matches values smaller than or equal to ( <= ) a specified value:

result = coll.find({"account_id": {"$lt": 422033}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}
result = coll.find({"account_id": {"$lte": 422033}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}

$ne

$ne matches documents that are not equal to a specified value. Please note that if documents don't contain a specific field at all, that the $ne Query Operator matches those non-existent field-documents as well!

result = coll.find({"account_id": {"$ne": 123456}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660570'),
 'account': 'stoodkev',
 'active': True}
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}
{'_id': ObjectId('5ae4bc73dd58332709589486'),
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'}

$in and $nin

The $in operator allows for passing in a list (array) of values and it then matches any documents containing values specified in the list (array).

result = coll.find({"account": {"$in": ["stoodkev","scipio", "jedigeiss"]}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46285dd58330cd666056f'),
 'account': 'scipio',
 'account_id': 422033,
 'active': True,
 'slogan': "Does it matter who's right, or who's left?"}
{'_id': ObjectId('5ae46693dd58330cd6660570'),
 'account': 'stoodkev',
 'active': True}
{'_id': ObjectId('5ae4bc73dd58332709589486'),
 'account': 'jedigeiss',
 'active': True,
 'slogan': 'IT Nerd, Risk Specialist, Musician, Cryptocoin Enthusiast, Banker, '
           'Gamer'}

The $nin operator does the opposite: it matches any documents not specified in the list (array).

result = coll.find({"account": {"$nin": ["stoodkev","scipio", "jedigeiss"]}})
for account in result:
    pprint(account)
{'_id': ObjectId('5ae46693dd58330cd6660571'),
 'account': 'fabiyamada',
 'account_id': 261379,
 'active': True}

What did we learn, hopefully?

In this episode, we expanded on our gathered knowledge regarding PyMongo by completing the basic CRUD (Create, Read, Update, Delete) operations, by reviewing the methods update_one(), update_many(), replace_one(), and delete_one(). We then looked at a few common so-called "Query Operators" with which we can make somewhat more complex queries in order to find, update or delete documents based.

Thank you for your time!



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

The submission is reviewed and approved. Thank you for your contributions to open source community.

I have a question for the update methods, how would we update many values in a subset of a document or documents in a collection, like as we would change the reviewed state of many posts in an account or accounts? Should we define a variable like coll = db.accounts , but for the subset of the accounts? Or is there a different syntax of update_many() to this?


Need help? Write a ticket on https://support.utopian.io.
Chat with us on Discord.

[utopian-moderator]

Hey @roj, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!

Great series @scipio, there is always something to learn, beginners and pros alike.
Matthias Reim, well, hahahaha. Thanks for setting the data back to my normal self ;)
Keep up the brilliant work!
Jan

Well.... I really do think the slogan "Verdammt ich lieb dich" suits you right! :-)
And... could you do a video of yourself performing better on the guitar than Matthias did?

On the score metric "hair coupe" you're already doing slightly better! :P

hello, @scipio
I am dotnet programmer, Seems it will be easy for me to learn python.
Actually your Presentation inspired me...

I think this is the best post,
today I see, about python. You choose a great content for teaching us.

Dear@scipio sir,
It's very helpful post for us. I shall follow you continue.
Thank you very much for share a good post.

Hi! I will upvote and resteem your post to my 35,000+ followers if you reply to this comment.
👍🏻 a-0-0

In my opinion your knowledge sharing work is really appreciable and i am not in developer field but in my opinion the new developers who are working towards the data protection, these session post is really helpful and this knowledge can bring some beneficial aspects in turn when implemented. Thanks for sharing and wishing you an great day. Stay blessed. 🙂

Resteemit done .I always see your post..

another part two, i think its helpfull for programer.
thanky you sir@scipio

Very nice tutorial. I've been recently using MongoBD more and more recently. Its nice to see a good little rundown on the technology especially with such a popular programming language.