視圖函數
javascript
Map方法的參數只有一個,就是當前的文檔對象。Map方法的實現須要根據文檔對象的內容,肯定是否要輸出結果。 若是須要輸出的話,能夠經過emit來完成。 emit方法有兩個參數,分別是key和value,分別表示輸出結果的鍵和值。 使用什麼樣的鍵和值應該根據視圖的實際須要來肯定。 emit函數能夠在map函數裏被調用屢次,建立一個文檔的多個記錄。
當但願對文檔的某個字段進行排序和過濾操做的時候,應該把該字段做爲鍵(key)或是鍵的一部分; value的值能夠提供給 Reduce 方法使用,也可能會出如今最終的結果中。 能夠做爲鍵的不只是簡單數據類型,也能夠是任意的 JSON 對象。好比emit([doc.title, doc.price], doc)中,使用數組做爲鍵。java
map函數示例(javascript代碼):python
function(doc) { emit(doc._id, doc); }
Reduce方法的參數有三個:key、values和rereduce,分別表示鍵、值和是不是rereduce 。 因爲rereduce狀況的存在,Reduce 方法通常須要處理兩種狀況:git
傳入的參數rereduce的值爲falsegithub
這代表Reduce方法的輸入是 Map方法輸出的中間結果。數據庫
參數key的值是一個數組,對應於中間結果中的每條記錄。 該數組的每一個元素都是一個包含兩個元素的數組,第一個元素是在Map方法中經過emit輸出的鍵(key),第二個元素是記錄所在的文檔 ID 。數組
參數values的值是一個數組,對應於 Map 方法中經過emit輸出的值(value)。服務器
傳入的參數rereduce的值爲truecurl
這代表Reduce方法的輸入是上次Reduce方法的輸出。函數
參數key的值爲null。
參數values的值是一個數組,對應於上次Reduce方法的輸出結果。
reduce函數示例(javascript代碼):
function (key, values, rereduce) { return sum(values); }
一、建立數據庫testdb2,添加以下文檔 :
{ "_id": "ef3c0dddfd988a9fa5dd77452a46a5e6", "phoneNumber": "1001", "billSeconds": 180, "timestamp": "201408251705" } { "_id": "ef3c0dddfd988a9fa5dd77452a482dd0", "phoneNumber": "1001", "billSeconds": 100, "timestamp": "201408251715" }
上述是分機1001的兩條CDR,記錄了兩次通話的billSeconds,若是要計算通話時長,須要將 phoneNumber做爲key,billSeconds做爲value進行map和reduce操做。
二、用map操做過濾數據
function(doc) { emit(doc.phoneNumber, doc.billSeconds); }
map函數以phoneNumber參數做爲key,以billSeconds做爲value對數據庫執行過濾操做。
三、用reduce計算結果
function (key, values, rereduce) { return sum(values); }
reduce函數執行聚合操做,將key相同的value進行求和。
完整view建立代碼以下:
{ "_id": "_design/jsTest", "language": "javascript", "views": { "all": { "map": "function(doc) { emit(doc.phoneNumber, doc.billSeconds); }", "reduce": "function (key, values, rereduce) { return sum(values); }" } } }
單獨執行map的結果:
curl http://127.0.0.1:5984/testdb2/_design/jsTest/_view/all?reduce=false {"total_rows":2,"offset":0,"rows":[ {"id":"ef3c0dddfd988a9fa5dd77452a46a5e6","key":"1001","value":180}, {"id":"ef3c0dddfd988a9fa5dd77452a482dd0","key":"1001","value":100} ]}
map-reduce結果:
curl http://127.0.0.1:5984/testdb2/_design/jsTest/_view/all?group=true {"rows":[ {"key":"1001","value":280} ]}
python示例:
import couchdb server = couchdb.Server("http://192.168.131.121:5984") db = server.create('python-tests') db['johndoe'] = dict(type='Person', name='John Doe') db['maryjane'] = dict(type='Person', name='Mary Jane') db['gotham'] = dict(type='City', name='Gotham City') map_fun = '''function(doc) { if (doc.type == 'Person') emit(doc.name, null); }''' for row in db.query(map_fun): print(row.key) del server['python-tests']
BigCouch不支持臨時視圖
python示例:
import couchdb server = couchdb.Server("http://192.168.131.121:5984") db = server.create('python-tests') db['johndoe'] = dict(type='Person', name='John Doe') db['maryjane'] = dict(type='Person', name='Mary Jane') db['gotham'] = dict(type='City', name='Gotham City') viewData = { "getdata":{ "map":"function(doc){ if (doc.type == 'Person') emit(doc.name, null);}" } } db['_design/example'] = dict(language='javascript', views=viewData) for row in db.view('example/getdata'): print(row.key) del server['python-tests']
couchDB默認的查詢語言是javascript,不須要進行配置便可使用js建立視圖。
示例代碼:
{ "_id": "_design/jsTest", "language": "javascript", "views": { "all": { "map": "function(doc) { emit(doc._id, doc); }", "reduce": "function (key, values, rereduce) { return values.length; }" } } }
編輯local.ini文件,添加以下配置:
[native_query_servers] erlang = {couch_native_process, start_link, []}
配置後須要重啓couchDB服務器。
示例代碼:
{ "_id": "_design/erlangTest", "language": "erlang", "views": { "all": { "map": "%% Map Function\nfun({Doc}) ->\n V = proplists:get_value(<<\"_id\">>, Doc, null),\n Emit(V,{Doc})\nend.\n\n", "reduce": "%% Reduce Function\nfun(Keys, Values, ReReduce) -> length(Values) end." } } }
安裝couchdb-python包:
pip install pycouchdb or pip install -i http://simple.crate.io/ pycouchdb or git clone git://github.com/niwibe/py-couchdb.git cd py-couchdb python setup.py install
編輯local.ini文件,添加以下配置:
[query_servers] python=/usr/bin/couchpy
配置後須要重啓couchDB服務器。
示例代碼:
{ "_id": "_design/pythonTest", "language": "python", "views": { "all": { "map": "def fun(doc):\n yield doc['_id'],doc\n", "reduce": "def fun(key, values, rereduce):\n return len(values)\n" } } }
運行視圖的可選參數
key 限定結果中只包含鍵爲該參數值的記錄。 startkey 限定結果中只包含鍵大於或等於該參數值的記錄。 endkey 限定結果中只包含鍵小於或等於該參數值的記錄。 limit 限定結果中包含的記錄的數目。 descending 指定結果中記錄是否按照降序排列。 skip 指定結果中須要跳過的記錄數目。 group 指定是否對鍵進行分組。 reduce 指定reduce=false能夠只返回 Map 方法的運行結果。
示例數據:
{ "_id": "ef3c0dddfd988a9fa5dd77452a46a5e6", "phoneNumber": "1001", "billSeconds": 180, "timestamp": "201408251705" } { "_id": "ef3c0dddfd988a9fa5dd77452a482dd0", "phoneNumber": "1001", "billSeconds": 100, "timestamp": "201408251715" } { "_id": "ef3c0dddfd988a9fa5dd77452a63a9e3", "phoneNumber": "1002", "billSeconds": 180, "timestamp": "201408251735" } { "_id": "5fecc0d7fe5acac6b46359b5eec4f3ff", "phoneNumber": "1003", "billSeconds": 190, "timestamp": "201408261035" } { "_id": "_design/jsTest", "language": "javascript", "views": { "all": { "map": "function(doc) { emit(doc.phoneNumber, doc.billSeconds); }", "reduce": "function (key, values, rereduce) {return sum(values); }" } } }
語法:
/database/_design/designdocname/_view/viewname?key="${key}"
示例:
curl "http://192.168.131.121:5984/testdb2/_design/jsTest/_view/all?group=true&key=\"1001\"" {"rows":[ {"key":"1001","value":280} ]}
語法:
/database/_design/designdocname/_view/viewname?startkey="${startkey}"&endkey="${endkey}"
示例:
curl "http://192.168.131.121:5984/testdb2/_design/jsTest/_view/all?group=true&startkey=\"1001\"&endkey=\"1002\"" {"rows":[ {"key":"1001","value":280}, {"key":"1002","value":180} ]} curl "http://192.168.131.121:5984/testdb2/_design/jsTest/_view/all?group=true&startkey=\"1001\"" {"rows":[ {"key":"1001","value":280}, {"key":"1002","value":180}, {"key":"1003","value":190} ]} curl "http://192.168.131.121:5984/testdb2/_design/jsTest/_view/all?group=true&endkey=\"1002\"" {"rows":[ {"key":"1001","value":280}, {"key":"1002","value":180} ]} 通常的關係數據庫只要數據的結構是正確的,就容許運行任何的查詢操做,一樣的,CouchDB使用事先定義好的Map和Reduce函數以Map/Reduce的方式來進行查詢。這些函數爲用戶提供了極大的靈活性,因爲每個文檔均可以單獨、並行地進行計算,因此它們能夠適應不一樣的文檔結構和索引[2]。CouchDB將一個Map函數和一個Reduce函數結合在一塊兒稱之爲視圖。 CouchDB中的視圖是用來得到開發者或當前程序想要的數據集的方式,並且是一種靜態的數據查詢方式[3]。視圖頗有用,在篩選數據庫中的文檔,爲某個特殊的程序找到相關的文檔;按照必定的順序從數據庫的文檔中提取數據;按照某種須要的值爲文檔創建相應的索引等狀況下均可以使用。總之,CouchDB中的視圖所作的事情與關係數據庫中的SQL查詢所作的事情有許多相似之處,是開發應用程序,瀏覽數據庫中的文檔必不可少的工具。 在CouchDB中通常將視圖稱爲MapReduce視圖,一個MapReduce視圖由兩個JavaScript函數組成,一個是Map函數,這個函數必須定義;另外一個是Reduce函數,這個是可選的,根據程序的須要能夠進行選擇性定義[3]。 詳細介紹CouchDB的Map和Reduce執行過程以前,先了解一下Futon爲咱們提供的臨時視圖(Temporary View)。臨時視圖是Futon中的一個能夠對視圖進行調試的界面(見圖),沒有問題的函數能夠保存在Design文檔中造成固定的視圖。爲了瞭解Map和Reduce的過程,咱們以創建一個數據庫groups爲例來進行講解,如圖所示。 在groups中咱們添加三個文檔做爲示例,文檔的內容分別以下: { "_id": "4c1168fca1d0ad9f69a1267b86000ed5", "_rev": "1-283727dba699785809c18abc7eaedfcc", "名稱": "A小組", "成員": [ "李剛", "劉偉", "趙小云" ], "平均年齡": 23, "所獲榮譽": [ "衛生先進小組", "文體先進小組", "學習先進小組" ] } { "_id": "4c1168fca1d0ad9f69a1267b86001c23", "_rev": "2-42b7064413f6667f3c3cfc77dc0dd0de", "名稱": "B小組", "成員": [ "張力", "劉明" ], "平均年齡": 22.5, "所獲榮譽": [ "學習先進小組", "文體先進小組", "衛生先進小組" ] } { "_id": "4c1168fca1d0ad9f69a1267b86002695", "_rev": "1-2d7bd5cff3806e3227fc9b48fa3b8cc8", "名稱": "C小組", "成員": [ "劉明", "楊麗", "馬曉雯", "朱慧" ], "平均年齡": 21, "所獲榮譽": [ "文體先進小組", "學習先進小組" ] } 在Map的步驟中,輸入的文檔會從原始的結構轉換或者映射爲新的key/value對。如今咱們經過一個Map函數來完成對小組名稱的查詢。在臨時視圖中輸入如下函數: function(doc) { if(doc.名稱) { emit(doc.名稱); } } 運行後獲得如圖11-9所示的結果。 能夠看到咱們查詢到了全部小組的名稱,同時也能夠看到小組的名稱的下面還有該文檔的ID。還能夠查詢出全部小組中的全部人的姓名。 Map函數以下: function(doc) { if(doc.成員) { for(var i in doc.成員) { emit(doc.成員[i]); } } } 該函數的運行結果如圖11-10所示。 單擊圖11-10所示界面左上角的「key」按鈕能夠執行改變成員的姓名稱的排序等操做。 如今來看看Reduce函數的執行過程。Reduce就是將執行Map函數後獲得的key/value對削減爲一個單個的值或一個數據集合,這個過程是可選擇的,而Map函數的執行過程是一個視圖所必需的。前面提到一個Map函數執行的過程會產生包含文檔ID和key以及value的行,Reduce的輸入則是這些由Map獲得的key/value的值,而不是文檔ID。執行Reduce函數事後,將會產生一個單獨的對全部value的化簡結果或一個基於key的全部分組的計算。分組(Grouping)的操做沒法在Reduce函數中控制,它只能經過傳遞到視圖中的參數來控制。 CouchDB自己包含了三個內嵌的Reduce函數,分別是:_count,_sum和_stats,它們的功能分別如表11-2所示。在大多數狀況下,對於咱們開發CouchDB的應用程序來講,它們基本上夠用了。在這三個函數中,_sum和_stats僅僅會對數據的值進行化簡,而_count函數能夠對任何類型的值計數,固然也包括null類型的值。 表11-2 內嵌Reduce函數 函 數 輸 出 _count 返回Map結果集中值的個數 _sum 返回Map結果集中數值的求和結果 _stats 返回Map結果集中數值的統計結果 內嵌的_count函數是開發程序的時候最經常使用的Reduce函數,因爲該函數能夠用來統計任何值的個數,包括null值,因此當Map函數中emit的value值略去不寫的時候,也可使用該函數。下面是一個示例。 Map函數: function(doc) { if(doc.成員) { for(var i in doc.成員) { emit(doc.成員[i]); } } } Reduce函數: _count 程序運行的結果如圖11-11所示。 內嵌函數_sum會返回一個Map函數輸出的值的和,因此該函數要求全部求和的值均爲數值類型。 Map函數: function(doc) { if(doc.成員) { for(var i in doc.成員) { emit(doc.成員[i] ,doc.平均年齡); } } } Reduce函數: _sum 在「Grouping」下拉框中選擇「exact」,則返回每一個人對應的平均年齡。運行的結果如圖11-12所示。 內嵌函數_stats返回一個JSON數據對象,該對象中包含sum、count、min、max、sumsqr的值,分別表示求和、計數、求最小值、求最大值、求和的平方。和_sum同樣,該函數一樣要求全部的參與運算的值均爲數值類型。 Map函數: function(doc) { if(doc.成員) { for(var i in doc.成員) { emit(doc.成員[i] ,doc.平均年齡); } } } Reduce函數: _stats 執行後的結果如圖11-13所示。 在執行Map/Reduce的過程當中,數據庫中的每個文檔爲Map函數的參數,該函數能夠將全部的文檔都忽略,或者經過emit()返回一行或者多行key/value對。除了文檔以外Map函數幾乎不依靠任何信息,正是這種執行的獨立性,使得CouchDB視圖能夠大量、並行地產生。 CouchDB的視圖按照鍵(key)的順序進行排序並以行組(rows)的形式進行存儲,所以即便按照多個鍵值在成百萬、上千萬的數據中進行檢索也會具備很高的執行效率。因此,當編寫Map函數的時候,首要的目標就是創建一個按照類似的鍵值來存儲相關數據的索引。 雖然MapReduce的功能很強大,能夠完成不少的工做,但它也有本身的侷限性。在Map階段產生的索引是一維的,這就意味着在Reduce的階段不能產生大量的數據,不然會大大下降程序的性能。好比,CouchDB的MapReduce並不適於全文本檢索和自組織搜索等應用,這類問題更適合用Lucene這樣的工具來解決。此外,在CouchDB中處理地理信息數據也並非很方便,若是須要處理地理信息數據,則最好使用CouchDB的一個分支系統GeoCouch。