视图函数
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。