PyMongo 庫(kù)使用基礎(chǔ)使用速成教程

mongodb symbol.jpeg

為什么要使用 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

pymongo check installed.jpeg

安裝完成后,可以使用 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ù)

我們先引入 pymongoMongoClient 模塊颜阐。再創(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_oneinsert_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)手。

參考資料

《Mongo權(quán)威指南》
MongoDB 官方文檔
Pymongo 官方文檔

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胜榔,一起剝皮案震驚了整個(gè)濱河市胳喷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夭织,老刑警劉巖吭露,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尊惰,居然都是意外死亡讲竿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)弄屡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)题禀,“玉大人,你說(shuō)我怎么就攤上這事膀捷÷踵冢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵全庸,是天一觀的道長(zhǎng)秀仲。 經(jīng)常有香客問(wèn)我,道長(zhǎng)壶笼,這世上最難降的妖魔是什么神僵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮覆劈,結(jié)果婚禮上保礼,老公的妹妹穿的比我還像新娘沛励。我一直安慰自己,他們只是感情好氓英,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布侯勉。 她就那樣靜靜地躺著,像睡著了一般铝阐。 火紅的嫁衣襯著肌膚如雪址貌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天徘键,我揣著相機(jī)與錄音练对,去河邊找鬼。 笑死吹害,一個(gè)胖子當(dāng)著我的面吹牛螟凭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播它呀,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼螺男,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了纵穿?” 一聲冷哼從身側(cè)響起下隧,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谓媒,沒(méi)想到半個(gè)月后淆院,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡句惯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年土辩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抢野。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拷淘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出指孤,到底是詐尸還是另有隱情辕棚,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布邓厕,位于F島的核電站,受9級(jí)特大地震影響扁瓢,放射性物質(zhì)發(fā)生泄漏详恼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一引几、第九天 我趴在偏房一處隱蔽的房頂上張望昧互。 院中可真熱鬧挽铁,春花似錦、人聲如沸敞掘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)玖雁。三九已至更扁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赫冬,已是汗流浹背浓镜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劲厌,地道東北人膛薛。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像补鼻,于是被迫代替她去往敵國(guó)和親哄啄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容