python---mongodb
https://github.com/qiwsir/StarterLearningPython/blob/master/232.md
mongodb資料庫(1)
MongoDB開始火了,這是時代發展的需要。為此,本教程也要涉及到如何用python來操作mongodb。考慮到讀者對這種資料庫可能比mysql之類的更陌生,所以,要用多一點的篇幅稍作介紹,當然,更完備的內容還是要去閱讀專業的mongodb書籍。
mongodb是屬於NoSql的。
NoSql,全稱是 Not Only Sql,指的是非關係型的資料庫。它是為了大規模web應用而生的,其特徵諸如模式自由、支援簡易複製、簡單的API、大容量資料等等。
MongoDB是其一,選擇它,主要是因為我喜歡,否則我不會列入我的教程。數說它的特點,可能是:
- 面向文件儲存
- 對任何屬性可索引
- 複製和高可用性
- 自動分片
- 豐富的查詢
- 快速就地更新
也許還能列出更多,基於它的特點,擅長領域就在於:
- 大資料(太時髦了!以下可以都不看,就要用它了。)
- 內容管理和交付
- 移動和社交基礎設施
- 使用者資料管理
- 資料平臺
安裝mongodb
先演示在ubuntu系統中的安裝過程:
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list sudo apt-get update sudo apt-get install mongodb-10gen
啟動mongodb
安裝完畢,就可以啟動資料庫。因為本教程不是專門講資料庫,所以,這裡不設計資料庫的詳細講解,請讀者參考有關資料。下面只是建立一個簡單的庫,並且說明mongodb的基本要點,目的在於為後面用python來操作它做個鋪墊。
執行mongo
啟動shell,顯示的也是>
,有點類似mysql的狀態。在shell中,可以實現與資料庫的互動操作。
在shell中,有一個全域性變數db,使用哪個資料庫,那個資料庫就會被複制給這個全域性變數db,如果那個資料庫不存在,就會新建。
> use mydb
switched to db mydb
> db
mydb
除非向這個資料庫中增加實質性的內容,否則它是看不到的。
> show dbs;
local 0.03125GB
向這個資料庫增加點東西。mongodb的基本單元是文件,所謂文件,就類似與python中的字典,以鍵值對的方式儲存資料。
> book = {"title":"from beginner to master", "author":"qiwsir", "lang":"python"}
{
"title" : "from beginner to master",
"author" : "qiwsir",
"lang" : "python"
}
> db.books.insert(book)
> db.books.find()
{ "_id" : ObjectId("554f0e3cf579bc0767db9edf"), "title" : "from beginner to master", "author" : "qiwsir", "lang" : "python" }
db指向了資料庫mydb,books是這個資料庫裡面的一個集合(類似mysql裡面的表),向集合books裡面插入了一個文件(文件對應mysql裡面的記錄)。“資料庫、集合、文件”構成了mongodb資料庫。
從上面操作,還發現一個有意思的地方,並沒有類似create之類的命令,用到資料庫,就通過use xxx
,如果不存在就建立;用到集合,就通過db.xxx
來使用,如果沒有就建立。可以總結為“隨用隨取隨建立”。是不是簡單的有點出人意料。
> show dbs
local 0.03125GB
mydb 0.0625GB
當有了充實內容之後,也看到剛才用到的資料庫mydb了。
在mongodb的shell中,可以對資料進行“增刪改查”等操作。但是,我們的目的是用python來操作,所以,還是把力氣放在後面用。
安裝pymongo
要用python來驅動mongodb,必須要安裝驅動模組,即pymongo,這跟操作mysql類似。安裝方法,我最推薦如下:
$ sudo pip install pymongo
如果順利,就會看到最後的提示:
Successfully installed pymongo
Cleaning up...
如果不選擇版本,安裝的應該是最新版本的,我在本教程測試的時候,安裝的是:
>>> import pymongo
>>> pymongo.version
'3.0.1'
這個版本在後面給我挖了一個坑。如果讀者要指定版本,比如安裝2.8版本的,可以:
$ sudo pip install pymongo==2.8
如果用這個版本,我後面遇到的坑能夠避免。
安裝好之後,進入到python的互動模式裡面:
>>> import pymongo
說明模組沒有問題。
連線mongodb
既然python驅動mongdb的模組pymongo業已安裝完畢,接下來就是連線,也就是建立連線物件。
>>> pymongo.Connection("localhost",27017)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'Connection'
報錯!我在去年做的專案中,就是這樣做的,並且網上檢視很多教程都是這麼連線。
所以,讀者如果用的是舊版本的pymongo,比如2.8,仍然可以使用上面的連線方法,如果是像我一樣,是用的新的(我安裝時沒有選版本),就得注意這個問題了。
經驗主義害死人。必須看看下面有哪些方法可以用:
>>> dir(pymongo)
['ALL', 'ASCENDING', 'CursorType', 'DESCENDING', 'DeleteMany', 'DeleteOne', 'GEO2D', 'GEOHAYSTACK', 'GEOSPHERE', 'HASHED', 'IndexModel', 'InsertOne', 'MAX_SUPPORTED_WIRE_VERSION', 'MIN_SUPPORTED_WIRE_VERSION', 'MongoClient', 'MongoReplicaSetClient', 'OFF', 'ReadPreference', 'ReplaceOne', 'ReturnDocument', 'SLOW_ONLY', 'TEXT', 'UpdateMany', 'UpdateOne', 'WriteConcern', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '_cmessage', 'auth', 'bulk', 'client_options', 'collection', 'command_cursor', 'common', 'cursor', 'cursor_manager', 'database', 'errors', 'get_version_string', 'has_c', 'helpers', 'ismaster', 'message', 'mongo_client', 'mongo_replica_set_client', 'monitor', 'monotonic', 'network', 'operations', 'periodic_executor', 'pool', 'read_preferences', 'response', 'results', 'server', 'server_description', 'server_selectors', 'server_type', 'settings', 'son_manipulator', 'ssl_context', 'ssl_support', 'thread_util', 'topology', 'topology_description', 'uri_parser', 'version', 'version_tuple', 'write_concern']
瞪大我的那雙渾濁迷茫佈滿血絲渴望驚喜的眼睛,透過近視鏡的玻璃片,怎麼也找不到Connection()這個方法。原來,剛剛安裝的pymongo變了,“他變了”。
不過,我發現了它:MongoClient()
>>> client = pymongo.MongoClient("localhost", 27017)
很好。python已經和mongodb建立了連線。
剛才已經建立了一個數據庫mydb,並且在這個庫裡面有一個集合books,於是:
>>> db = client.mydb
或者
>>> db = client['mydb']
獲得資料庫mydb,並賦值給變數db(這個變數不是mongodb的shell中的那個db,此處的db就是python中一個尋常的變數)。
>>> db.collection_names()
[u'system.indexes', u'books']
檢視集合,發現了我們已經建立好的那個books,於是在獲取這個集合,並賦值給一個變數books:
>>> books = db["books"]
或者
>>> books = db.books
接下來,就可以操作這個集合中的具體內容了。
編輯
剛剛的books所引用的是一個mongodb的集合物件,它就跟前面學習過的其它物件一樣,有一些方法供我們來驅使。
>>> type(books)
<class 'pymongo.collection.Collection'>
>>> dir(books)
['_BaseObject__codec_options', '_BaseObject__read_preference', '_BaseObject__write_concern', '_Collection__create', '_Collection__create_index', '_Collection__database', '_Collection__find_and_modify', '_Collection__full_name', '_Collection__name', '__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattr__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_command', '_count', '_delete', '_insert', '_socket_for_primary_reads', '_socket_for_reads', '_socket_for_writes', '_update', 'aggregate', 'bulk_write', 'codec_options', 'count', 'create_index', 'create_indexes', 'database', 'delete_many', 'delete_one', 'distinct', 'drop', 'drop_index', 'drop_indexes', 'ensure_index', 'find', 'find_and_modify', 'find_one', 'find_one_and_delete', 'find_one_and_replace', 'find_one_and_update', 'full_name', 'group', 'index_information', 'initialize_ordered_bulk_op', 'initialize_unordered_bulk_op', 'inline_map_reduce', 'insert', 'insert_many', 'insert_one', 'list_indexes', 'map_reduce', 'name', 'next', 'options', 'parallel_scan', 'read_preference', 'reindex', 'remove', 'rename', 'replace_one', 'save', 'update', 'update_many', 'update_one', 'with_options', 'write_concern']
這麼多方法不會一一介紹,只是按照“增刪改查”的常用功能,介紹幾種。讀者可以使用help()去檢視每一種方法的使用說明。
>>> books.find_one()
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
提醒讀者注意的是,如果你熟悉了mongodb的shell中的命令,跟pymongo中的方法稍有差別,比如剛才這個,在mongodb的shell中是這樣子的:
> db.books.findOne()
{
"_id" : ObjectId("554f0e3cf579bc0767db9edf"),
"title" : "from beginner to master",
"author" : "qiwsir",
"lang" : "python"
}
請注意區分。
目前在集合books中,有一個文件,還想再增加,於是插入一條:
新增和查詢
>>> b2 = {"title":"physics", "author":"Newton", "lang":"english"}
>>> books.insert(b2)
ObjectId('554f28f465db941152e6df8b')
成功地向集合中增加了一個文件。得看看結果(我們就是充滿好奇心的小孩子,我記得女兒小時候,每個給她照相,拍了一張,她總要看一看。現在我們似乎也是這樣,如果不看看,總覺得不放心),看看就是一種查詢。
>>> books.find().count()
2
這是檢視當前集合有多少個文件的方式,返回值為2,則說明有兩條文件了。還是要看看內容。
>>> books.find_one()
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
這個命令就不行了,因為它只返回第一條。必須要:
>>> for i in books.find():
... print i
...
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
{u'lang': u'english', u'title': u'physics', u'_id': ObjectId('554f28f465db941152e6df8b'), u'author': u'Newton'}
在books引用的物件中有find()方法,它返回的是一個可迭代物件,包含著集合中所有的文件。
由於文件是鍵值對,也不一定每條文件都要結構一樣,比如,也可以插入這樣的文件進入集合。
>>> books.insert({"name":"Hertz"})
ObjectId('554f2b4565db941152e6df8c')
>>> for i in books.find():
... print i
...
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
{u'lang': u'english', u'title': u'physics', u'_id': ObjectId('554f28f465db941152e6df8b'), u'author': u'Newton'}
{u'_id': ObjectId('554f2b4565db941152e6df8c'), u'name': u'Hertz'}
如果有多個文件,想一下子插入到集合中(在mysql中,可以實現多條資料用一條命令插入到表裡面,還記得嗎?忘了看上一節),可以這麼做:
>>> n1 = {"title":"java", "name":"Bush"}
>>> n2 = {"title":"fortran", "name":"John Warner Backus"}
>>> n3 = {"title":"lisp", "name":"John McCarthy"}
>>> n = [n1, n2, n3]
>>> n
[{'name': 'Bush', 'title': 'java'}, {'name': 'John Warner Backus', 'title': 'fortran'}, {'name': 'John McCarthy', 'title': 'lisp'}]
>>> books.insert(n)
[ObjectId('554f30be65db941152e6df8d'), ObjectId('554f30be65db941152e6df8e'), ObjectId('554f30be65db941152e6df8f')]
這樣就完成了所謂的批量插入,檢視一下文件條數:
>>> books.find().count()
6
但是,要提醒讀者,批量插入的文件大小是有限制的,網上有人說不要超過20萬條,有人說不要超過16MB,我沒有測試過。在一般情況下,或許達不到上線,如果遇到極端情況,就請讀者在使用時多注意了。
如果要查詢,除了通過迴圈之外,能不能按照某個條件查呢?比如查詢'name'='Bush'
的文件:
>>> books.find_one({"name":"Bush"})
{u'_id': ObjectId('554f30be65db941152e6df8d'), u'name': u'Bush', u'title': u'java'}
對於查詢結果,還可以進行排序:
>>> for i in books.find().sort("title", pymongo.ASCENDING):
... print i
...
{u'_id': ObjectId('554f2b4565db941152e6df8c'), u'name': u'Hertz'}
{u'_id': ObjectId('554f30be65db941152e6df8e'), u'name': u'John Warner Backus', u'title': u'fortran'}
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
{u'_id': ObjectId('554f30be65db941152e6df8d'), u'name': u'Bush', u'title': u'java'}
{u'_id': ObjectId('554f30be65db941152e6df8f'), u'name': u'John McCarthy', u'title': u'lisp'}
{u'lang': u'english', u'title': u'physics', u'_id': ObjectId('554f28f465db941152e6df8b'), u'author': u'Newton'}
這是按照"title"的值的升序排列的,注意sort()中的第二個引數,意思是升序排列。如果按照降序,就需要將引數修改為pymongo.DESCEDING
,也可以指定多個排序鍵。
>>> for i in books.find().sort([("name",pymongo.ASCENDING),("name",pymongo.DESCENDING)]):
... print i
...
{u'_id': ObjectId('554f30be65db941152e6df8e'), u'name': u'John Warner Backus', u'title': u'fortran'}
{u'_id': ObjectId('554f30be65db941152e6df8f'), u'name': u'John McCarthy', u'title': u'lisp'}
{u'_id': ObjectId('554f2b4565db941152e6df8c'), u'name': u'Hertz'}
{u'_id': ObjectId('554f30be65db941152e6df8d'), u'name': u'Bush', u'title': u'java'}
{u'lang': u'python', u'_id': ObjectId('554f0e3cf579bc0767db9edf'), u'author': u'qiwsir', u'title': u'from beginner to master'}
{u'lang': u'english', u'title': u'physics', u'_id': ObjectId('554f28f465db941152e6df8b'), u'author': u'Newton'}
讀者如果看到這裡,請務必注意一個事情,那就是mongodb中的每個文件,本質上都是“鍵值對”的類字典結構。這種結構,一經python讀出來,就可以用字典中的各種方法來操作。與此類似的還有一個名為json的東西,可以閱讀本教程第貳季進階的第陸章模組中的《標準庫(8)。但是,如果用python讀過來之後,無法直接用json模組中的json.dumps()方法操作文件。其中一種解決方法就是將文件中的'_id'
鍵值對刪除(例如:del
doc['_id']
),然後使用json.dumps()即可。讀者也可是使用json_util模組,因為它是“Tools for using Python’s json module with BSON documents”,請閱讀http://api.mongodb.org/python/current/api/bson/json_util.html中的模組使用說明。
更新
對於已有資料,進行更新,是資料庫中常用的操作。比如,要更新name為Hertz那個文件:
>>> books.update({"name":"Hertz"}, {"$set": {"title":"new physics", "author":"Hertz"}})
{u'updatedExisting': True, u'connectionId': 4, u'ok': 1.0, u'err': None, u'n': 1}
>>> books.find_one({"author":"Hertz"})
{u'title': u'new physics', u'_id': ObjectId('554f2b4565db941152e6df8c'), u'name': u'Hertz', u'author': u'Hertz'}
在更新的時候,用了一個$set
修改器,它可以用來指定鍵值,如果鍵不存在,就會建立。
關於修改器,不僅僅是這一個,還有別的呢。
修改器 | 描述 |
---|---|
$set | 用來指定一個鍵的值。如果不存在則建立它 |
$unset | 完全刪除某個鍵 |
$inc | 增加已有鍵的值,不存在則建立(只能用於增加整數、長整數、雙精度浮點數) |
$push | 陣列修改器只能操作值為陣列,存在key在值末尾增加一個元素,不存在則建立一個數組 |
刪除
刪除可以用remove()方法:
>>> books.remove({"name":"Bush"})
{u'connectionId': 4, u'ok': 1.0, u'err': None, u'n': 1}
>>> books.find_one({"name":"Bush"})
>>>
這是將那個文件全部刪除。當然,也可以根據mongodb的語法規則,寫個條件,按照條件刪除。
索引
索引的目的是為了讓查詢速度更快,當然,在具體的專案開發中,要視情況而定是否建立索引。因為建立索引也是有代價的。
>>> books.create_index([("title", pymongo.DESCENDING),])
u'title_-1'
我這裡僅僅是對pymongo模組做了一個非常簡單的介紹,在實際使用過程中,上面知識是很有限的,所以需要讀者根據具體應用場景再結合mongodb的有關知識去嘗試新的語句。