couchDB視圖

視圖函數
javascript

map函數

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函數

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']

視圖實現語言

javascript實現

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; }"
       }
   }
}

erlang實現

編輯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."
       }
   }
}

python實現

安裝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。
相關文章
相關標籤/搜索