為什么要使用 MongoDB 以及 Pymongo
在程序開(kāi)發(fā)實(shí)踐中,除了學(xué)習(xí)代碼、算法之外,其他開(kāi)發(fā)有用的程序一定離不開(kāi)數(shù)據(jù)庫(kù)饭豹。然而,傳統(tǒng)的 CS 專業(yè)里面教授數(shù)據(jù)庫(kù)往往會(huì)從 SQL 數(shù)據(jù)庫(kù)開(kāi)始學(xué)起务漩,但是拄衰,要使用 SQL 數(shù)據(jù)庫(kù),那么第一步一定離不開(kāi)如何建表饵骨,設(shè)置字段翘悉、設(shè)置鍵的類型等,而作為初學(xué)者居触,加上沒(méi)有真是的項(xiàng)目背景的情況下妖混,數(shù)據(jù)庫(kù)的設(shè)計(jì)往往只能是那種一個(gè) User 用戶表,然后再一個(gè) username 轮洋、 passwd制市、 nickname 之類的字段設(shè)計(jì),再在這個(gè)‘小朋友’基礎(chǔ)版本的數(shù)據(jù)庫(kù)上完成一些 CURD 操作后弊予,向再進(jìn)一步開(kāi)發(fā)一些小 DEMO 時(shí)祥楣,由于無(wú)法預(yù)先知道所需要的數(shù)據(jù)模型到底是什么樣的,就會(huì)出現(xiàn)不斷的改表汉柒、改數(shù)據(jù)庫(kù)的情況误褪,再面對(duì)不熟悉的項(xiàng)目,最后弄得焦頭爛額碾褂,反倒是打擊了學(xué)習(xí)的興趣兽间。
再者,在一些個(gè)人項(xiàng)目正塌,或者是一些小微創(chuàng)業(yè)公司嘀略、新孵化的項(xiàng)目恤溶,同樣存在市場(chǎng)不明確、需求不明確的情況屎鳍,使用傳統(tǒng)的數(shù)據(jù)庫(kù)分析宏娄、設(shè)計(jì)流程问裕,一來(lái)耗費(fèi)時(shí)間經(jīng)歷逮壁,二來(lái)辛辛苦苦設(shè)計(jì)出來(lái)的模型可能剛設(shè)計(jì)出來(lái)又不符合需求了。
針對(duì)這些情況粮宛,使用 NoSQL 數(shù)據(jù)庫(kù)窥淆,比如 MongoDB,就能很好避免上述的問(wèn)題巍杈。NoSQL 數(shù)據(jù)庫(kù)有一個(gè)特點(diǎn)忧饭,就是并沒(méi)有表的概念,以 MongoDB 為例筷畦,它只有 數(shù)據(jù)庫(kù)词裤、集合、文檔三種結(jié)果鳖宾,到文檔這個(gè)維度級(jí)別吼砂,還可以在其中創(chuàng)建子文檔、數(shù)組等數(shù)據(jù)類型鼎文∮婕纾基于這種特性,我們就很容易做到“讓產(chǎn)品跑起來(lái)拇惋,最小可用”的理念周偎。當(dāng)然,NoSQL 數(shù)據(jù)庫(kù)并不僅僅只有 MongoDB 一款撑帖,不過(guò)由于 MongoDB 安裝比較簡(jiǎn)單蓉坎,社群完善,網(wǎng)上能找到比較多的教程胡嘿,因此我們可以考慮使用 MongoDB 進(jìn)行 NoSQL 數(shù)據(jù)庫(kù)的學(xué)習(xí)及使用蛉艾。
目前人工智能非常火灶平,在人工智能流行的同時(shí)伺通,Python 憑借其“膠水語(yǔ)言”的特點(diǎn),也越來(lái)越受歡迎逢享。使用 Python罐监,我們可以借助龐大的庫(kù)的支持,很快瞒爬、很方便就能創(chuàng)造出很多有意思的項(xiàng)目弓柱,因此沟堡,本文下面都將以 Python 作為開(kāi)發(fā)語(yǔ)言,MongoDB 作為數(shù)據(jù)庫(kù)矢空,來(lái)學(xué)習(xí)如何使用 Pymongo 這一個(gè) MongoDB 操作的封裝庫(kù)來(lái)實(shí)現(xiàn)對(duì) MongoDB 的使用航罗。
本文雖然標(biāo)題稱為教程,但我卻并不認(rèn)為這篇文章能稱得上教程屁药,主要有以下兩個(gè)原因:
- 人都好為人師而易自滿粥血。在軟件,或者說(shuō)可以行業(yè)酿箭,幾乎所有東西都是日新月異复亏,很可能這篇教程發(fā)布的時(shí)候,這些庫(kù)或者軟件就更新了缭嫡,或者當(dāng)中許多方法都變了缔御,按照文章的方法可能就行不通了,這樣的教程肯定是沒(méi)有多大效用的妇蛀。所以耕突,分享這篇文章,主要是想分享一下從發(fā)現(xiàn)一個(gè)需求评架,到尋求答案眷茁、解決方案,再到最終能實(shí)施落地的一連串思考、思路的分享,這才是值得分享违帆、交流傳播的東西
- 人學(xué)習(xí)的過(guò)程,需要一個(gè)科學(xué)的步驟來(lái)完成雇逞。科學(xué)表明茁裙,看一次塘砸,跟著做一次的學(xué)習(xí)效果,遠(yuǎn)沒(méi)有在看完晤锥、跟著做完掉蔬,再呈現(xiàn)出來(lái)分享給別人的效果好。再有矾瘾,把東西分享出去的過(guò)程當(dāng)中女轿,能收獲到不少自己沒(méi)有意識(shí)到,遺漏甚至錯(cuò)漏的地方壕翩,這對(duì)深造非常有幫助蛉迹。
文章整體脈絡(luò)
本文參考了 《MongoDB權(quán)威指南》以及 MongoDB 的官方文檔,Pymongo 的官方文檔作為參考放妈。實(shí)際中北救,《MongoDB權(quán)威指南》主要以 mongo 終端交互為例進(jìn)行數(shù)據(jù)庫(kù)操作荐操,因此本文借鑒了其中第三章、第四章的部分案例進(jìn)行演示珍策。而 Pymongo 的官方文檔中托启,有一些方法也并沒(méi)有提供詳細(xì)的解釋,因此通過(guò)推敲摸索以及綜合部分Stack Overflow的討論形成攘宙,關(guān)于這一部分屯耸,比較多的存在于修改器的使用部分,如對(duì)游標(biāo)的 min() 及 max() 的使用等模聋。
本文主要以五大部分來(lái)講解如何利用 Pymongo 操作 MongoDB肩民,具體如下:
- 環(huán)境的搭建
- Pymongo 的安裝
- Pymongo的基本使用
- 數(shù)據(jù)庫(kù)連接
- 數(shù)據(jù)庫(kù)、集合相關(guān)操作
- 基本的文檔 CURD
- insert_one() insert_many()
- find() find_one()
- update()
- remove()
- 修改器在更新链方、查詢中的使用
- $set
- $unset
- $inc
- $push
- $addToSet
- $pull
- $each
- $pop
- $lt $lte
- $gt $gte
- $ne
- $in nin
- $or
- $exist
- $all
- $size
- $slice
- min() max()
- $where
- 游標(biāo)的使用
- sor()
- limit()
- skip()
PyMongo 安裝
pymongo 可以使用 pip
安裝,方法如下:
python -m pip install pymongo
如果是使用 Anaconda 環(huán)境的灶搜,可以使用 conda install
來(lái)安裝祟蚀。方法如下:
conda install pymongo
安裝完成后,可以使用
conda list
命令查看 pymongo 模塊是否成功安裝割卖,如果成功安裝前酿,可以在輸出中看到上圖結(jié)果。
Pymongo 數(shù)據(jù)庫(kù)連接鹏溯,數(shù)據(jù)庫(kù)罢维、集合相關(guān)操作
本文介紹使用 MongoDB 為 Docker 的鏡像,系統(tǒng)為 Mac OS 10.13.4丙挽。MongoDB Docker 容器啟動(dòng)后肺孵,地址為默認(rèn)的 localhost:27017
。
pymongo 連接數(shù)據(jù)庫(kù)
我們先引入 pymongo
的 MongoClient
模塊颜阐。再創(chuàng)建一個(gè)連接平窘。
from pymongo import MongoClient
client = MongoClient()
print(client)
>> MongoClient(host=['localhost:27017'], document_class=dict, tz_aware=False, connect=True)
pymongo client 數(shù)據(jù)庫(kù)相關(guān)操作
pymongo client 數(shù)據(jù)庫(kù)的相關(guān)操作主要有三種,分別是使用現(xiàn)有數(shù)據(jù)庫(kù)凳怨,創(chuàng)建不存在的數(shù)據(jù)庫(kù)瑰艘,刪除數(shù)據(jù)庫(kù)。具體操作如下:
# 列出當(dāng)前所有數(shù)據(jù)庫(kù)名稱
database_names = client.database_names()
# 使用現(xiàn)有數(shù)據(jù)庫(kù)肤舞,假設(shè)已存在數(shù)據(jù)庫(kù)紫新,數(shù)據(jù)庫(kù)名稱為 test_database
test_database = client.test_database
# 創(chuàng)建不存在的數(shù)據(jù)庫(kù)并使用
new_database = client.new_database
# 刪除現(xiàn)有數(shù)據(jù)庫(kù)
client.drop_database('new_database')
小結(jié)一下 pymongo client 的數(shù)據(jù)庫(kù)操作,可以簡(jiǎn)單的理解為三類常見(jiàn)操作李剖,一類是選擇或者創(chuàng)建數(shù)據(jù)庫(kù)芒率,都可以直接指定一個(gè)數(shù)據(jù)庫(kù)名稱,如果該數(shù)據(jù)庫(kù)存在就使用該數(shù)據(jù)庫(kù)杖爽,否則將創(chuàng)建一個(gè)敲董;第二類為刪除數(shù)據(jù)庫(kù)操作紫皇,可以使用 drop_database()
方法。最后則為查看當(dāng)前 MongoDB 連接中的所有數(shù)據(jù)庫(kù)腋寨,可以使用 database_names()
方法聪铺。
pymongo 集合相關(guān)操作
pymongo 的集合操作和數(shù)據(jù)庫(kù)的操作類似,同樣是指定一個(gè)集合名稱萄窜,如何集合存在就使用該集合铃剔,否則創(chuàng)建一個(gè)新的集合。以下以使用 test_database 數(shù)據(jù)庫(kù)進(jìn)行集合操作:
# 查看當(dāng)前數(shù)據(jù)庫(kù)中的所有集合
collection_names = test_database.collection_names()
# 使用一個(gè)已存在數(shù)集合查刻,假設(shè)集合名稱為 posts
posts = test_database.posts
# 創(chuàng)建一個(gè)新的集合
new_collection = test_database.new_collection
# 刪除一個(gè)集合
test_database.drop(''new_collection)
這里有一點(diǎn)值得注意键兜,當(dāng)我們創(chuàng)建一個(gè)新集合時(shí),如果我們只是創(chuàng)建集合穗泵,但并沒(méi)有向集合中添加數(shù)據(jù)普气,這是新的集合其實(shí)并沒(méi)有創(chuàng)建,可以看下面的例子:
test_database.collection_names()
>> []
posts = test_database.posts
# 創(chuàng)建一個(gè)新集合佃延,但未向集合中存放數(shù)據(jù)
test_database.collection_names()
>> []
# 向集合中存放數(shù)據(jù)
doc = {'test': 1}
post_doc = posts.insert_one(doc)
test_database.collection_names()
>> ['posts']
向集合中添加文檔
pymongo 提供兩種添加新文檔的方法现诀,分別是 insert_one
和 insert_many
。其中履肃,insert_one
是將一個(gè)文檔添加到集合中仔沿, insert_many()
是將多個(gè)文檔一次性添加到集合中。例子如下:
# insert_one() 用法
foo = test_database.foo
foo.insert_one({'_id': 0})
for data in foo.find():
print(data)
>> {'_id': 0}
# insert_many() 用法
foo.insert_many([{'_id': 1}, {'_id': 2}])
for data in foo.find():
print(data)
>> {'_id': 0}
>> {'_id': 1}
>> {'_id': 2}
刪除集合中的文檔
我們可以使用 remove()
方法完成刪除集合中的文檔尺棋。
# 刪除指定文檔
foo.remove({'_id': 2})
for data in foo.find():
print(data)
>> {'_id': 0}
>> {'_id': 1}
# 刪除所有文檔
foo.remove()
foo.count()
>> 0
更新文檔數(shù)據(jù)
我們先在 test_databse 數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè) user 數(shù)據(jù)庫(kù)封锉,然后在數(shù)據(jù)庫(kù)中添加一條用戶文檔記錄,具體操作如下:
user = test_database.user
joe = {'name': 'joe', 'friends': 32, 'enemies': 2}
user.insert_one(joe)
user.find_one()
>> {'_id': ObjectId('5ac8d33a29561f64220f6f9a'),
'enemies': 2,
'friends': 32,
'name': 'joe'}
接下來(lái)膘螟,我們將對(duì)這個(gè)文檔作出幾處修改成福,修改如下:
- 將 friedns 和 enemies 移動(dòng)到 relationships 子文檔之下
- 將字段 name 更改為 username
具體操作如下:
joe = user.find_one({'name': 'joe'})
print(joe)
>> {'_id': ObjectId('5ac8d33a29561f64220f6f9a'),
'enemies': 2,
'friends': 32,
'name': 'joe'}
joe['relationships'] = {'firends': joe['friends'], 'enemies': joe['enemies']}
joe['username'] = joe['name']
del joe['name']
del joe['enemies']
del joe['friends']
print(joe)
>> {'_id': ObjectId('5ac8d33a29561f64220f6f9a'),
'relationships': {'enemies': 2, 'firends': 32},
'username': 'joe'}
user.replace_one({'name': 'joe'}, joe)
joe = user.find_one()
>> {'_id': ObjectId('5ac8d33a29561f64220f6f9a'),
'relationships': {'enemies': 2, 'firends': 32},
'username': 'joe'}
替換文檔我們使用了replace_one()
方法,該方法傳入兩個(gè)參數(shù)萍鲸,第一個(gè)參數(shù)是需要被替換的文檔查找條件闷叉,第二個(gè)參數(shù)是更新的文檔數(shù)據(jù)。
更新操作中的修改器
在實(shí)際中脊阴,更新文檔往往是更新文檔的一部分內(nèi)容握侧,在 MongoDB 中,我們可以使用更新修改器 (update modifier) 來(lái)對(duì)文檔中某些字段進(jìn)行更新嘿期,常用的修改器有以下幾個(gè):
- $set 用來(lái)指定一個(gè)字段的值品擎,如果不存在,將創(chuàng)建一個(gè)新的字段
- $unset 刪除一個(gè)字段
- $inc 用來(lái)增加(或減少)一個(gè)已有鍵的值备徐,如果不存在將會(huì)創(chuàng)建一個(gè)
- $push 向已有的數(shù)組末尾添加一個(gè)元素
- $addToSet 避免插入重復(fù)數(shù)據(jù)
- $pull 刪除元素萄传,基于特定條件
- $each 遍歷列表操作
- $pop 刪除元素
以下將展開(kāi)演示上述修改器的使用方法。
$set
# 先清除之前存儲(chǔ)的 user 集合中的數(shù)據(jù)
user.drop()
user.count()
>> 0
joe = {'name': 'joe',
'age': 30,
'sex': 'male',
'location': 'Wisconsin' }
user.insert_one(joe)
print(user.find_one())
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 為用戶添加一項(xiàng) favorite 的字段
user.update_one({'_id': ObjectId('5ac9836829561f64220f6f9d')}, {'$set' : {'favorite': 'War adn Peace'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'favorite': 'War adn Peace',
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 將 favorite 字段的值修改為 Green Eggs and Ham
user.update_one({'name': 'joe'}, {'$set': {'favorite': 'Green Eggs and Ham'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'favorite': 'Green Eggs and Ham',
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 將 favorite 字段的值替換為一個(gè)數(shù)組
user.update_one({'name': 'joe'}, {'$set': {'favorite': ["Cat's Cradle": , "Foundation Trilogy", "Ender's Game"]}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'favorite': ["Cat's Cradle", 'Foundation Trilogy', "Ender's Game"],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 內(nèi)嵌文檔修改
blog = test_database.blog # 創(chuàng)建一個(gè) blog 集合
posts = {'title': 'A Blog Post', 'content': '...', 'author': {'name': 'joe', 'email': 'joe@example.com'}} # 創(chuàng)建一個(gè) posts 文檔
blog.insert_one(posts)
blog.find_one()
{'_id': ObjectId('5ac98a0a29561f64220f6f9e'),
'author': {'email': 'joe@example.com', 'name': 'joe'},
'content': '...',
'title': 'A Blog Post'}
# 將作者名稱字段 name 的值修改為 joe schmoe
blog.update_one({'author.name': 'joe'}, {'$set': {'author.name': 'joe schmoe'}})
blog.find_one()
>> {'_id': ObjectId('5ac98a0a29561f64220f6f9e'),
'author': {'email': 'joe@example.com', 'name': 'joe schmoe'},
'content': '...',
'title': 'A Blog Post'}
$unset
# 刪除 user 集合中 joe 的 favorite 字段
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'favorite': ["Cat's Cradle", 'Foundation Trilogy', "Ender's Game"],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
user.update_one({'name': 'joe'}, {'$unset': {'favorite': 1}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
$inc
# 創(chuàng)建一個(gè) games 的集合,并向集合中添加數(shù)據(jù)
games = test_database.games
games.insert_one({'game': 'pinball', 'user': 'joe'})
games.find_one()
>> {'_id': ObjectId('5ac9c55f29561f64220f6f9f'), 'game': 'pinball', 'user': 'joe'}
# 增加一個(gè)分?jǐn)?shù)字段 score
games.update_one({'game': 'pinball', 'user': 'joe'}, {'$inc': {'score': 50}})
games.find_one()
>> {'_id': ObjectId('5ac9c55f29561f64220f6f9f'),
'game': 'pinball',
'score': 50,
'user': 'joe'}
# 為 score 字段的值增加 5000
games.update_one({'game': 'pinball', 'user': 'joe'}, {'$inc': {'score': 5000}})
games.find_one()
>> {'_id': ObjectId('5ac9c55f29561f64220f6f9f'),
'game': 'pinball',
'score': 5050,
'user': 'joe'}
$push
# 選擇 blog 數(shù)據(jù)庫(kù)
blog = test_database.blog
blog.find_one()
>> {'_id': ObjectId('5ac98a0a29561f64220f6f9e'),
'author': {'email': 'joe@example.com', 'name': 'joe schmoe'},
'content': '...',
'title': 'A Blog Post'}
# 添加一項(xiàng)評(píng)論字段 comment
blog.update_one({'title': 'A Blog Post'}, {'$push' : {'comments': {'name': 'joe', 'email': 'joe@example.com', 'content': 'nice post.'}}})
blog.find_one()
>> {'_id': ObjectId('5ac98a0a29561f64220f6f9e'),
'author': {'email': 'joe@example.com', 'name': 'joe schmoe'},
'comments': [{'content': 'nice post.',
'email': 'joe@example.com',
'name': 'joe'}],
'content': '...',
'title': 'A Blog Post'}
# 在添加一條由 bob 發(fā)表的評(píng)論
blog.update_one({'title': 'A Blog Post'}, {'$push' : {'comments': {'name': 'bob', 'email': 'bob@example.com', 'content': 'good post.'}}})
blog.find_one()
>> {'_id': ObjectId('5ac98a0a29561f64220f6f9e'),
'author': {'email': 'joe@example.com', 'name': 'joe schmoe'},
'comments': [{'content': 'nice post.',
'email': 'joe@example.com',
'name': 'joe'},
{'content': 'good post.', 'email': 'bob@example.com', 'name': 'bob'}],
'content': '...',
'title': 'A Blog Post'}
$addToSet
# 為 user 集合中的 joe 文檔添加 emails 字段
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
user.update_one({'name': 'joe'},{'$push': {'emails': 'joe@example.com'}})
user.update_one({'name': 'joe'},{'$push': {'emails': 'joe@gmail.com'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@example.com', 'joe@gmail.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 使用 $addToSet 再添加一項(xiàng) joe@example.com 的記錄秀菱,因?yàn)榇嬖谥貜?fù)振诬,數(shù)據(jù)不會(huì)被重復(fù)添加
user.update_one({'name': 'joe'}, {'$addToSet': {'emails': 'joe@example.com'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@example.com', 'joe@gmail.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 如果直接使用 $push ,記錄會(huì)被重復(fù)添加
user.update_one({'name': 'joe'}, {'$push': {'emails': 'joe@example.com'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@example.com', 'joe@gmail.com', 'joe@example.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
$pull
# 刪除 user 集合中 joe 文檔重復(fù)的 emails 值 ($pull 會(huì)刪除所有符合條件的記錄)
user.update_one({'name': 'joe'}, {'$pull': {'emails': 'joe@example.com'}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@gmail.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
$each
# 向 user 集合中 joe 文檔的 emails 字段追加兩個(gè)郵箱地址
user.update_one({'name': 'joe'}, {'$push': {'emails': {'$each': ['joe@example.com', 'joe@outlook.com']}}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@gmail.com', 'joe@example.com', 'joe@outlook.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
$pop
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@gmail.com', 'joe@example.com', 'joe@outlook.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 刪除集合 user 中 joe 文檔 emails 字段的第一個(gè)郵箱地址
user.update_one({'name': 'joe'}, {'$pop': {'emails': -1}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@example.com', 'joe@outlook.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
# 刪除集合 user 中 joe 文檔 emails 字段的最后一個(gè)郵箱地址
user.update_one({'name': 'joe'}, {'$pop': {'emails': 1}})
user.find_one()
>> {'_id': ObjectId('5ac9836829561f64220f6f9d'),
'age': 30,
'emails': ['joe@example.com'],
'location': 'Wisconsin',
'name': 'joe',
'sex': 'male'}
基于位置的數(shù)組修改器
如果文檔中存在數(shù)組記錄衍菱,而且數(shù)組記錄有多個(gè)值赶么,我們可以視同位置修噶器或者定位符 $
來(lái)修改數(shù)組其中的某些值。
# 先清除 blog 集合中的原油數(shù)據(jù)
blog.drop()
# 添加一條 posts 記錄
posts = {'content': '...', 'comments': [{'comment': 'good post', 'author': 'John', 'votes': 0}, {'comment': 'i thought it was too short', 'author': 'Claire', 'votes': 3}, {'comment': 'free watches', 'auth: or': 'Alice', 'votes': -1}]}
blog.insert_one(posts)
blog.find_one()
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'John', 'comment': 'good post', 'votes': 0},
{'author': 'Claire', 'comment': 'i thought it was too short', 'votes': 3},
{'author': 'Alice', 'comment': 'free watches', 'votes': -1}],
'content': '...'}
# 為第一條評(píng)論 comments 的 votes 字段增加 1
post_id = blog.find_one()['_id']
blog.update_one({'_id': post_id}, {'$inc': {'comments.0.votes': 1}})
blog.find_one()
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'John', 'comment': 'good post', 'votes': 1},
{'author': 'Claire', 'comment': 'i thought it was too short', 'votes': 3},
{'author': 'Alice', 'comment': 'free watches', 'votes': -1}],
'content': '...'}
# 定位符的使用脊串,通常在不知道具體數(shù)組位置辫呻,使用定位查詢文檔來(lái)匹配數(shù)組元素
blog.update_one({'comments.author': 'John'}, {'$set': {'comments.$.author': 'Jim'}})
blog.find_one()
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'Jim', 'comment': 'good post', 'votes': 1},
{'author': 'Claire', 'comment': 'i thought it was too short', 'votes': 3},
{'author': 'Alice', 'comment': 'free watches', 'votes': -1}],
'content': '...'}
PyMongo 查詢相關(guān)操作
接下來(lái),我們繼續(xù)深入了解 Pymongo 的查詢操作琼锋。查詢的常用操作大致可以歸為三部分放闺,分別是查詢的方法,可以使用find_one()
以及 find()
來(lái)進(jìn)行查詢缕坎;然后在實(shí)際運(yùn)用中怖侦,我們可能需要使用 $
條件操作符來(lái)幫助我們定位數(shù)據(jù);最后我們會(huì)了解關(guān)于游標(biāo)的一些常用操作念赶。
PyMongo 的 find_one() 和 find()
為方便演示础钠,我們會(huì)在 test_database
數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè) users
的集合,并向其中添加三條文檔記錄叉谜。
users = test_database.users
joe = {'name': 'joe', 'age': 26}
mike = {'name': 'mike', 'age': 28}
jake = {'name': 'jake', 'age': 26}
# 使用 insert_many() 可以一次添加多個(gè)文檔記錄
users.insert_many([joe, mike, jake])
for data in users.find():
print(data)
{'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
find() 的使用方法
我們使用 find()
方法,如果不傳入任何參數(shù)踩萎,將返回該集合中的所有數(shù)據(jù)的一個(gè)游標(biāo)停局,然后我們可以通過(guò) for
來(lái)遍歷游標(biāo)來(lái)打印查詢結(jié)果。
如果我們需要查找特定的數(shù)據(jù)香府,比如年齡為 28 的用戶董栽,那么我們可以給 find()
方法傳入一個(gè)匹配的規(guī)則。
result = users.find({'age': 28})
# find() 的返回值是一個(gè)查詢游標(biāo)企孩,在 python 中的數(shù)據(jù)類型為一個(gè)迭代器锭碳,我們可以使用 count() 查看查詢結(jié)果數(shù)量
result.count()
>> 1
result.next()
>> {'_id': ObjectId('5acb225729561f64220f6fa2'), 'age': 28, 'name': 'mike'}
我們還可以通過(guò)傳入多個(gè)同查詢條件進(jìn)行查詢。
result = users.find({'age': 26, 'name': 'jake'})
# 以上表達(dá)式多個(gè)條件會(huì)被解釋為 AND 關(guān)系
result.count()
>> 1
result.next()
>> {'_id': ObjectId('5acb225729561f64220f6fa3'), 'age': 26, 'name': 'jake'}
find_one() 的使用方法
上面我們演示了 find()
的用法勿璃,我們接下來(lái)將會(huì)演示 find_one()
的用法擒抛。 find_one()
的用法與 find()
的使用方法差別不大,他們的區(qū)別是使用 find_one()
最多只會(huì)返回一條文檔記錄补疑,而 find()
則返回查詢游標(biāo)歧沪。以下是代碼演示:
user.find_one()
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'age': 26, 'name': 'joe'}
# find_one() 如果沒(méi)有查詢條件,會(huì)返回第一條記錄
type(users.find_one({'name': 'kate'}))
>> NoneType
# 如果傳入查詢條件莲组,沒(méi)有查詢結(jié)果诊胞,則會(huì)返回一個(gè) NoneType
users.find_one({'age': 26})
>> '_id': ObjectId('5acb225729561f64220f6fa1'), 'age': 26, 'name': 'joe'}
# 如果查詢匹配多個(gè)結(jié)果,find_one() 只會(huì)返回第一條匹配記錄
指定返回字段
我們?cè)诓樵兊臅r(shí)候锹杈,可能并不需要文檔中的所有字段撵孤,這是我們可以在查詢條件之后再傳入一個(gè)參數(shù)來(lái)指定返回的字段迈着。
# 不要 _id 字段
users.find_one({}, {'_id': 0})
>> {'age': 26, 'name': 'joe'}
# 只輸出 _id 字段
users.find_one({}, {'_id': 1})
>> {'_id': ObjectId('5acb225729561f64220f6fa1')}
Pymongo 條件查詢操作
比較操作符
在查詢中,我們會(huì)經(jīng)常用到比較字段值得大小來(lái)查詢數(shù)據(jù)邪码,實(shí)現(xiàn)這一功能我們會(huì)用到比較操作符裕菠,Pymongo 常用的比較操作符有以下幾個(gè):
- $lt 小于
- $let 小于等于
- $ge 大于
- $gte 大于等于
- $ne 不等于
下面我們將繼續(xù)使用上面的 users
集合來(lái)演示上面幾個(gè)比較操作符。
# 先遍歷打印 users 集合中的數(shù)據(jù)
for data in users.find():
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
# 查詢大于 26 歲的用戶
for data in user.find({'age': {'$gt': 26}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
# 查詢大于等于 26 歲的用戶
for data in user.find({'age': {'$gte': 26}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
# 查詢小于 28 歲的用戶
for data in user.find({'age': {'$lt': 28}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
# 查詢小于等于 28 歲的用戶
for data in user.find({'age': {'$lte': 28}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
# 查詢不等于 28 歲的用戶
for data in user.find({'name': {'$ne': 'mike'}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
$in 和 $nin 的用法
我們可以使用 $in
和 $nin
操作符來(lái)匹配一個(gè)鍵的多個(gè)值霞扬,具體用法示例如下:
# 匹配 users 集合中 用戶名為 joe 和 mike 的文檔記錄
for data in users.find({'name': {'$in': ['joe', 'mike']}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
# 匹配用戶名不是 mike 的用戶 注意: $in 和 $nin 條件必須是一個(gè)數(shù)組
for data in users.find({'name': {'$nin': ['mike']}}):
print(data)
>> {'_id': ObjectId('5acb225729561f64220f6fa1'), 'name': 'joe', 'age': 26}
{'_id': ObjectId('5acb225729561f64220f6fa3'), 'name': 'jake', 'age': 26}
$or 的用法
如果需要查詢兩個(gè)條件中其中一個(gè)為真的查詢結(jié)果糕韧,可以使用 $or 操作符。具體示例如下:
# 為方便演示喻圃,我們先插入多一條文檔記錄
kate = {'name': 'kate', 'age': 30}
users.insert_one(kate)
for data in users.find({'$or': [{'name': 'mike'}, {'age': 30}]}):
print(data)
{'_id': ObjectId('5acb225729561f64220f6fa2'), 'name': 'mike', 'age': 28}
{'_id': ObjectId('5acb6cfc29561f64220f6fa4'), 'name': 'kate', 'age': 30}
null 值查詢和 $exists 條件判定
在 Python 中萤彩,mongodb 中的 null 值以 None 表示。但在查詢 null 值中斧拍,會(huì)出現(xiàn)比較奇怪的情況雀扶,以下為演示案例:
# 為方便演示,創(chuàng)建一個(gè) c 集合肆汹,并向里面添加 3 條記錄
c = test_database.c
c.insert_many({'y': None}, {'y': 1}, {'y': 2})
for data in c.find():
print(data)
>> {'_id': ObjectId('5acb738029561f64220f6fa5'), 'y': None}
{'_id': ObjectId('5acb741029561f64220f6fa6'), 'y': 1}
{'_id': ObjectId('5acb741029561f64220f6fa7'), 'y': 2}
# 查詢 null 值
for data in c.find({'y': None}):
print(data)
>> {'_id': ObjectId('5acb738029561f64220f6fa5'), 'y': None}
# 查詢一個(gè)不存在的鍵愚墓,查詢條件為 null
for data in c.find({'z': None}):
print(data)
>> {'_id': ObjectId('5acb738029561f64220f6fa5'), 'y': None}
{'_id': ObjectId('5acb741029561f64220f6fa6'), 'y': 1}
{'_id': ObjectId('5acb741029561f64220f6fa7'), 'y': 2}
可以看到,當(dāng)我們查找 {'z': None}
的時(shí)候昂勉,會(huì)把所有不包含這個(gè)條件的文檔都查詢出來(lái)浪册,這樣明顯和我們的意圖不一致,因此我們需要增加一個(gè)限定岗照,具體代碼如下:
for data in c.find({'z': {'$in': [None], '$exists': 1}}):
print(data)
>>
通過(guò)加上 $exists
的限定村象,我們可以看到代碼執(zhí)行完之后并沒(méi)有查詢結(jié)果輸出,符合我們的查詢意圖攒至。
查詢數(shù)組
在實(shí)際使用當(dāng)中厚者,我們還可能會(huì)存在文檔中有數(shù)組形式的字段值,因此我們需要一些特定的操作來(lái)查詢匹配這些數(shù)組迫吐,同樣的库菲,MongoDb 提供了相關(guān)的操作符可以使用,常用的數(shù)組操作符有以下幾個(gè):
- $all 匹配多個(gè)元素?cái)?shù)組
- $size 匹配特定長(zhǎng)度的數(shù)組
- $slice 返回匹配數(shù)組的一個(gè)子集
下面將用代碼演示以上三個(gè)操作符的用法志膀。為方便演示熙宇,我們會(huì)先創(chuàng)建一個(gè) food 的集合用來(lái)存放水果的文檔記錄。
food = test_database.food
food.insert_one({'_id': 1, 'fruit': ['apple', 'banana', 'peach']})
food.insert_one({'_id': 2, 'fruit': ['apple', 'kumquat', 'orange']})
food.insert_one({'_id': 3, 'fruit': ['cherry', 'banana', 'apple']})
for data in food.find():
print(data)
>> {'_id': 1, 'fruit': ['apple', 'banana', 'peach']}
{'_id': 2, 'fruit': ['apple', 'kumquat', 'orange']}
{'_id': 3, 'fruit': ['cherry', 'banana', 'apple']}
$all 的用法
result = food.find({'fruit': {'$all': ['apple', 'banana']}})
for data in result:
print(data)
>> {'_id': 1, 'fruit': ['apple', 'banana', 'peach']}
{'_id': 3, 'fruit': ['cherry', 'banana', 'apple']}
# 也可以使用位置定位匹配
result = food.find({'fruit.1': 'banana'})
for data in result:
print(data)
>> {'_id': 1, 'fruit': ['apple', 'banana', 'peach']}
{'_id': 3, 'fruit': ['cherry', 'banana', 'apple']}
$size 的用法
為方便演示梧却,我們會(huì)向 food 集合中的第二個(gè)文檔添加多一個(gè)水果奇颠。
food.update_one({'_id': 2}, {'$push': {'fruit': 'strawbreey'}})
for data in food.find():
print(data)
>> {'_id': 1, 'fruit': ['apple', 'banana', 'peach']}
{'_id': 2, 'fruit': ['apple', 'kumquat', 'orange', 'strawbreey']}
{'_id': 3, 'fruit': ['cherry', 'banana', 'apple']}
# 查找數(shù)組size為3的結(jié)果
result = food.find({'fruit': {'$size': 3}})
for data in result:
print(data)
>> {'_id': 1, 'fruit': ['apple', 'banana', 'peach']}
{'_id': 3, 'fruit': ['cherry', 'banana', 'apple']}
$slice 的用法
$slice 可以返回某個(gè)鍵匹配的數(shù)組的一個(gè)子集,我們將會(huì)用 blog 集合來(lái)演示使用 $slice 操作符獲取特定數(shù)量的評(píng)論記錄放航。
blog.find_one()
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'Jim', 'comment': 'good post', 'votes': 1},
{'author': 'Claire', 'comment': 'i thought it was too short', 'votes': 3},
{'author': 'Alice', 'comment': 'free watches', 'votes': -1}],
'content': '...'}
# 獲取前兩條評(píng)論記錄
blog.find_one({}, {'comments': {'$slice': 2}})
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'Jim', 'comment': 'good post', 'votes': 1},
{'author': 'Claire', 'comment': 'i thought it was too short', 'votes': 3}],
'content': '...'}
# 獲取最后一條評(píng)論記錄
blog.find_one({}, {'comments': {'$slice': -1}})
>> {'_id': ObjectId('5aca17cf29561f64220f6fa0'),
'comments': [{'author': 'Alice', 'comment': 'free watches', 'votes': -1}],
'content': '...'}
min() 和 max() 的使用
如果我們以某個(gè)區(qū)間值作為查詢條件烈拒,我們可以使用比較操作符來(lái)實(shí)現(xiàn),但是,如果文檔中存在值荆几,以及值組成的數(shù)組時(shí)吓妆,查詢結(jié)果往往與我們的意圖不一致,這是我們就需要用到 $elemMatch 來(lái)匹配非數(shù)組元素吨铸,或者使用 min() 和 max() 方法行拢。
# 我們先清空 c 集合,并加入新的文檔記錄
c.drop()
c.count()
>> 0
c.insert_many([{'x': 5}, {'x': 15}, {'x': 25}, {'x': [5, 25]}])
c.count()
>> 4
for data in c.find():
print(data)
>> {'_id': ObjectId('5acc31c729561f64220f6fa8'), 'x': 5}
{'_id': ObjectId('5acc31c729561f64220f6fa9'), 'x': 15}
{'_id': ObjectId('5acc31c729561f64220f6faa'), 'x': 25}
{'_id': ObjectId('5acc31c729561f64220f6fab'), 'x': [5, 25]}
# 假設(shè)我們需要查詢 [10, 20] 區(qū)間內(nèi)的記錄
result = c.find({'x': {'$gt': 10. '$lt': 20}})
for data in result:
print(data)
>> {'_id': ObjectId('5acc31c729561f64220f6fa9'), 'x': 15}
{'_id': ObjectId('5acc31c729561f64220f6fab'), 'x': [5, 25]}
# 這里看到 [5, 25] 這一條記錄其實(shí)是不符合我們的查詢預(yù)期的
# 我們可以使用 $elemMatch 來(lái)不匹配非數(shù)組元素
result = c.find({'x': {'$elemMatch': {'$gt': 10, '$lt': 20}}})
for data in result:
print(data)
>> // 沒(méi)有輸出結(jié)果
# 通過(guò)添加 $elemMatch 可以剔除 [5, 25] 這一記錄诞吱,但正確的查詢結(jié)果 {'x': 15} 卻不能匹配
# 我們將使用 min() 以及 max() 方法
# 為使用者兩個(gè)方法舟奠,我們需要先給 c 集合的 x 字段建立索引
c.create_index('x')
>> 'x_1'
result = c.find({'x': {'$gt': 10, '$lt': 20}}).min([('x', 10)]).max([('x', 20)])
for data in result:
print(data)
>> {'_id': ObjectId('5acc31c729561f64220f6fa9'), 'x': 15}
關(guān)于 min()
和 max()
兩個(gè)方法,有兩點(diǎn)需要注意:
- 使用這兩個(gè)方法前房维,必須先要為需要查詢的字段建立索引沼瘫,否則會(huì)報(bào)錯(cuò)
- 這兩個(gè)方法和在 mongodb 中的寫(xiě)法有一些不一樣,在 mongodb 中咙俩,同樣操作寫(xiě)作: min({'x': 10}) 但在 Pymongo 中耿戚,應(yīng)寫(xiě)成 min([(‘x’, 10)]) 注意區(qū)別,否則同樣會(huì)報(bào)錯(cuò)阿趁。
$where 查詢
針對(duì)一些比較復(fù)雜的查詢膜蛔,我們可以使用 $where 。然而脖阵,由于 $where
可以在查詢中執(zhí)行任意的 Javascript皂股,因此可能會(huì)產(chǎn)生出一些不安全的操作,因此命黔,在實(shí)際生產(chǎn)環(huán)境中屑墨,竟可能的不用或者禁用 $where
。以下代碼演示 $where
的基本用法:
# 為方便演示纷铣,我們會(huì)再次用到 foo 集合,我們先清空以下這一個(gè)集合
foo.drop()
# 向 foo 集合中添加兩條文檔記錄
foo.insert_one({'apple': 1, 'banana': 6, 'peach': 3})
foo.insert_one({'apple': 8, 'spinach': 4, 'watermelon': 4})
for data in foo:
print(data)
>> {'_id': ObjectId('5acdbf4729561f64220f6fac'), 'apple': 1, 'banana': 6, 'peach': 3}
{'_id': ObjectId('5acdbf6a29561f64220f6fad'), 'apple': 8, 'spinach': 4, 'watermelon': 4}
# 接下來(lái)战转,我們需要查找存在兩個(gè)水果數(shù)量相等的文檔
result = foo.find({'$where':
"""
function() {
for (var current in this) {
for (var other in this) {
if (current != other && this[current] == this[other]){
return true;
}
}
}
return false;
}
"""})
for data in result:
print(data)
>> {'_id': ObjectId('5acdbf6a29561f64220f6fad'), 'apple': 8, 'spinach': 4, 'watermelon': 4}
游標(biāo)
當(dāng)我們進(jìn)行查詢操作的時(shí)候搜立,程序執(zhí)行查詢指令后,返回的不是我們的查詢結(jié)果槐秧,而是一個(gè)游標(biāo)啄踊,在 Pymongo 庫(kù)中,指定 find()
方法后刁标,返回的是一個(gè) pymongo.cursor.Cursor
的對(duì)象颠通,這一個(gè)對(duì)象我們可以簡(jiǎn)單的理解成一個(gè)迭代器,因此我們就可以像上文一樣膀懈,使用 for 循環(huán)來(lái)遍歷所有查詢結(jié)果顿锰。
type(foo.find())
>> pymongo.cursor.Cursor
以下再次演示使用 for 遍歷輸出查詢結(jié)果,為方便演示,我們?cè)俅吻蹇?foo 集合硼控。
foo.drop()
# 我們創(chuàng)建一系列文檔
import random
for i in range(20):
foo.insert_one({'x': random.randint(0, 20)})
for data in foo.find():
print(data)
>> {'_id': ObjectId('5acdc47d29561f64220f6fc2'), 'x': 15}
{'_id': ObjectId('5acdc47d29561f64220f6fc3'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc4'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fc5'), 'x': 10}
{'_id': ObjectId('5acdc47d29561f64220f6fc6'), 'x': 7}
{'_id': ObjectId('5acdc47d29561f64220f6fc7'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc8'), 'x': 8}
{'_id': ObjectId('5acdc47d29561f64220f6fc9'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fca'), 'x': 13}
{'_id': ObjectId('5acdc47d29561f64220f6fcb'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fcc'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fcd'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fce'), 'x': 14}
{'_id': ObjectId('5acdc47d29561f64220f6fcf'), 'x': 18}
{'_id': ObjectId('5acdc47d29561f64220f6fd0'), 'x': 5}
{'_id': ObjectId('5acdc47d29561f64220f6fd1'), 'x': 1}
{'_id': ObjectId('5acdc47d29561f64220f6fd2'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fd3'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fd4'), 'x': 4}
{'_id': ObjectId('5acdc47d29561f64220f6fd5'), 'x': 5}
sort() 排序的用法
我們?cè)诨谏厦娴募蠑?shù)據(jù)之下刘陶,對(duì) x
進(jìn)行一個(gè)排序操作,可以使用 sort() 方法牢撼,具體操作如下:
import pymongo
result = foo.find()
# 升序
result.sort([('x', pymongo.ASCENDING)])
for data in result:
print(data)
>> {'_id': ObjectId('5acdc47d29561f64220f6fc4'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fd2'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fd1'), 'x': 1}
{'_id': ObjectId('5acdc47d29561f64220f6fcc'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fcd'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fd4'), 'x': 4}
{'_id': ObjectId('5acdc47d29561f64220f6fd0'), 'x': 5}
{'_id': ObjectId('5acdc47d29561f64220f6fd5'), 'x': 5}
{'_id': ObjectId('5acdc47d29561f64220f6fcb'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fd3'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fc6'), 'x': 7}
{'_id': ObjectId('5acdc47d29561f64220f6fc8'), 'x': 8}
{'_id': ObjectId('5acdc47d29561f64220f6fc5'), 'x': 10}
{'_id': ObjectId('5acdc47d29561f64220f6fca'), 'x': 13}
{'_id': ObjectId('5acdc47d29561f64220f6fce'), 'x': 14}
{'_id': ObjectId('5acdc47d29561f64220f6fc2'), 'x': 15}
{'_id': ObjectId('5acdc47d29561f64220f6fc3'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc7'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc9'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fcf'), 'x': 18}
# 降序
for data in foo.find().sort([('x', pymongo.DESCENDING)]):
print(data)
>> {'_id': ObjectId('5acdc47d29561f64220f6fcf'), 'x': 18}
{'_id': ObjectId('5acdc47d29561f64220f6fc3'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc7'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc9'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc2'), 'x': 15}
{'_id': ObjectId('5acdc47d29561f64220f6fce'), 'x': 14}
{'_id': ObjectId('5acdc47d29561f64220f6fca'), 'x': 13}
{'_id': ObjectId('5acdc47d29561f64220f6fc5'), 'x': 10}
{'_id': ObjectId('5acdc47d29561f64220f6fc8'), 'x': 8}
{'_id': ObjectId('5acdc47d29561f64220f6fc6'), 'x': 7}
{'_id': ObjectId('5acdc47d29561f64220f6fcb'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fd3'), 'x': 6}
{'_id': ObjectId('5acdc47d29561f64220f6fd0'), 'x': 5}
{'_id': ObjectId('5acdc47d29561f64220f6fd5'), 'x': 5}
{'_id': ObjectId('5acdc47d29561f64220f6fd4'), 'x': 4}
{'_id': ObjectId('5acdc47d29561f64220f6fcc'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fcd'), 'x': 2}
{'_id': ObjectId('5acdc47d29561f64220f6fd1'), 'x': 1}
{'_id': ObjectId('5acdc47d29561f64220f6fc4'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fd2'), 'x': 0}
limit() 的使用
上面我們的 foo 文檔中有 20 條文檔記錄匙隔,假如我們不需要一次獲取所有文檔,我們可以使用 limit() 方法來(lái)限制查詢結(jié)果數(shù)量熏版,具體操作如下:
for data in foo.find().limit(5):
print(data)
>> {'_id': ObjectId('5acdc47d29561f64220f6fc2'), 'x': 15}
{'_id': ObjectId('5acdc47d29561f64220f6fc3'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc4'), 'x': 0}
{'_id': ObjectId('5acdc47d29561f64220f6fc5'), 'x': 10}
{'_id': ObjectId('5acdc47d29561f64220f6fc6'), 'x': 7}
skip() 的使用
我們可以使用 skip() 來(lái)跳過(guò)一定數(shù)量的文檔纷责,以下為代碼演示:
for data in foo.find().skip(5).limit(5):
print(data)
>> {'_id': ObjectId('5acdc47d29561f64220f6fc7'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fc8'), 'x': 8}
{'_id': ObjectId('5acdc47d29561f64220f6fc9'), 'x': 17}
{'_id': ObjectId('5acdc47d29561f64220f6fca'), 'x': 13}
{'_id': ObjectId('5acdc47d29561f64220f6fcb'), 'x': 6}
# 我們可以看到,輸出的結(jié)果和上面使用 limit(5) 的數(shù)據(jù)不一樣撼短,這里是跳過(guò)前 5 條記錄的后 5條記錄
小結(jié)
通過(guò)以上的講解以及代碼演示再膳,我們已經(jīng)了解基本的 Pymongo 操作,我們可以自己完成一些小的項(xiàng)目阔加,將以上的技能融會(huì)貫通的使用吸收饵史,以便日后在開(kāi)發(fā)中更得心應(yīng)手。