使用 Couchdb-python 操作 CouchDB
簡介
Couchdb-python 是目前最常用的操作 CouchDB 的第三方 Python 庫,其提供了幾乎所有 CouchDB RESTful 接口的功能,主要包括三個模塊:
- Client 模塊:提供與 CouchDB server 的交互,對數據庫的基本操作如增刪改查,操作 temporary view 等功能包括在該模塊中
- View 模塊:為用戶提供操作 CouchDB 中預定義視圖的接口
- Mapping 模塊:將 Python 對象與 CouchDB 的 JSON 文檔映射在一起,在進行面向對象編程時十分有用
本文將重點介紹前兩個模塊中的重要方法的使用。
Couchdb-python 下載地址 。
安裝方法同普通的第三方 Python 庫,安裝后只需使用 import couchdb 語句即可導入全部 couchdb-python 中的所有模塊。
回頁首
Client.server 中提供的方法
該模塊提供了一系列獲取 CouchDB server 信息的方法,通過這些方法用戶可以獲取關于 server 的各種狀態信息。但在使用這些方法之前,首先要獲取 server 實體,方法如下:
server = couchdb.Server('http://yourhost:5984/')
CouchDB 默認使用 5984 端口,我們可根據實際情況填入不同參數,如需獲取 admin 賬戶權限,可使用帶有用戶名密碼的 host 地址:
server=couchdb.Server('http://couchdb:passw0rd@10.120.138.211:5984/')
通過以上兩種方法獲取到 server 實體后,即可使用其他方法獲得 server 相關信息。
表格 1 中描述了 server 中提供的常用方法:
表 1.server 中提供的方法
方法名 | 用途 | 參數 | 樣例 |
---|---|---|---|
create(name) | 創建一個名為 name 的數據庫 | name-數據庫名 | db = server.create(‘test’) |
server[name] | 獲取名為 name 的現存數據庫 | name-數據庫名 | db= server[‘test] |
delete(name) | 刪除名為 name 的數據庫 | name-數據庫名 | server.delete(‘test’) |
version() | 獲取 CouchDB server 版本號 | None | myversion=server.version() |
tasks() | 獲取正在運行的任務狀態,如數據壓縮、數據庫復制等 | None | taskJson=server.tasks() |
stats(name) | 返回 server 的相關統計數據 | Name-統計數據名, 主要有 couchdb 和 httpd 兩種 None 將返回所有 Couchdb 下的統計參數有 7 種: auth_cache_hits auth_cache_misses database_reads database_writes open_databases open_os_files request_time httpd 下的統計參數有 5 種: bulk_requests clients_requesting_changes requests temporary_view_reads view_reads |
statsJson=server.stats('couchdb/open_os_files') statsJson=server.stats('httpd/ view_reads') |
Replicate(source,target) | 從源數據庫復制數據到目標數據庫,源數據庫和目的數據必須已經存在 | Source-源數據庫地址 Target-目的數據庫地址 |
server.replicate("http://localhost:5984/testdb", "http://coucdb-remote:5984/recipes") |
通過表格中所列的 db=server[name] 或 db=create(name)獲取數據庫實體 db 后,即可使用其他方法對數據庫中的各種數據進行操作, 這里我們創建一個名為”test”的數據庫。
回頁首
使用 update(documents) 批量插入和更新數據
盡管我們可以通過創建一個 dict 類型的數據并使用 db.save(dict) 方法將一條記錄插入數據庫,但實際的項目中使用 update(documents) 方法可以更加高效地插入、更新一條或多條數據,因為 update(documents) 方法將多條記錄的數據包裹在一個 request 請求中一次性地發送給 CouchDB server。
update(documents) 的參數及返回值說明如下:
參數:documents--包含一組 dict 對象的數組,dict 對象即為要插入或更新的數據。
返回值:三元組列表 (success, docid, rev_or_exc)--列表元素依次對應 documents 中的元素,success 為布爾型,表示是否更新成功,docid 表示對應的文檔“_id”,rev_or_exc 表示新紀錄的版本號“_rev”或更新失敗的異常信息。
這里需要明確一個概念,CouchDB 中“_id”字段用來唯一地標識一條記錄,“_rev”字段用來表示一條記錄的更新版本號。任何的數據修改操作(除了 delete)都將在原數據的基礎上 append 一條新數據,并遞增原數據的“_rev”字段。也就是說,CouchDB 中不存在數據覆蓋,舊數據仍然保存在數據庫中,并可通過之前的“_rev”值找到,這也是 CouchDB 本身的一個特性。如圖 1 所示:
圖 1. 未經任何修改的一條新記錄

我們在 test 數據庫中建立了一條記錄,除“_id”和”_rev”外,此記錄還有 age,company,hometown 和 name 字段。此時該記錄未經任何修改,其“_rev”字段的首位為 1。將其 hometwon 字段修改后, 此時“_rev”字段的首位遞增為 2,但之前“_rev”字段首位為 1 的記錄仍可通過點擊 futon 中記錄左下角的“previous version”按鈕找到,如圖 2 所示:
圖 2. 記錄更新后“_rev”字段遞增

基于 CouchDB 的這種特性,當我們想要通過 update(documents) 批量插入數據時,只需將要插入的數據 dict 加入 documents 數組,如若未指定“_rev”和“_id”字段,系統將自動產生;當需要批量更新數據時,不論更新哪個字段,除賦予該字段新值外,都必須完整地在 dict 中加入該記錄所有其他字段(包括“_rev”和“_id”),否則記錄被更新后將丟失未列出的字段。至于如何一次性地將包含“_rev”和“_id”字段的整條記錄取出為一個 dict, 將在介紹 view 時提到。插入和更新的示例代碼如下:
插入三條記錄:
docs = [ dict(name='Mary',age='20',hometown='Shenzhen',company='NEC' ), dict(name='Leo',age='45',hometown='Wuhan',company='MS' ), dict(name='Kata',age='22',hometown='Chengdu',company='IBM' ) ] resultList=db.update(docs) updateNum=0 for item in resultList: if(item[0]): updateNum+=1 else: log.info('%s db[%s]' %(item[2],item[1])) log.info('%s update successfully\n' %updateNum)
循環遍歷 update() 的返回值 list 是為了記錄日志,明確地知道是否數據全部插入成功,如若失敗,是什么原因導致了哪些記錄插入失敗。
如圖 3,通過 futon 可以看到,三條新紀錄產生。
圖 3. 插入 3 條記錄成功,“_rev”值首位均為 1

更新上面三條記錄的 company 為“IBM China”,獲取三條記錄的完整字段,存入一個列表 docs,之后調用 update 方法即可。
代碼如下:
docs = [dict( _id='bd50bad62946f07e202112a04b00d85e', _rev='1-df98f39480c1bfc022130732f8a3469c' name='Mary',age='20',hometown='Shenzhen',company='IBM China' ), dict( _id='bd50bad62946f07e202112a04b00df17', _rev='1-aa008ff2e24dc68a4b696c46fcd08540', name='Leo',age='45',hometown='Wuhan',company='IBM China' ), dict( _id='bd50bad62946f07e202112a04b00eeaa', _rev='1-80b875494e4672b6a8f623ef4ab7ffe8, name='Kata',age='22',hometown='Chengdu',company='IBM China' ) ] resultList=db.update(docs) updateNum=0 for item in resultList: if(item[0]): updateNum+=1 else: log.info('%s db[%s]' %(item[2],item[1])) log.info('%s update successfully\n' %updateNum)
如圖 4,可以看到更新成功,且更新后的“_rev”值首位已經遞增為 2
圖 4. 更新 3 條記錄成功,“_rev”值首位遞增為 2

在實際開發過程中,如若需要批量插入數據,只需將每條數據拼成 Python 的 dict 格式,然后將所有 dict 放進列表并調用一次 update 方法即可;如若要更新數據,需要首先從數據庫中獲取帶有完整字段的記錄,CouchDB 提供了一種功能強大的視圖功能,借助視圖就可以將需要的記錄完整的取出,下面我們介紹視圖相關的方法。
回頁首
使用 query() 和 view() 查詢視圖
CouchDB 中的 view 使得用戶可以靈活快速地查詢數據,實現類似 SQL 中 select 的功能,同時 view 也是 CouchDB 中實現數據 index 的一個過程,通過 JavaScript 語言編寫的的 map function 來實現。view 分為 temporary view 和 predefined view 兩種。
temporary view
由于 temporary view 所定義的 map function 和數據的 index 文件并沒有真正保存在數據庫中,用戶可在程序中即寫即用。因此它常用來快速地驗證 map function 的功能。但正因為如此,每次調用 temporary view 都將對數據臨時建立一次 index,在驗證數據量比較大的數據庫時,temporary view 的查詢時間將會很慢。
test 數據庫中現在有 4 條記錄,其中有兩條記錄的 hometown 字段為“Shenzhen”,我們可以在 Python 中編寫如下的 map function 來構建一個簡單的 temporary view。盡管是在 Python 中,但 map function 的部分仍然需要使用 JavaScript 的語法:
map_fun ='''function(doc){ if(doc.hometown=="Shenzhen"){ emit(doc.age, doc); } }'''
Map function 以 doc 作為唯一的參數,代表數據庫中的一條記錄。函數將查看記錄中的 hometown 字段是否為“Shenzhen”,如果是,將調用內建的 emit(arg1,arg2) 方法。emit() 函數的兩個參數中,第一個為 key,也即 index,可以是單一字段,也可以是多個字段組成的數組,這里我們以 age 字段作為 key;第二個為 value,即將要 emit 出的結果,如果設為 doc,將 emit 整條記錄;如果設為 doc 的某個字段,如 doc.name, 將只 emit 該字段。這里我們將 emit 出整條記錄 doc。
Couchdb-python 中用來執行 temporary view 的方法是 query(), 其參數如下:
query(map_fun, reduce_fun=None, language='javascript', wrapper=None, **options)
map_fun :map 函數名
reduce_fun:reduce 函數名 (可選)
language :函數語言,默認為 JavaScript
wrapper : 一個可選的參數,用來包裹查詢結果,默認為空
options : 可選的查詢參數,如 key=’yourkey’,descending=True
返回類型:List
此時就可以使用 query() 方法來獲取 temporary view 的結果了
for row in db.query(map_fun,descending=True): print row.key print row.value print row
這里我們遍歷 temporary view 的結果并依次打印出 key,value 和整條記錄。options 參數使用 descending =True 將結果進行降序排序。
打印結果如下:
點擊查看代碼清單
關閉 [x]
25 {u'name': u'Johnson', u'hometown': u'Shenzhen', u'age': u'25', u'_id': u'bd50bad62946f07e202112a04b00bb52', u'company': u'IBM', u'_rev': u'5-6b8e641ab94b58c7782461f2d657de3d'} <Row id=u'bd50bad62946f07e202112a04b00bb52', key=u'25', value={u'name': u'Johnson', u'hometown': u'Shenzhen', u'age': u'25', u'_id': u'bd50bad62946f07e202112a04b00bb52', u'company': u'IBM', u'_rev': u'5-6b8e641ab94b58c7782461f2d657de3d'}> 20 {u'name': u'Mary', u'hometown': u'Shenzhen', u'age': u'20', u'_id': u'bd50bad62946f07e202112a04b00d85e', u'company': u'IBM China', u'_rev': u'2-6f8727ba21418735f944663667b2421c'} <Row id=u'bd50bad62946f07e202112a04b00d85e', key=u'20', value={u'name': u'Mary', u'hometown': u'Shenzhen', u'age': u'20', u'_id': u'bd50bad62946f07e202112a04b00d85e', u'company': u'IBM China', u'_rev': u'2-6f8727ba21418735f944663667b2421c'}>
Predefined view
Predefined view 需要用戶先在數據庫中建立對應的 design doc,map function 作為 design doc 的一部分寫在 design doc 中。design doc 的結構如下:
design_doc = {'_id': '_design/yourDisgnName, 'views': { 'yourViewName1': { 'map': 'function(doc){emit(doc.age,doc);' }, 'yourViewName2': { 'map': 'function(doc){emit(doc.hometown,doc);' } ... } }
每個 design doc 必須包括‘_id’和‘view’字段,且‘_id’字段的值必須為‘_design/yourDesignName’的格式,‘view’字段中可定義多個 map function, 每個 map function 擁有自己的名字,map function 的具體定義寫在 map 字段的值中。這里我們定義了兩個 map function, 分別在 age 和 hometown 字段建立了 index,emit 出所有的記錄。之后只需使用 db.save(design_doc)方法即可將寫好的 design doc 保存在數據庫中。如圖 5 所示。
圖 5.design document 樣例

Couchdb-python 中用來執行 predefined view 的方法是 view(),
其參數如下:
view(name, wrapper=None, **options)
map_fun – design_docid/viewname
wrapper – 一個可選的參數,用來包裹查詢結果,默認為空
options – 可選的查詢參數,如 key=’yourkey’,descending=True 等
返回類型:List
使用 view() 來查詢 hometown 為”Shenzhen”和”Wuhan”的記錄:
results=db.view('yourDisgnName/yourViewName2',keys=['Shenzhen','Wuhan']) for row in results: dic=row.value print dic
這里的 options 參數我們使用了 keys=[key1,key2,...],在 CouchDB HTTP API 中列舉了所有其他可選的查詢參數,讀者可靈活選擇。
回頁首
使用 changes() 監聽數據庫更新
changes() 方法即是對 CouchDB API 中_changes 的封裝,它提供了一種監聽數據庫中數據變更的操作,并以時間順序返回變更信息列表,可以用于構建類似消息通知和推送的服務。其常用方法參數列表如下:
- doc_ids (array) – 記錄的 ID 列表,用于監聽指定記錄的改變。
- conflicts (boolean) –在返回值中包含沖突信息,默認值為 false。
- descending (boolean) – 以時間降序順序返回數據變更結果,默認為 false。
- feed (string) –默認為 normal,這里我們設置為 continuous。
- include_docs (boolean) –在返回結果中包含每條記錄的內容,默認為 false。
- limit (number) – 指定返回結果的數量。
- view (string) – 允許使用視圖作為搜索條件。
ch = db.changes(feed='continuous', include_docs=True) counter=0 for each in ch: counter+=1 if (counter > 20): print each T=datetime.datetime.now print T counter=0
除非人為終止程序的運行,否則 feed=’continuous’將一直保持一個與 CouchDB 的連接(CouchDB 的并發機制可以很容易地在不影響效率的前提下支持這種長連接),只要有數據更新(包括刪除)都將返回給 ch 列表,include_docs=True 將返回已更新的數據本身。Counter 計數器的作用是,在數據庫中的更新操作大于 20 時返回被更新的記錄。
回頁首
總結:
通過本文的介紹相信讀者可以在實際的開發工作中快速地編寫 Python 腳本操作 CouchDB,希望文中提到的概念和示例代碼對讀者的開發工作提供一定的幫助。
來自: http://www.ibm.com/developerworks/cn/opensource/os-cn-couchdb-python/index.html?ca=drs-