我有一个有趣的问题。我有一个工作的M / R版本,但它在小规模环境中并不是真正可行的解决方案,因为它太慢而且查询需要实时执行。
我想迭代集合中的每个元素并对其进行评分,按降序排序,限制为前10并将结果返回给应用程序。
这是我想用伪代码应用于每个文档的函数。
var score = 0;
foreach(tag in document.Tags) {
score += someMap[tag];
}
return score;
答案 0 :(得分:3)
由于您的someMap
每次都在变化,除了对所有文档进行评分并返回得分最高的文档之外,我看不到任何其他选择。无论您采用何种方法进行此类操作,您都必须考虑集合中的所有文档,这些文档速度会很慢,并且随着您扫描的集合的增长而变得越来越昂贵。
map reduce的一个问题是每个mongod
实例只能运行一个并发映射reduce。这是javascript引擎的限制,它是单线程的。多个映射reduce将被交错,但它们不能彼此同时运行。这意味着,如果您依赖map reduce进行“实时”使用,也就是说,如果您的网页必须运行地图缩小以进行渲染,那么您最终将达到限制,其中页面加载时间会变得无法接受。
您可以通过查询应用程序中的所有文档,以及对应用程序代码进行评分,排序和限制来解决此问题。与map reduce不同,MongoDB中的查询可以同时运行,但这当然意味着您的应用程序服务器必须做很多工作。
最后,如果您愿意等待MongoDB 2.2发布(应该在几个月内),您可以使用新的aggregation framework代替map reduce。您必须按下someMap
以生成正确的管道步骤。以下是someMap
为{"a": 5, "b": 2}
时的情况示例:
db.runCommand({aggregate: "foo",
pipeline: [
{$unwind: "$tags"},
{$project: {
tag1score: {$cond: [{$eq: ["$tags", "a"]}, 5, 0]},
tag2score: {$cond: [{$eq: ["$tags", "b"]}, 3, 0]}}
},
{$project: {score: {$add: ["$tag1score", "$tag2score"]}}},
{$group: {_id: "$_id", score: {$sum: "$score"}}},
{$sort: {score: -1}},
{$limit: 10}
]})
这有点复杂,并且解释说:
_id
)对于每个展开的元素都是重复的。$cond
/ $eq
表达式意味着(对于tag1score
示例)“如果'tags'字段中的文档中的值id等于'a',则返回5并将该值分配给新字段tag1score
,否则返回0并指定“。对于someMap
中的每个标记/分数组合,将重复此表达式。在管道中的这一点上,每个文档将包含N tagNscore
个字段,但最多其中一个将具有非零值。score
字段,其值是文档中tagNscore
字段的总和。_id
对文档进行分组,并在每个组中的所有文档中总结上一步中score
字段的值。score
排序,降序(即最高分)我将把它作为练习留给读者如何将someMap
转换为步骤2中正确的投影集,以及在步骤3中添加的正确字段集。
这与您的应用程序代码或map reduce会经历的步骤基本相同,但具有以下明显优势:聚合框架不是map reduce,而是完全用C ++实现,并且比map更快,更并发降低;与向应用程序查询所有文档不同,聚合框架与服务器端的数据一起使用,从而节省了网络负载。但与其他两种方法一样,这仍然需要考虑每个文档,并且只能在为所有文档计算得分后限制结果集。