Pymongo Tutorial & Pymongo入門教

鴨脖發表於2015-05-24
本教程是pymongo和Mongo的一個簡單介紹,基於pymongo2.7.2的tutorial。看完後應該對Pymongo對Mongo的基本操作有認識了。

教程

這教程是pymongo和Mongo的一個簡單介紹。看完後應該對Pymongo對Mongo的基本操作認識了。

前置條件

開始之前,安裝PyMongo和Mongo。確保在python互動介面執行import不報錯:

?
1
>>> import pymongo

你需要有一個已經在執行的MongoDB例項。如果你已經下載安裝了,可以這樣啟動:

?
1
$ mongod

通過MongoClient建立一個連線。

開始使用PyMongo的第一步是建立一個MongoClient,對應於MongoDB例項。操作起來so easy:

?
1
2
>>> from pymongo import MongoClient
>>> client = MongoClient()

上面程式碼將會連線預設的host和port。當然也可指定:

?
1
>>> client = MongoClient('localhost'27017)

或者用 MongoDB URI 格式:

?
1
>>> client = MongoClient('mongodb://localhost:27017/')

獲取一個資料庫

一個MongoDB例項可以支援多個獨立的資料庫。使用PyMongo時,可以通過訪問MongoClient的屬性的方式來訪問資料庫:

?
1
>>> db = client.test_database

如果資料庫名字使屬性訪問方式不能用(類似test-database),也可以通過訪問字典值的方式:

?
1
>>> db = client['test-database']

獲取一個Collection

一個collection指一組存在MongoDB中的檔案,大致可以認為是關係型資料庫中表的概念。獲取Collection方法與獲取資料庫方法一致:

?
1
>>> collection = db.test_collection

或字典方式:

?
1
>>> collection = db['test-collection']

需要強調的一點是,MongoDB裡的collection和資料庫都是惰性建立的 - 之前我們提到的所有命令實際沒有對MongoDB server進行任何操作。直到第一個檔案插入後,才會建立。

檔案(Documents)

資料在MongoDb中是以JSON類檔案的形式儲存起來的。在PyMongo中用字典來代表檔案。例如,下面這個字典就可以代表一篇博文:

?
1
2
3
4
5
>>> import datetime
>>> post = {"author""Mike",
...         "text""My first blog post!",
...         "tags": ["mongodb""python""pymongo"],
...         "date": datetime.datetime.utcnow()}

注意,檔案裡可以儲存python原生型別(datetime.datetime),這些型別的值會被自動在原生型別和BSON格式之間轉換。

檔案插入操作

要把一個檔案插入collection,可以使用insert()方法:

?
1
2
3
4
>>> posts = db.posts
>>> post_id = posts.insert(post)
>>> post_id
ObjectId('...')

檔案被插入之後,如果檔案內沒有_id這個鍵值,那麼系統自動新增一個到檔案裡。這是一個特殊鍵值,它的值在整個collection裡是唯一的。insert()返回這個檔案的_id值。對這個值的更多內容,可以參考:the documentation on _id

插入第一個檔案後,這個posts collection 就真實的在server上建立了。可以通過檢視資料庫上的所有collection來驗證:

?
1
2
>>> db.collection_names()
[u'system.indexes', u'posts']

這個名為 system.indexes的 collection是個特殊的內部collection,這是自動建立的。

單個檔案獲取 find_one()

MongoDB中最基本的查詢就是find_one。這個函式返回一個符合查詢的檔案,或者在沒有匹配的時候返回None。這在你知道只有一個檔案符合條件的時候,或者只對第一個符合條件的檔案感興趣的時候,很有用。下面用 find_one() 來獲取 posts collection裡的第一個檔案:

?
1
2
>>> posts.find_one()
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}

返回結果是一個我們之前插入的符合條件的字典型別值。 
注意,返回的檔案裡已經有了_id這個鍵值,這是自動新增的。 
find_one()還支援對特定元素進行匹配的查詢。篩選出作者是“Mike”的檔案:

?
1
2
>>> posts.find_one({"author""Mike"})
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}

如果換個作者名,像 “Eliot”,就查不到結果:

?
1
2
>>> posts.find_one({"author""Eliot"})
>>>

按照ObjectId查詢

通過_id也可以進行查詢, 在例子中就是ObjectId:

?
1
2
3
4
>>> post_id
ObjectId(...)
>>> posts.find_one({"_id": post_id})
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}

注意 ObjectId 並不等價於他的字串形式。

?
1
2
3
>>> post_id_as_str = str(post_id)
>>> posts.find_one({"_id": post_id_as_str}) # No result
>>>

web應用的一個常見任務就是在requset的URL裡獲取ObjectId然後找到與之匹配的檔案。在本例中,必須要先從字串轉換為ObjectId然後傳給find_one:

?
1
2
3
4
5
6
from bson.objectid import ObjectId
 
# 框架從URL裡獲取post_id,然後把他作為字串傳入
def get(post_id):
    # 從字串轉換為ObjectId:
    document = client.db.collection.find_one({'_id': ObjectId(post_id)})

與這個話題相關的文章:When I query for a document by ObjectId in my web application I get no result

關於Unicode字串的一點說明

你可能已經注意到,之前存入資料庫的事常規的Python字串,這與我們從資料庫伺服器裡取回來的看起來不同(比如 u’Mike’ 而不是‘Mike’)。下面簡單解釋一下。

MongoDB 以格式儲存資料. BSON 字串都是 UTF-8編碼的, 所以PyMongo必須確保它儲存的字串值包含有效地 UTF-8資料.常規字串 ( )都是有效的,可以不改變直接儲存。Unicode 字串( )就需要先編碼成 UTF-8 格式.例子裡的字串顯示為u’Mike’ 而不是 ‘Mike’是因為 PyMongo 會把每個BSON 字串轉換成 Python 的unicode 字串, 而不是常規的 str.

更多關於Python unicode字串的內容,參考這裡.

批量插入

為了讓查詢更有趣點,我們多插入幾個檔案。除了單個檔案插入,也可以通過給insert()方法傳入可迭代的物件作為第一個引數,進行批量插入操作。這將會把迭代表中的每個檔案插入,而且只向server傳送一條命令:

?
1
2
3
4
5
6
7
8
9
10
>>> new_posts = [{"author""Mike",
...               "text""Another post!",
...               "tags": ["bulk""insert"],
...               "date": datetime.datetime(200911121114)},
...              {"author""Eliot",
...               "title""MongoDB is fun",
...               "text""and pretty easy too!",
...               "date": datetime.datetime(200911101045)}]
>>> posts.insert(new_posts)
[ObjectId('...'), ObjectId('...')]

這個例子裡有一些比較有趣的地方:

insert()現在返回兩個ObjectId例項,每個代表一個插入的檔案。

new_posts[1]與其他的posts內容格式不相同:裡面沒有"tags”,另外我們增加了一個新的“title”域。這就是MongoDB所提到的無schema特點。

查詢多個檔案

想獲取多個檔案的時候,可以使用find()方法。find()返回一個 Cursor 例項,通過它我們可以便利每個符合查詢條件的檔案。比如便利每個 posts collection裡的檔案:

?
1
2
3
4
5
6
>>> for post in posts.find():
...   post
...
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}
{u'date': datetime.datetime(200911121114), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}
{u'date': datetime.datetime(200911101045), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'}

與使用find_one()時候相同,可以傳入一個檔案來限制查詢結果。比如查詢所有作者是 “Mike”的文章:

?
1
2
3
4
5
>>> for post in posts.find({"author""Mike"}):
...   post
...
{u'date': datetime.datetime(...), u'text': u'My first blog post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'mongodb', u'python', u'pymongo']}
{u'date': datetime.datetime(200911121114), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}

Counting

如果只想知道符合查詢條件的檔案有多少,可以用count()操作,而不必進行完整的查詢。查詢collection的檔案總數:

?
1
2
>>> posts.count()
3

或者只是特定的一些檔案:

?
1
2
>>> posts.find({"author""Mike"}).count()
2

限定範圍的查詢

MongoDB 支援多種高階查詢。例如,查詢晚於某個特定時間的post,結果按作者名排序:

?
1
2
3
4
5
6
>>> d = datetime.datetime(2009111212)
>>> for post in posts.find({"date": {"$lt": d}}).sort("author"):
...   print post
...
{u'date': datetime.datetime(200911101045), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'}
{u'date': datetime.datetime(200911121114), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}

這裡使用了特殊的”$lt"操作符來進行範圍查詢,並呼叫sort()對結果按照作者排序。

索引(Indexing)

為了讓上述查詢更快一點,可以新增一個在"date” 和 “author"上新增複合索引。首先,使用explain()方法來了解查詢在沒有新增索引情況下如何執行:

?
1
2
3
4
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["cursor"]
u'BasicCursor'
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["nscanned"]
3

可以看道,查詢使用的是BasicCursor,而且掃描了全部的三個檔案。現在新增一個複合索引,再看看同樣的操作:

?
1
2
3
4
5
6
7
>>> from pymongo import ASCENDING, DESCENDING
>>> posts.create_index([("date", DESCENDING), ("author", ASCENDING)])
u'date_-1_author_1'
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["cursor"]
u'BtreeCursor date_-1_author_1'
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["nscanned"]
2

現在查詢使用的是BtreeCursor(利用這個索引),並且只掃描了兩個符合條件的檔案。

關於索引可以檢視MongoDB的文件indexs.


來自:http://my.oschina.net/zanyang1103/blog/337142

相關文章