使用Python操作MongoDB
微信公眾號:運(yùn)維開發(fā)故事,作者:double冬
通常在項(xiàng)目中,一般都需要一種編程語言來操作數(shù)據(jù)庫,使用Python來操作數(shù)據(jù)庫有著天然的優(yōu)勢,因?yàn)镻ython的字典和MongoDB的文檔幾乎是一樣的格式,本文講介紹如何使用Python進(jìn)行MongoDB操作
1 連接數(shù)據(jù)庫
1.1 安裝PyMongo
使用Python操作MongoDB需要使用一個(gè)第三方庫——PyMongo。安裝這個(gè)庫與安裝Python其他的第三方庫一樣,使用pip安裝即可:
python3 -m pip install pymongo
也可以指定安裝的版本:
python3 -m pip3 install pymongo==3.5.1
更新 pymongo 命令:
python3 -m pip3 install --upgrade pymongo
安裝完成以后,打開Python交互環(huán)境,導(dǎo)入PyMongo。如果不報(bào)錯(cuò)(如圖所示),則表示安裝成功
image.png
1.2 連接數(shù)據(jù)庫
要使用PyMongo操作MongoDB,首先需要初始化數(shù)據(jù)庫連接。
- (1)如果 MongoDB 就運(yùn)行在本地電腦上,而且也沒有修改端口或者添加用戶名和密碼,則初始化MongoClient的實(shí)例不需要帶參數(shù),直接寫為以下格式:
import pymongo
conn = pymongo.MongoClient()
- (2)如果MongoDB運(yùn)行在其他服務(wù)器上,則需要使用“URI(UniformResource Identifier,統(tǒng)一資源標(biāo)志符)”來指定鏈接地址
import pymongo
conn = pymongo.MongoClient('mongodb://test:12345@45.10.110.77:27019')
MongoDB的URI格式如下:mongodb://用戶名:密碼@服務(wù)器IP或域名:端口例如:
- (3)如果沒有設(shè)置權(quán)限驗(yàn)證,則不需要用戶名和密碼,可寫為
import pymongo
conn = pymongo.MongoClient('mongodb://45.10.110.77:27019')
1.3 連接庫與集合
PyMongo連接庫與集合有兩種方式
- 方式1
連接數(shù)據(jù)庫與集合的方法1
from pymongo import MongoClient conn = MongoClient() databae = conn.數(shù)據(jù)庫名 collection = database.集合名
需要注意,在使用這種方式時(shí),代碼中的“數(shù)據(jù)庫名”和“集合名”都不是變量名,它們直接就是庫的名字和集合的名字。例如,要連接上example_data_1所在的集合,則Python代碼如下:
from pymongo import MongoClient conn = MongoClient() database = conn.chapter_1 collection = database.example_data_1
- 方式2
連接數(shù)據(jù)庫與集合方法2
from pymongo import MongoClient
db_name = 'chapter_1'
collection_name = 'example_data_1'
conn = MongoClient()
database = conn[db_name]
collection = database[collection_name]
在使用這種方式時(shí),在方括號中可以直接填變量來指定庫名和集合名。當(dāng)然,也可以直接填字符串,例如:
from pymongo import MongoClient conn = MongoClient() database = conn['chapter_1'] collection = database['example_data_1']
方式1 和方式2效果是完全相同的。大家可以任意選擇一種自己喜歡的方式。
方式2主要用在需要批量操作數(shù)據(jù)庫的情況下。例如在項(xiàng)目中,有時(shí)有多個(gè)測試環(huán)境,現(xiàn)在需要同時(shí)更新這些環(huán)境對應(yīng)的數(shù)據(jù)庫,則可以使用方式2。因?yàn)?,這樣可以將多個(gè)數(shù)據(jù)庫的名字或者是多個(gè)集合的名字保存在列表中,然后再使用循環(huán)來進(jìn)行操作,如下所示:
database_name_list = ['develop_env_alpha','develop_env_beta','develop_env_preflight'] for each_db in database_name_list: database = conn[each_db] collection = database.account collection.updateMany(.....)
其中第3行代碼,在循環(huán)里面每次連接不同的庫,這樣寫可以同時(shí)更新多個(gè)數(shù)據(jù)庫的信息,對于同一個(gè)數(shù)據(jù)庫里面的多個(gè)集合,也可以使用這個(gè)方法來操作。
注意: 在 MongoDB 中,集合只有在內(nèi)容插入后才會(huì)創(chuàng)建! 就是說,創(chuàng)建集合(數(shù)據(jù)表)后要再插入一個(gè)文檔(記錄),集合才會(huì)真正創(chuàng)建。
2 MongoDB命令在Python中的對應(yīng)方法
在獲取到集合連接對象collection后,就可以用這個(gè)對象的各個(gè)方法來操作MongoDB了。
雖然 MongoDB 的命令和 collection 的方法名在寫法上有微小的差異,但絕大多數(shù)的MongoDB語句的參數(shù)直接復(fù)制到Python代碼中都可以使用。
MongoDB的命令使用的是駝峰命名法,而PyMongo使用的是“小寫字母加下劃線”的方式。它們的對比見下表:
MongoDB命令 | PyMongo方法 |
---|---|
insertOne | insert_one |
insertMany | insert_many |
find | find |
updateone | update_one |
updateMany | update_many |
deleteOne | delete_one |
deleteMany | delete_many |
例如,Robo 3T執(zhí)行的批量插入語句:
db.getCollection('example_data_1').insertMany([ {'name': '趙小三','age':20,'address':'北京'}, {'name': '錢小四','age':21,'address':'上海'}, {'name': '孫小五','age':20,'address':'山東'}, {'name': '李小六','age':23,'address':'河北'}, {'name': '歐陽小七','age':24,'address':'杭州'} ])
使用Python批量插入數(shù)據(jù),代碼如下:
from pymongo import MongoClient conn = MongoClient() database = conn.chapter_1 collection = database.example_data_2 collection.insert_many([ {'name': '王小二','age':21,'student':True,'address':'廣州'}, {'name': '趙小三','age':20,'student':True,'address':'北京'}, {'name': '錢小四','age':21,'student':True,'address':'上海'}, {'name': '孫小五','age':20,'student':True,'address':'山東'}, {'name': '李小六','age': None,'student':True,'address':'河北'}, {'name': '歐陽小七','age':24,'student':False,'address':'杭州'}, {'name': '公孫小八','age':25,'student':False,'address':'廣州'} ])
其中,第4行代碼中使用了新的集合名字,用以區(qū)別。
使用Python操作MongoDB還有一個(gè)好處:如果當(dāng)前使用的庫或者集合不存在,則在調(diào)用了插入方法以后,PyMongo會(huì)自動(dòng)創(chuàng)建對應(yīng)的庫或集合。
總之,絕大部分的操作,直接從Robo 3T中復(fù)制到Python中都可以運(yùn)行,幾乎不需要修改。
3 插入數(shù)據(jù)到MongoDB
基本語法
collection.insert_one(字典) # 插入一條數(shù)據(jù)
collection.insert_many(包含字典的列表) # 批量插入多條數(shù)據(jù)
被插入的數(shù)據(jù)格式
{'field_1': value_1, 'field_2': value_2} [ {'field_1': value_1, 'field_2': value_2}, {'field_1': value_3, 'field_2': value_4} ]
說明
-
MongoDB不需要提前創(chuàng)建數(shù)據(jù)庫、不需要提前創(chuàng)建集合、不需要提取定義數(shù)據(jù)格式,想插入什么數(shù)據(jù),直接插就行。
-
同一個(gè)集合的不同行數(shù)據(jù),字典可以不一
舉例
在Python中,將字典{‘name’: ’王小六’, ‘a(chǎn)ge’: 25, ‘work’: ’廚師’}插入到MongoDB中。
具體命令如下:
collection.insert_one({'name': ’王小六’, 'age': 25, 'work': ’廚師’})
提示:PyMongo還有一個(gè)通用方法——collection.insert()。
-
如果傳入的是一個(gè)字典,則collection.insert()相當(dāng)于insert_one
-
如果傳入的是一個(gè)包含字典的集合,則collection.insert()相當(dāng)于insert_many
但是PyMongo開發(fā)者準(zhǔn)備移除它,因此不推薦讀者在正式環(huán)境中使用這個(gè)方法。
4 從MongoDB中查詢數(shù)據(jù)
查詢一條數(shù)據(jù)
我們可以使用 find_one() 方法來查詢集合中的一條數(shù)據(jù),查詢example_data_2 文檔中的第一條數(shù)據(jù):
from pymongo import MongoClient db_name = 'chapter_1' collection_name = 'example_data_1' conn = MongoClient() database = conn[db_name] collection = database[collection_name] x=collection.find_one({}) print(x)
輸出結(jié)果:
image.png
查詢集合中所有數(shù)據(jù)
from pymongo import MongoClient db_name = 'chapter_1' collection_name = 'example_data_1' conn = MongoClient() database = conn[db_name] collection = database[collection_name] collection = database.example_data_2 rows = collection.find() for row in rows: print(row)
結(jié)果如下:
image.png
邏輯查詢
collection.find({'字段名': {'基本符號': 邊界值, '基本符號': 邊界值}})
在Python中,從MongoDB中查詢所有“age”大于21小于25,并且“name”不等于“夏侯小七”的記錄。
collection = database.example_data_2 rows = collection.find({'age':{'$lt':25,'$gt':21}, 'name':{'$ne':'公孫小八'}}) for row in rows: print(row)
運(yùn)行效果如圖所示:
image.png
查詢并對結(jié)果進(jìn)行計(jì)數(shù)
collection.find().count()
查詢并對結(jié)果進(jìn)行計(jì)數(shù)
# 在Python中 collection.find().sort('字段名', 方向) # 在Robo 3T的命令輸入?yún)^(qū)域 collection.find().sort({'字段名': 方向})
其中方向?yàn)?strong>1表示升序,方向?yàn)?*-1**表示降序
對字段去重
# 對字段去重 handler.distinct('字段名') # 先篩選再去重 handler.distinct('字段名', 查詢條件)
注意:如果字段的數(shù)量很大,那么輕易不要在Robo 3T里面直接執(zhí)行,否則可能導(dǎo)致Robo 3T卡死
5 更新/刪除MongoDB中的數(shù)據(jù)
基本語法
# 更新一條數(shù)據(jù) collection.update_one(查詢條件, {'$set': 被更新的數(shù)據(jù)}) # 更新所有滿足要求的數(shù)據(jù) collection.update_many(查詢條件, {'$set': 被更新的數(shù)據(jù)})
被更新的數(shù)據(jù)
{'field_1': value_1, 'field_2': value_2}
舉例
在Python中更新數(shù)據(jù)和刪除數(shù)據(jù):
-
(1)對于“name”為“公孫小八”的記錄,將“age”更新為80,將“address”更新為“美國”。
-
(2)刪除“age”為0的數(shù)據(jù)
1.更新MongoDB中的數(shù)據(jù)
在Python中,可以使用udate_many方法來批量更新數(shù)據(jù)
collection.update_many( {'name': '公孫小八'}, {'$set': {'address': '英國','age':80}})
更新操作還支持一個(gè)“upsert”參數(shù)。該參數(shù)的作用是:如果數(shù)據(jù)存在,則更新;如果數(shù)據(jù)不存在,則創(chuàng)建。
例如,對于“name”為“隱身人”的記錄,將“age”改為0,將“address”改為“里世界”
由于example_data_1中沒有這一條記錄,因此直接更新會(huì)報(bào)錯(cuò),如圖所示。
result = collection.update_one({'name':'隱身人'}, {'$set':{'name':'隱身人', 'age': 0, 'address':'里世界'}}) print(list(result))
image.png
加上“upsert”參數(shù),看看效果
result = collection.update_one({'name':'隱身人'},
{'$set':{'name':'隱身人',
'age': 0,
'address':'里世界'}},
upsert = True)
print(result)
image.png
提示:如果打開了更新或插入功能,則“$set”的值是完整的文檔內(nèi)容,應(yīng)該包含每一個(gè)字段,而不僅僅是需要被更新的字段,否則被插入的內(nèi)容只有被更新的這幾個(gè)字段。
2.刪除MongoDB中的數(shù)據(jù)
基本語法
# 刪除第一個(gè)滿足條件的數(shù)據(jù)
collection.delete_one(查詢條件)
# 刪除所有滿足條件的數(shù)據(jù)
collection.delete_many(查詢條件)
刪除“age”為0的數(shù)據(jù)。刪除語句如下:
collection.delete_many({'age': 0})
建議先寫查詢語句,確認(rèn)查詢出來的數(shù)據(jù)就是自己想刪除的數(shù)據(jù),然后把關(guān)鍵字find改為delete_one或者delete_many
6 MongoDB與Python不通用的操作
絕大部分情況下,MongoDB中的命令參數(shù)直接復(fù)制到Python中就可以使用,但有一些情況例外。假設(shè)數(shù)據(jù)集example_data_2如圖所示:
image.png
6.1 空值
在MongoDB中,空值寫作null,在Python中,空值寫作None。
MongoDB不認(rèn)識(shí)None,Python不認(rèn)識(shí)null。
為了從數(shù)據(jù)集example_data_2中查詢出所有“age”字段為空的數(shù)據(jù),在Robo 3T中的查詢語句為:
db.getCollection('example_data_2').find({'age': null})
運(yùn)行結(jié)果如圖所示:
image.png
如果直接把這段查詢語句中的參數(shù)搬到Python中運(yùn)行,則會(huì)導(dǎo)致報(bào)錯(cuò),如圖所示:
image.png
Python會(huì)把null當(dāng)作一個(gè)普通的變量,但是這個(gè)變量又沒有定義,所以導(dǎo)致報(bào)錯(cuò)。
在 Python 中,要查詢空值需要使用 None,對上述代碼做一些修改——把“null”改為“None”,則查詢成功,如圖所示:
image.png
6.2 布爾值
布爾值就是“真”和“假”兩個(gè)值。在MongoDB中,“真”為true,“假”為false,首字母小寫;在Python中,“真”為True,“假”為False,首字母大寫。
在MongoDB中,查詢所有student為true的記錄,如圖所示:
image.png
如果把這段查詢語句的參數(shù)直接復(fù)制到 Python 中,同樣也會(huì)導(dǎo)致報(bào)錯(cuò),因?yàn)?Python 會(huì)把true當(dāng)作一個(gè)普通的變量,如圖所示:
image.png
把true改為True,則查詢成功,如圖所示:
image.png
6.3 排序參數(shù)
對查詢到的結(jié)果進(jìn)行排序是一個(gè)常見操作,在MongoDB中,sort()命令接收一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)字典,Key是被排序的字段名,值為1或者?1。
對于數(shù)據(jù)集example_data_2,在Robo 3T中對“age”字段進(jìn)行倒序排列,如圖所示:
image.png
但在Python中,查詢結(jié)果的sort()方法如果使用MongoDB的寫法則會(huì)報(bào)錯(cuò),如圖所示:
image.png
在Python中,sort()方法接收兩個(gè)參數(shù):第1個(gè)參數(shù)為字段名,第2個(gè)參數(shù)為-1或者1。就能夠正常運(yùn)行,如圖所示:
image.png
6.4 查詢_id
在Robo 3T中,可以根據(jù)_id的值來查詢文檔。此時(shí)查詢語句如下:
image.png
在安裝PyMongo的同時(shí),Python會(huì)自動(dòng)安裝一個(gè)叫作“bson”的第三方庫。ObjectId這個(gè)類需要從bson庫中導(dǎo)入,具體命令如下:
from bson import ObjectId
collection.find({'_id': ObjectId('5e8ac5dfdd9cf99b7a446e99')})
運(yùn)行結(jié)果如下:
image.png
小結(jié)
本文首先介紹了MongoDB的安裝,然后介紹了MongoDB的圖形化操作軟件Robo 3T。通過Robo 3T的命令輸入窗口輸入命令,可實(shí)現(xiàn)對MongoDB數(shù)據(jù)庫的增、刪、改、查操作。
MongoDB的大部分操作都可以平滑移植到Python中。因此,大多數(shù)情況下,直接把Robo 3T中的MongoDB操作語句復(fù)制到Python中就能使用。當(dāng)然,有很小一部分情況例外。
公眾號:運(yùn)維開發(fā)故事
github:https://github.com/orgs/sunsharing-note/dashboard
作者:double冬 運(yùn)維開發(fā)故事
歡迎關(guān)注:運(yùn)維開發(fā)故事