11

我可能对此有点不知所措,因为我仍在学习 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}) 。

4

2 回答 2

15

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

但是,您做错的是您将 sort() 应用于您的输入,但它没有用,因为当map()阶段完成时,中间文档按 each 排序keys。因为您的密钥是一个文档,所以它是按 , 排序product_idpopularity

这就是我生成数据集的方式

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)
        }) 
    }
}

这是我的地图/减少任务:

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, popularity. 你可以随心所欲地进行排序,只要记住最终排序是由key你输入的排序方式决定的。

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

于 2012-08-18T06:16:46.573 回答
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 }}
)

..和样本输出:

{
    "result" : [
        {
            "_id" : 50,
            "popularity" : 139
        },
        {
            "_id" : 150,
            "popularity" : 99
        },
        {
            "_id" : 123,
            "popularity" : 55
        }
    ],
    "ok" : 1
}
于 2012-08-18T06:02:24.633 回答