Mapreduce 基于使用Cloudant/CouchDB链式map reduce的聚合进行筛选和排序

Mapreduce 基于使用Cloudant/CouchDB链式map reduce的聚合进行筛选和排序,mapreduce,couchdb,cloudant,Mapreduce,Couchdb,Cloudant,我想筛选一个列表,并根据聚合对其进行排序;一些用SQL表达起来相当简单的东西,但我对使用迭代Map Reduce实现这一点的最佳方法感到困惑。我专门将Cloudant的“dbcopy”添加到CouchDB中,但我认为这种方法可能与其他map/reduce体系结构类似 伪代码SQL可能是这样的: SELECT grouping_field, aggregate(*) FROM data WHERE #{filter} GROUP BY grouping_field ORDER B

我想筛选一个列表,并根据聚合对其进行排序;一些用SQL表达起来相当简单的东西,但我对使用迭代Map Reduce实现这一点的最佳方法感到困惑。我专门将Cloudant的“dbcopy”添加到CouchDB中,但我认为这种方法可能与其他map/reduce体系结构类似

伪代码SQL可能是这样的:

SELECT   grouping_field, aggregate(*)
FROM     data
WHERE    #{filter}
GROUP BY grouping_field
ORDER BY aggregate(*), grouping_field
LIMIT    page_size
{
  "id": "foobar", "mailbox": "INBOX", "date": "2013-03-29",
  "sender": "foo@example.com", "subject": "Foo Bar"
}
筛选器可能正在查找匹配项,也可能在某个范围内搜索;e、 g.('foo','bar')中的
字段
或37和42之间的
字段

作为一个具体的例子,考虑电子邮件的数据集;分组字段可以是“列表id”、“发件人”或“主题”;聚合函数可以是

count(*)
,或
max(date)
min(date)
;筛选器子句可以考虑标志、日期范围或邮箱ID。文档可能看起来是这样的:

SELECT   grouping_field, aggregate(*)
FROM     data
WHERE    #{filter}
GROUP BY grouping_field
ORDER BY aggregate(*), grouping_field
LIMIT    page_size
{
  "id": "foobar", "mailbox": "INBOX", "date": "2013-03-29",
  "sender": "foo@example.com", "subject": "Foo Bar"
}
获取同一发件人的电子邮件数量非常简单:

"map": "function (doc) { emit(doc.sender, null) }",
"reduce": "_count"
和。 但当我还想过滤时(例如按邮箱过滤),事情很快就会变得一团糟

如果我将过滤器添加到视图键(例如,最终结果看起来像
{“key”:[“收件箱”,1234,”foo@example.com“],“value”:null}
,则在单个筛选器值内按计数排序很简单。但使用多个筛选器按计数排序数据将需要遍历整个数据集(每个键),在大型数据集上速度太慢

或者,我可以为每个可能的过滤器选择创建一个索引;例如,最终结果看起来像
{“key”:[[“mbox1”,“mbox2”],1234,”foo@example.com“],“value”:null},
(用于同时选择“mbox1”和“mbox2”时)或
{“key”:[[“mbox1”],1234,”foo@example.com“],“值”:{…},
(用于仅选择“mbox1”时)。这很容易查询,速度也很快。但索引的磁盘大小似乎会成倍增长(随着不同筛选字段的数量增加)。而且,对于对开放式数据(如日期范围)进行筛选,这似乎是完全站不住脚的

最后,我可以动态生成视图,仅在需要时动态处理所需的过滤器,并在不再使用这些过滤器后将其拆下(以节省磁盘空间)。缺点是代码复杂度大幅增加,每次选择新过滤器都会带来巨大的前期成本


有更好的方法吗?

我已经思考了将近一天,我认为没有比你提出的更好的方法了。你面临的挑战如下:

1) 聚合工作(计数、求和等)只能通过物化视图引擎(mapreduce)在CouchDB/Cloudant API中完成

2) 虽然group_级别的API提供了一定的灵活性,可以在查询时指定可变粒度,但对于任意布尔查询来说,它不够灵活

3) 通过基于lucene的搜索API,Cloudant API中可以进行任意布尔查询。但是,搜索API不支持聚合后查询。对您想要做的事情的有限支持只能在lucene中使用faceting,而Cloudant中还不支持faceting。即使如此,我相信它可能只支持
count
而不支持
sum
或更复杂的聚合

我认为您面临的最佳选择是使用_searchAPI并使用sort、groupby或groupu sort,然后在客户端上进行聚合。要测试的几个示例URL如下所示:

GET/db/_design/ddoc/_search/indexname?q=name:mike和age:[1.2到4.5]&sort=[“age”,“name”]


GET/db/\u design/ddoc/\u search/indexname?q=name:mike和group\u by=“mailbox”和group\u sort=[“age”,“name”]

我很害怕。在昨天对Cloudant_search API进行了实验之后,我得出结论,所有的聚合都是在查询前进行的,因此无法对组合的总和进行排序。此外,我不确定查询_search API并在客户端进行聚合是否比我们现在做的更好,这是在客户端上创建第一个过程map/reduce视图并进行聚合。对于小型或中型数据集来说,这很好,但是对于大型数据集(例如,成千上万的独特主题或发送者),它可能会非常慢。我对lucene中的面处理不是很熟悉。但是,即使它只支持
count
,我们也可以在最重要的情况下使用它(按计数排序)。re:“我不确定在客户端查询搜索API并进行聚合是否比我们现在所做的更好,我们现在正在客户端上创建首个pass map/reduce视图并进行聚合”;仔细想想,它可能会更好地处理范围过滤器(例如日期)。