Pymongo Tutorial & Pymongo入門教
本教程是pymongo和Mongo的一個簡單介紹,基于pymongo2.7.2的tutorial。看完后應該對Pymongo對Mongo的基本操作有認識了。
教程
這教程是pymongo和Mongo的一個簡單介紹。看完后應該對Pymongo對Mongo的基本操作認識了。
前置條件
開始之前,安裝PyMongo和Mongo。確保在python交互界面執行import不報錯:
>>> import pymongo
你需要有一個已經在運行的MongoDB實例。如果你已經下載安裝了,可以這樣啟動:
$ mongod
通過MongoClient建立一個連接。
開始使用PyMongo的第一步是創建一個MongoClient,對應于MongoDB實例。操作起來so easy:
>>> from pymongo import MongoClient >>> client = MongoClient()
上面代碼將會連接默認的host和port。當然也可指定:
>>> client = MongoClient('localhost', 27017) 或者用 MongoDB URI 格式:
>>> client = MongoClient('mongodb://localhost:27017/') 獲取一個數據庫
一個MongoDB實例可以支持多個獨立的數據庫。使用PyMongo時,可以通過訪問MongoClient的屬性的方式來訪問數據庫:
>>> db = client.test_database
如果數據庫名字使屬性訪問方式不能用(類似test-database),也可以通過訪問字典值的方式:
>>> db = client['test-database']
獲取一個Collection
一個collection指一組存在MongoDB中的文件,大致可以認為是關系型數據庫中表的概念。獲取Collection方法與獲取數據庫方法一致:
>>> collection = db.test_collection
或字典方式:
>>> collection = db['test-collection']
需要強調的一點是,MongoDB里的collection和數據庫都是惰性創建的 - 之前我們提到的所有命令實際沒有對MongoDB server進行任何操作。直到第一個文件插入后,才會創建。
文件(Documents)
數據在MongoDb中是以JSON類文件的形式保存起來的。在PyMongo中用字典來代表文件。例如,下面這個字典就可以代表一篇博文:
>>> import datetime
>>> post = {"author": "Mike",
... "text": "My first blog post!",
... "tags": ["mongodb", "python", "pymongo"],
... "date": datetime.datetime.utcnow()} 注意,文件里可以保存python原生類型(datetime.datetime),這些類型的值會被自動在原生類型和BSON格式之間轉換。
文件插入操作
要把一個文件插入collection,可以使用insert()方法:
>>> posts = db.posts
>>> post_id = posts.insert(post)
>>> post_id
ObjectId('...') 文件被插入之后,如果文件內沒有_id這個鍵值,那么系統自動添加一個到文件里。這是一個特殊鍵值,它的值在整個collection里是唯一的。insert()返回這個文件的_id值。對這個值的更多內容,可以參考:the documentation on _id。
插入第一個文件后,這個posts collection 就真實的在server上創建了。可以通過查看數據庫上的所有collection來驗證:
>>> db.collection_names() [u'system.indexes', u'posts']
這個名為 system.indexes的 collection是個特殊的內部collection,這是自動創建的。
單個文件獲取 find_one()
MongoDB中最基本的查詢就是find_one。這個函數返回一個符合查詢的文件,或者在沒有匹配的時候返回None。這在你知道只有一個文件符合條件的時候,或者只對第一個符合條件的文件感興趣的時候,很有用。下面用 find_one() 來獲取 posts collection里的第一個文件:
>>> 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”的文件:
>>> 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”,就查不到結果:
>>> posts.find_one({"author": "Eliot"})
>>> 按照ObjectId查詢
通過_id也可以進行查詢, 在例子中就是ObjectId:
>>> 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 并不等價于他的字符串形式。
>>> post_id_as_str = str(post_id)
>>> posts.find_one({"_id": post_id_as_str}) # No result
>>> web應用的一個常見任務就是在requset的URL里獲取ObjectId然后找到與之匹配的文件。在本例中,必須要先從字符串轉換為ObjectId然后傳給find_one:
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發送一條命令:
>>> new_posts = [{"author": "Mike",
... "text": "Another post!",
... "tags": ["bulk", "insert"],
... "date": datetime.datetime(2009, 11, 12, 11, 14)},
... {"author": "Eliot",
... "title": "MongoDB is fun",
... "text": "and pretty easy too!",
... "date": datetime.datetime(2009, 11, 10, 10, 45)}]
>>> posts.insert(new_posts)
[ObjectId('...'), ObjectId('...')] 這個例子里有一些比較有趣的地方:
insert()現在返回兩個ObjectId實例,每個代表一個插入的文件。
new_posts[1]與其他的posts內容格式不相同:里面沒有"tags”,另外我們增加了一個新的“title”域。這就是MongoDB所提到的無schema特點。
查詢多個文件
想獲取多個文件的時候,可以使用find()方法。find()返回一個 Cursor 實例,通過它我們可以便利每個符合查詢條件的文件。比如便利每個 posts collection里的文件:
>>> 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(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']}
{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'} 與使用find_one()時候相同,可以傳入一個文件來限制查詢結果。比如查詢所有作者是 “Mike”的文章:
>>> 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(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']} Counting
如果只想知道符合查詢條件的文件有多少,可以用count()操作,而不必進行完整的查詢。查詢collection的文件總數:
>>> posts.count() 3
或者只是特定的一些文件:
>>> posts.find({"author": "Mike"}).count()
2 限定范圍的查詢
MongoDB 支持多種高級查詢。例如,查詢晚于某個特定時間的post,結果按作者名排序:
>>> d = datetime.datetime(2009, 11, 12, 12)
>>> for post in posts.find({"date": {"$lt": d}}).sort("author"):
... print post
...
{u'date': datetime.datetime(2009, 11, 10, 10, 45), u'text': u'and pretty easy too!', u'_id': ObjectId('...'), u'author': u'Eliot', u'title': u'MongoDB is fun'}
{u'date': datetime.datetime(2009, 11, 12, 11, 14), u'text': u'Another post!', u'_id': ObjectId('...'), u'author': u'Mike', u'tags': [u'bulk', u'insert']} 這里使用了特殊的”$lt"操作符來進行范圍查詢,并調用sort()對結果按照作者排序。
索引(Indexing)
為了讓上述查詢更快一點,可以添加一個在"date” 和 “author"上添加復合索引。首先,使用explain()方法來了解查詢在沒有添加索引情況下如何執行:
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["cursor"]
u'BasicCursor'
>>> posts.find({"date": {"$lt": d}}).sort("author").explain()["nscanned"]
3 可以看道,查詢使用的是BasicCursor,而且掃描了全部的三個文件。現在添加一個復合索引,再看看同樣的操作:
>>> 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