我使用MongoDB mapReduce来编写排名提要算法,它几乎可以工作,但最新实现的是分页。map reduce支持结果限制,但知道我在使用mongoose,我如何实现基于结果的最新查看_id的偏移(跳过)?
这是我写的程序:
o = {};
o.map = function() {
//log10(likes+comments) / elapsed hours from the post creation
emit(Math.log(this.likes + this.comments + 1) / Math.LN10 / Math.abs((now - this.createdAt) / 6e7 + 1), this);
};
o.reduce = function(key, values) {
//sort the values, when they have the same score
values.sort(function(a, b) {
a.createdAt - b.createdAt;
});
//serialize the values, because mongoose does not support multiple returned values
return JSON.stringify(values);
};
o.scope = {now: new Date()};
o.limit = 15;
Posts.mapReduce(o, function(err, results) {
if (err) return console.log(err);
console.log(results);
});
此外,如果mapReduce不是一条路,你是否建议其他人如何实现这样的东西?
您需要的是一个页面分隔符,它不是您所说的最近查看的id,而是您的排序属性。在这种情况下,它似乎是公式Math.log(this.likes + this.comments + 1) / Math.LN10 / Math.abs((now - this.createdAt) / 6e7 + 1)
。
因此,在您的映射中,Reduce query
需要保存上述公式的where
值。或者具体地说,'formula>=. And also it needs to hold the value of createdAt at the last page, since you don't sort by that. (Assuming createdAt is unique). So your
查询of mapReduce would say
,其中:FormulaExpression,createdAt:{$lt:lastCreatedAt}`
如果您确实允许多个相同的createdAt值,那么您必须在数据库本身之外玩一点。
所以你只需要按公式搜索。
理想情况下,这会为您提供一个恰好具有该值的元素,然后对下一个元素进行排序。因此,在回复模块调用程序时,从数组中删除第一个元素(并确保您实际要求的结果比您需要的结果多)。
现在,由于您允许多个类似的值,因此需要另一个标识道具,例如对象id或created_at。您的使用者(此模块的调用方)必须同时提供(last value of the score
和createdAt of the last object
)。假设你有一个页面正好被拆分在中间——一个或多个对象在上一个页面上,另一个对象设置在下一个页面。你不需要简单地删除最高值(因为上一页已经提供了相同的分数),还可能从顶部删除几个分数。
然后它就变得非常疯狂了,因为可能你的整个页面都已经被服务了——比较_id,在模块调用程序为你提供的_id之后寻找第一个_id。或者查看数据,确定有多少类似的匹配值,尝试从mapReduce中获得至少与实际页面大小一样多的值。
除此之外,我会用聚合来做这件事,它应该更具性能。