>先看[《MongoDB權威指南》](http://book.douban.com/subject/6068947/) >本教程是[官方教程](http://api.mongodb.org/python/current/tutorial.html)的非專業翻譯 教程 --- 本教程的目的是介紹__MongoDB__和__PyMongo__ 准備工作 --- 在我們開始之前,首先確保已經安裝__PyMongo__模塊,然后嘗試在python shell中運行以下語句,沒有出現異常,說明__pymongo__已經可以正常工作了: [shell] import pymongo 本教程還假設__MongoDB__實例已經正常運行在默認端口上。如果您已經下載並安裝了__MongoDB__,可以像這樣啟動它: $ mongod 建立鏈接 --- 第一步就是使用__PyMongo__創建一個鏈接,用來鏈接到__mongod__實例: [shell] from pymongo import Connection [shell] connection = Connection() 上面的代碼將會鏈接到默認的主機與端口。我們也可以指定主機和端口: [shell] connection = Connection('localhost', 27017) 獲取數據庫 --- 一個__MongoDB__實例可以支持多個獨立的數據庫。在__PyMongo__中,你可以使用屬性風格來使用__connection__獲得一個數據庫: [shell] db = connection.test_database 如果是因為數據庫名稱或其他什么原因不能使用屬性風格來訪問的話,可以使用字典風格來訪問這個數據庫: [shell] db = connection['test-database'] 獲取集合 --- 集合(Collection)是存放在__MongoDB__數據庫中的一組文檔,相當與關系型數據庫中的表。獲取一個集合與獲取一個數據庫的方法大致相同: [shell] collection = db.test_collection 或者(使用字典風格): [shell] collection = db['test-collection'] 需要注意的是,上述的語句中的集合與數據庫在MongoDB中都是延遲創建的,當執行其他操作時才會被真正的創建。集合與數據庫將會在有第一個文檔插入后真正創建。 文檔 --- MongoDB中使用JSON風格的__BSON__文檔(document)來表示數據。在__PyMongo__中,我們使用字典(dic)來表示一個文檔(document)。例如,下面的字典就i表示一篇博客文章: [shell] import datetime [shell] post = {"author": "Mike", ... "text": "My first blog post!", ... "tags": ["mongodb", "python", "pymongo"], ... "date": datetime.datetime.utcnow()} 需要注意的是,上述代碼中,要提交的文檔包含了Python類型的數據(`datetime.datetime`類型),它會被自動轉換為適當的__BSON__類型。 插入文檔 --- 我們可以使用`insert()`方法來將文檔插入到一個集合中: [shell] posts = db.posts [shell] posts.insert(post) ObjectId('...') 在插入文檔時,如果沒有顯式指明`_id`鍵,__MongoDB__會自動為`_id`鍵產生一個`ObjectId`類型的值;如果指明`_id`鍵,那么請確保它的值在集合(collection)中是唯一。`insert()`方法會在執行后返回文檔的`_id`值。 在插入第一個文檔的過程中,上述代碼中的*posts*集合實際上已經被創建了,我們可以列出數據庫中所有的集合來驗證一下前面提到過的延遲創建: [shell] db.collection_names() [u'posts', u'system.indexes'] *__注意:__* *system.indexes*集合是一個自動創建的特殊內部集合 使用`find_one()`獲取文檔 --- 在__MongoDB__中最常用的查詢就是`find_one()`。此方法返回一個匹配條件的文檔(如果沒有參數則不進行匹配)。在知道只有一個匹配文檔或者只對第一個文檔感興趣的情況下,這個方法非常有用。現在我們從*post*集合獲取地一個文檔: [shell] 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()`同時支持根據特定條件的查詢。為了限制結果,我們現在只查詢作者*author*為*Mike*的文檔: [shell] 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*,我們不會得到結果的,因為集合中唯一的一個文檔不滿足條件: [shell] posts.find_one({"author": "Eliot"}) Unicode字符串 --- 你可能會注意到返回結果的字符串與__Python__中默認的字符串有些不同(比如用`u'Mike`來代替`'Mike'`)。這里簡短說明一下。 MongoDB以__BSON__格式存儲數據,而__BSON__字符串使用的是UTF-8編碼,所以__PyMongo__必須確保它存儲的字符串為UTF-8格式。普通字符串(`str`)的存儲不變,unicode字符串會被__PyMongo__自動轉為UTF-8格式。 批量插入 --- 為了使查詢更有趣,讓我們插入幾個文檔。除了插入單個文檔,我們也可以通過傳入一個可迭代的參數(`list`)批量插入多個文檔。這樣只使用一條命令將每次迭代的文檔插入數據庫: [shell] 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)}] [shell] posts.insert(new_posts) [ObjectId('...'), ObjectId('...')] 有一下幾點比較有趣的事情需要注意: 1. `insert()`的返回值包含了兩個`ObjectId`對象,每個都對應上面批量插入的文檔 2. *new_posts[1]*與其他的posts看起來不一樣:沒有`tags`,並且增加了一個新的`title`。這里也證明了為什么我們一直說__MongoDB__是沒有模式的 查詢多個文檔 --- 要獲得多個文檔結果,我們使用`find()`方法來查詢。`find()`返回一個`Cursor`(游標)對象,它可以讓我們遍歷所有匹配的文檔。例如,我們可以遍歷所有*posts*集合的文檔: [shell] 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()`也可以像`find_one()`那樣來進行條件查詢。現在,我們來查詢所有作者*author*為*Mike*的文檔: [shell] 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']} 計數 --- 如果我們只是單純的想知道有多少文件符合條件,我們可以執行`count()`,而不用進行一次完整的查詢。我們可以得到一個集合中所有文檔的總數: [shell] posts.count() 3 或者只統計符合條件的: [shell] posts.find({"author": "Mike"}).count() 2 范圍查詢 --- __MongoDB__支持許多不同類型的高級查詢。例如,我們只查詢符合某一特定日期提交的文檔,別且結果按作者*author*排序: [shell] d = datetime.datetime(2009, 11, 12, 12) [shell] 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()`方法來將結果以作者排序。 索引 --- 為了使上面的查詢速度快,我們可以在*date*和*author*上添加一個復合索引。首先,我們使用`explain`工具來獲取查詢在沒有使用索引情況下的一些信息: [shell] posts.find({"date": {"$lt": d}}).sort("author").explain()["cursor"] u'BasicCursor' [shell] posts.find({"date": {"$lt": d}}).sort("author").explain()["nscanned"] 3 我們可以看到,當前查詢使用*BasicCurosr*游標,說明沒有使用索引;*nscanned*說明數據庫查找了3個文檔。現在讓我們加上一個復合索引再看看: [shell] from pymongo import ASCENDING, DESCENDING [shell] posts.create_index([("date", DESCENDING), ("author", ASCENDING)]) u'date_-1_author_1' [shell] posts.find({"date": {"$lt": d}}).sort("author").explain()["cursor"] u'BtreeCursor date_-1_author_1' [shell] posts.find({"date": {"$lt": d}}).sort("author").explain()["nscanned"] 2 現在的查詢使用了*BtreeCursor*游標,說明使用了索引,並且索引存儲在B樹結構中;*nscanned*說明數據庫只查找了2個符合條件的文檔