过滤和排序基于与Cloudant / CouchDB链接映射缩减的聚合

时间:2013-04-03 20:11:20

标签: 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 BY aggregate(*), grouping_field
LIMIT    page_size

过滤器可能正在寻找匹配,或者可能会在范围内搜索;例如field in ('foo', 'bar')field between 37 and 42

作为一个具体的例子,考虑一下电子邮件的数据集;分组字段可能是“List-id”,“Sender”或“Subject”;聚合函数可能是count(*),或max(date)min(date); filter子句可能会考虑标志,日期范围或邮箱ID。文件可能如下:

{
  "id": "foobar", "mailbox": "INBOX", "date": "2013-03-29",
  "sender": "foo@example.com", "subject": "Foo Bar"
}

使用相同的发件人计算电子邮件数量非常简单:

"map": "function (doc) { emit(doc.sender, null) }",
"reduce": "_count"

Cloudant has a good example of sorting by count on the second pass of a map reduce。 但是,当我也想要过滤(例如通过邮箱)时,事情会变得很乱。

如果我将过滤器添加到视图键(例如,最终结果看起来像{"key": ["INBOX", 1234, "foo@example.com"], "value": null}),那么在单个过滤器值中按count 排序是微不足道的。但是按计数对数据进行排序使用多个过滤器需要遍历整个数据集(每个键),这对大型数据集来说太慢了。

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

最后,我可以动态生成只在需要时动态处理所需过滤器的视图,并在不再使用它们之后将其拆除(以节省磁盘空间)。这里的缺点是代码复杂性大幅增加,每次选择新过滤器时前期成本都很高。

有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

近一天我一直在考虑这个问题,我认为没有比你提出的更好的办法了。您面临的挑战如下:

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

2)虽然group_level API在查询时提供了一些指定变量粒度的灵活性,但它对于任意布尔查询都不够灵活。

3)通过基于lucene的_search API,Cloudant API中可以进行任意布尔查询。但是,_search API不支持聚合 post 查询。对你想要做的事情的有限支持只能在使用faceting的lucene中实现,而Cloudant尚不支持。即使这样,我相信它可能只支持count而不支持sum或更复杂的聚合。

我认为您面临的最佳选择是使用_search API并使用sort,group_by或group_sort,然后在客户端上进行聚合。一些要测试的示例网址如下所示:

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

GET / db / _design / ddoc / _search / indexname?q = name:mike AND group_by =“mailbox”AND group_sort = [“age”,“name”]