【发布时间】:2011-07-06 02:28:20
【问题描述】:
寻找与 Postgres 的 Distinct On 类似的功能。
有一个文档集合 {user_id, current_status, date},其中状态只是文本,日期是日期。仍处于围绕 mongo 探索最佳方法的早期阶段。
mapreduce 会是这里最好的解决方案吗,map 发出所有,reduce 记录最新的,还是有一个内置的解决方案而不拉出 mr?
【问题讨论】:
寻找与 Postgres 的 Distinct On 类似的功能。
有一个文档集合 {user_id, current_status, date},其中状态只是文本,日期是日期。仍处于围绕 mongo 探索最佳方法的早期阶段。
mapreduce 会是这里最好的解决方案吗,map 发出所有,reduce 记录最新的,还是有一个内置的解决方案而不拉出 mr?
【问题讨论】:
有一个distinct command,但我不确定这是否是您所需要的。 Distinct 是一种“查询”命令,如果有很多用户,您可能不希望实时汇总数据。
Map-Reduce 可能是一种方法。
Map Phase:您的key 只是一个ID。您的value 将类似于以下{current_status:'blah',date:1234}。
减少阶段:给定一组值,您将获取最新的值并仅返回它。
为了使这项工作达到最佳状态,您可能需要查看 1.8.0 中的一项新功能。 "re-reduce" feature。将允许您仅处理新数据,而不是重新处理整个状态集合。
另一种方法是构建一个“最近的”集合并将状态插入绑定到该集合。因此,当您为用户插入新状态时,您会更新他们的“最近”状态。
根据此功能的重要性,您可以同时做这两件事。
【讨论】:
upsert。 upsert 是“如果存在则更新否则创建”。所以没有删除或没有额外的查询。在 JS 中,这看起来像以下 db.most_recent.update({_id:user_id},{status:"blah"},false,true),请检查您的语言驱动程序是否有“upsert”。
目前看来运行良好的解决方案。
map = function () {emit(this.user.id, this.created_at);}
//We call new date just in case somethings not being stored as a date and instead just a string, cause my date gathering/inserting function is kind of stupid atm
reduce = function(key, values) { return new Date(Math.max.apply(Math, values.map(function(x){return new Date(x)})))}
res = db.statuses.mapReduce(map,reduce);
【讨论】:
实现相同结果的另一种方法是使用 group 命令,这是一种 mr 快捷方式,可让您聚合特定键或一组键。
在您的情况下,它将如下所示:
db.coll.group({ key : { user_id: true },
reduce : function(obj, prev) {
if (new Date(obj.date) < prev.date) {
prev.status = obj.status;
prev.date = obj.date;
}
},
initial : { status : "" }
})
但是,除非您的用户数量很少,否则我坚信更好的解决方案是,如前所述,保留一个单独的集合,其中仅包含每个用户的最新状态消息。
【讨论】: