PHP中的Mongodb MapReduce

时间:2012-06-24 22:48:13

标签: php mongodb mapreduce schema nosql

首先,这是我第一次来Mongo ...

概念:

  1. 用户能够用自然语言描述图像。
  2. 划分用户输入并将他描述的单词存储在名为的Collection中 话。
  3. 用户必须能够浏览最常用的单词并将这些单词添加到其说明中。
  4. 系统将使用最常用的单词(适用于所有用户)并使用 那些描述图像的词。
  5. 我的文字文件(目前)如下(例子)

    {
    "date": "date it was inserted"
    "reported": 0,
    "image_id": "image id"
    "image_name": "image name"
    "user": "user _id"
    "word": "awesome"
    }
    

    这些单词将被复制,以便每个单词都可以与用户相关联......

    问题:我需要执行一个Mongo查询,以便让我知道不是由给定用户创建的最常用的单词(描述图像)。 (见上文第3点)

    我见过MapReduce算法,但从我读到的内容中有几个问题:

    1. 无法对结果进行排序(我可以从最常用的排序中进行排序)
    2. 在数百万个文档中,处理时间很长。
    3. 无法限制返回的结果数
    4. 我考虑过每天在给定时间运行任务以存储文档(在不同的集合中)列表,给定用户未用于描述给定图像的单词的等级。我必须将此限制为300个结果或其他内容(任何关于适当限制的想法),例如:

      {
      user_id: "the user id"
      [
      {word: test, count: 1000},
      {word: test2, count: 980},
      {word: etc, count: 300}
      ]
      }
      

      我在这个解决方案中遇到的问题是:

      1. 结果会有很长的延迟,这是不可取的。
      2. 为所有用户生成此文档时服务器加载可能会出现峰值(我实际上对Mongo知之甚少,所以这只是一个假设)
      3. 也许我的方法没有任何意义......也许我在Mongo中缺乏经验会让我指出错误的“架构设计”。

        对这种问题有什么好办法的想法?

        对不起,感谢您的时间和帮助!

        若昂

2 个答案:

答案 0 :(得分:3)

如前所述,您可以使用易于使用的group命令,但您需要在客户端对结果进行排序。此外,结果作为单个BSON对象返回,因此必须相当小 - 少于10,000个键,否则您将获得异常。

基于您的数据结构的代码示例:

db.words.group({
    key : {"word" : true},
    initial: {count : 0},
    reduce: function(obj, prev) { prev.count++},
    cond: {"user" :{ $ne : "USERNAME_TO_IGNORE"}}
})

另一种选择是使用新的Aggregation framework,它将在2.2版本中发布。这样的事情应该有效。

db.words.aggregate({
   $match : { "user" : { "$ne" : "USERNAME_TO_IGNORE"} },
   $group : {
     _id : "$word",
     count: { $sum : 1}
   }
})

或者你仍然可以使用MapReduce。实际上你可以限制和排序输出,因为结果是 一个集合。只需在输出上使用.sort()和.limit()。您也可以使用增量 map-reduce输出选项,它将帮助您解决性能问题。请查看out中的MapReduce参数。

Bellow它是一个例子,它使用增量功能将现有集合与words_usage集合中的新数据合并:

m = function() { 
   emit(this.word, {count: 1}); 
};


r = function( key , values ){
     var sum = 0;
     values.forEach(function(doc) {
          sum += doc.count;
     });
     return {count: sum};
 };

db.runCommand({
    mapreduce : "words", 
    map : m,
    reduce : r,
    out : { reduce: "words_usage"},
    query : <query filter object>
})

# retrieve the top 10 words
db.words_usage.find().sort({"value.count" : -1}).sort({"value.count" : -1}).limit(10)

我猜你可以每隔几分钟/小时在cron中运行上面的MapReduce命令,这取决于你想要的结果。对于更新查询条件,您可以使用单词文档创建日期。

一旦你拥有系统顶级单词集合,你就可以构建每个用户的顶级单词或者只是实时计算它们(取决于系统大小)。

答案 1 :(得分:1)

group函数应该是MapReduce的更简单版本。您可以像这样使用它来获得每个单词的总和:

db.coll.group(
           {key: { a:true, b:true },
            cond: { active:1 },
            reduce: function(obj,prev) { prev.csum += obj.c; },
            initial: { csum: 0 }
            });