3

首先这是我第一次来蒙哥...

概念:

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

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

{
"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 中缺乏经验将我指向错误的“模式设计”。

知道什么可能是解决此类问题的好方法吗?

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

若昂

4

2 回答 2

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

另一种选择是使用新的聚合框架,它将在 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 命令,这取决于你想要的结果有多准确。对于更新查询条件,您可以使用单词文档创建日期。

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

于 2012-06-25T06:39:43.377 回答
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 }
            });
于 2012-06-25T00:05:00.653 回答