【问题标题】:Mongo DB MapReduce in PHPPHP中的MongoDB MapReduce
【发布时间】:2017-07-08 15:01:41
【问题描述】:

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

概念:

  1. 用户能够用自然语言描述图像。
  2. 划分用户输入并将他描述的单词存储在一个名为 字。
  3. 用户必须能够浏览最常用的词并将这些词添加到他们的描述中。
  4. 系统将使用最常用的词(针对所有用户)并使用 这些词来描述图像。

我的words文档(目前)如下(示例)

{
"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. 无法限制返回结果的数量

我考虑过每天在给定时间运行一项任务,以将给定用户未用于描述给定图像的单词排名存储在文档(在不同的集合中)列表中。我必须将其限制为 300 个结果或其他内容(关于适当限制的任何想法?)类似:

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

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

  1. 结果会有相当大的延迟,这是不可取的。
  2. 为所有用户生成此文档时的服务器负载可能会激增(实际上我在 Mongo 中对此知之甚少,因此这只是一个假设)

也许我的方法没有任何意义......也许我在 Mongo 方面缺乏经验,这将我指向错误的“模式设计”。

你知道什么是解决这类问题的好方法吗?

很抱歉这篇大文章,感谢您的时间和帮助!

若昂

【问题讨论】:

    标签: php mongodb mapreduce schema nosql


    【解决方案1】:

    如前所述,您可以使用易于使用的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 输出选项,它将帮助您解决性能问题。看看MapReduce中的out参数。

    下面是一个示例,它使用增量功能将现有集合与 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 命令,这取决于你想要的结果有多准确。对于更新查询条件,您可以使用单词文档创建日期。

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

    【讨论】:

    • 这是一个很好的答案!真的很期待2.2版本。同时我会试试你的cmets!真的很感激
    • 对不起。但是对 mapreduce 的限制是在计算完所有内容之后应用的,或者它会停止对 10 个文档的计算?考虑到我将在 php 中对结果进行排序(至少在组案例中),你的建议也是性能方面的。
    • 该限制在 MapReduce 执行后或您对集合进行排序/查找时应用。正如我所说,您可以随时重用结果。更新集合的时间和频率取决于您。同样在性能方面,您应该可以在 php 端进行排序。如果将来这将是一个问题,只需使用某种缓冲区。
    【解决方案2】:

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

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

    【讨论】:

    • 是的,排序必须在客户端进行
    猜你喜欢
    • 1970-01-01
    • 2021-04-07
    • 2011-08-18
    • 2015-09-27
    • 2013-06-03
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多