使用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方法
insertOneinsert_one
insertManyinsert_many
findfind
updateoneupdate_one
updateManyupdate_many
deleteOnedelete_one
deleteManydelete_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ā)故事