我可能对此有点不知所措,因为我仍在学习 MongoDB 的来龙去脉,但这里继续。
现在我正在开发一种工具来搜索/过滤数据集,按任意数据点(例如流行度)对其进行排序,然后按 id 对其进行分组。我认为我能做到这一点的唯一方法是通过 Mongo 的 MapReduce 功能。
我不能使用 .group(),因为我正在使用超过 10,000 个键,而且我还需要能够对数据集进行排序。
我的 MapReduce 代码运行良好,除了一件事:排序。排序根本不想工作。
db.runCommand({
'mapreduce': 'products',
'map': function() {
emit({
product_id: this.product_id,
popularity: this.popularity
}, 1);
},
'reduce': function(key, values) {
var sum = 0;
values.forEach(function(v) {
sum += v;
});
return sum;
},
'query': {category_id: 20},
'out': {inline: 1},
'sort': {popularity: -1}
});
我已经在流行度数据点上有一个降序索引,所以它肯定因为缺少它而不起作用:
{
"v" : 1,
"key" : { "popularity" : -1 },
"ns" : "app.products",
"name" : "popularity_-1"
}
我只是不明白为什么它不想排序。
由于此功能的工作方式,我不能将结果集输出到另一个集合,然后对其运行 .find().sort({popularity: -1}) ,而不是内联结果集。
最佳答案
首先,Mongo map/reduce 不是为用作查询工具而设计的(就像在 CouchDB 中一样),它是为您运行后台任务而设计的。我在工作中使用它来分析流量数据。
但是,您做错的是您将 sort() 应用于您的输入,但这没有用,因为当 map()
阶段完成时,中间文档按每个键
。因为您的 key 是一个文档,所以它按 product_id
、popularity
排序。
这就是我生成数据集的方式
function generate_dummy_data() {
for (i=2; i < 1000000; i++) {
db.foobar.save({
_id: i,
category_id: parseInt(Math.random() * 30),
popularity: parseInt(Math.random() * 50)
})
}
}
这是我的 map/reduce 任务:
var data = db.runCommand({
'mapreduce': 'foobar',
'map': function() {
emit({
sorting: this.popularity * -1,
product_id: this._id,
popularity: this.popularity,
}, 1);
},
'reduce': function(key, values) {
var sum = 0;
values.forEach(function(v) {
sum += v;
});
return sum;
},
'query': {category_id: 20},
'out': {inline: 1},
});
这是最终结果(很长在这里粘贴):
http://cesarodas.com/results.txt
这是可行的,因为现在我们正在按 sorting、product_id、流行度
进行排序。您可以随心所欲地进行排序,只要记住最终排序是按 key
进行的,无论您的输入是如何排序的。
无论如何,正如我之前所说,您应该避免使用 Map/Reduce 进行查询,它是为后台处理而设计的。如果我是你,我会设计我的数据,以便我可以通过简单的查询访问它,在这种情况下,总是需要权衡复杂的插入/更新来进行简单的查询(这就是我对 MongoDB 的看法)。
关于MongoDB、MapReduce 和排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12015064/