我想过滤一个列表,并根据一个聚合进行排序;这在SQL中是相当简单的,但是我不知道用迭代Map来实现它的最佳方法。我特别使用Cloudant在CouchDB中添加的"dbcopy“,但我认为这种方法可能与其他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有一个很好的例子,可以根据地图约简的第二次传递进行排序。。但是当我也想要过滤(例如通过邮箱)时,事情会很快变得混乱。
如果我将过滤器添加到视图键(例如,最终结果看起来类似于{"key": ["INBOX", 1234, "foo@example.com"], "value": null} ),那么在单个筛选值中按计数进行排序就很简单了。但是,使用多个筛选器按计数对数据进行排序需要遍历整个数据集(按键),这在大型数据集上太慢了。
或者,我可以为每个潜在的过滤器选择创建一个索引;例如,最终结果看起来像{"key": [["mbox1", "mbox2"], 1234, "foo@example.com"], "value": null}, (当"mbox1“和"mbox2”都被选中时)或{"key": [["mbox1"], 1234, "foo@example.com"], "value": {...}}, (当只选择"mbox1“时)。这很容易查询,而且速度很快。但是,似乎索引的磁盘大小将呈指数增长(随不同的过滤字段的数量而增加)。而且,对开放式数据(如日期范围)进行过滤似乎是完全站不住脚的。
最后,我可以动态地生成视图,这些视图只在需要的情况下动态地处理所需的过滤器,并在它们不再被使用(以节省磁盘空间)之后将它们删除。这里的缺点是代码复杂性的巨大飞跃,以及每次选择新的过滤器时都要付出很大的前期成本。
有更好的办法吗?
发布于 2013-04-04 18:24:21
我已经思考了将近一天了,我认为没有比你提议的更好的方法了。你们面临的挑战如下:
1)聚合工作(计数、和等)只能通过物化视图引擎(mapreduce)在CouchDB/Cloudant中完成。
2)虽然group_level API提供了一定的灵活性,可以在查询时指定变量粒度,但对于任意布尔查询,它不够灵活。
3) Cloudant中可以通过基于lucene的_search API进行任意布尔查询。但是,_search API不支持聚合post查询。对您想要做的事情的有限支持只能在lucene中使用faceting,而Cloudant中还不支持这个功能。即使如此,我相信它可能只支持count,而不支持sum或更复杂的聚合。
我认为您面临的最佳选择是使用_search API并使用排序、group_by或group_sort,然后在客户机上进行聚合。需要测试的几个示例URL如下所示:
获取/db/_design/ddoc/_search/indexname?q=name:mike和age:1.2 TO 4.5&sort="age","name“
获取/db/_design/ddoc/_search/indexname?q=name:mike和group_by=“邮箱”和group_sort=“年龄”、“名称”
https://stackoverflow.com/questions/15797112
复制相似问题