MongoDB,MapReduce和排序

Jon*_*ach 10 mongodb mongodb-php mongodb-query

我可能会对此有点了解,因为我还在学习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({popular:-1}),因为这个功能的工作方式.

cro*_*das 15

首先,Mongo map/reduce不是设计用作查询工具(就像在CouchDB中一样),它是为您设计运行后台任务的.我在工作中使用它来分析流量数据.

然而,你所做错的是你将sort()应用于你的输入,但它是无用的,因为当map()阶段完成时,中间文档按每个排序keys.因为你的密钥是一个文件,所以它正在排序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)
        }) 
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的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},
});
Run Code Online (Sandbox Code Playgroud)

这是最终结果(很长时间将其粘贴到此处):

http://cesarodas.com/results.txt

这是有效的,因为现在我们正在排序sorting, product_id, popularity.您可以按照自己喜欢的方式进行排序,只需记住,key无论您的输入如何排序,最终的排序都是如此.

无论如何,正如我之前所说,你应该避免使用Map/Reduce进行查询,它是专为后台处理而设计的.如果我是你,我会以这样的方式设计我的数据,我可以通过简单的查询访问它,在这种情况下总是需要权衡复杂的插入/更新以便进行简单的查询(这就是我看到的MongoDB).


Ste*_*nie 9

正如在对原始问题的讨论中所指出的:

  • 使用内联输出的Map/Reduce当前不能使用显式sort键(请参阅SERVER-3973).可能的解决方法包括依赖于发出的密钥顺序(参见@ crodas的答案); 输出到集合并使用排序顺序查询该集合; 或使用类似usort()的方法对应用程序中的结果进行排序.

  • OP的首选是内联结果,而不是创建/删除临时集合.

  • MongoDB 2.2中的聚合框架(目前是生产版本候选者)将提供合适的解决方案.

以下是对原始Map/Reduce进行类似查询的示例,而是使用聚合框架:

db.products.aggregate(
  { $match: { category_id: 20 }},
  { $group : {
     _id : "$product_id",
     'popularity' : { $sum : "$popularity" },
  }},
  { $sort: { 'popularity': -1 }}
)
Run Code Online (Sandbox Code Playgroud)

..和样本输出:

{
    "result" : [
        {
            "_id" : 50,
            "popularity" : 139
        },
        {
            "_id" : 150,
            "popularity" : 99
        },
        {
            "_id" : 123,
            "popularity" : 55
        }
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)