4

假设我有一组这样的用户:-

{
  "_id" : "1234",
  "Name" : "John",
  "OS" : "5.1",
  "Groups" : [{
      "_id" : "A",
      "Name" : "Group A"
    }, {
      "_id" : "C",
      "Name" : "Group C"
    }]
}

我有一系列这样的事件:-

{
  "_id" : "15342",
  "Event" : "VIEW",
  "UserId" : "1234"
}

我可以使用 mapreduce 来计算每个用户的事件数,因为我可以发出“UserId”并从中算出,但是我现在要做的是按组计算事件数。

如果我的事件文档中有一个“组”数组,那么这很容易,但是我没有,这只是一个例子,它的实际应用要复杂得多,我不想复制所有这些数据进入事件文档。

我在http://tebros.com/2011/07/using-mongodb-mapreduce-to-join-2-collections/看到了一个例子,但我看不出它在这种情况下是如何应用的,因为它是聚合值从两个地方......我真正想做的就是执行查找。

在 SQL 中,我只需将扁平化的 UserGroup 表加入事件表,然后 GROUP BY UserGroup.GroupName

我会对 mapreduce 的多次传递感到满意...首先通过 UserId 计数到类似 { "_id" : "1234", "count" : 9 } 但我在下一次传递时卡住了...如何包含组 ID

我考虑过的一些潜在方法:-

  • 在活动文档中包含群组信息(不可行)
  • 弄清楚如何“加入”用户集合或从 map 函数中查找用户组,这样我也可以发出组 ID(不知道该怎么做)
  • 弄清楚如何将事件和用户集合“加入”到我可以运行 mapreduce 的第三个集合中

什么是可能的,每种方法的好处/问题是什么?

4

1 回答 1

1

您的第三种方法是要走的路:

弄清楚如何将事件和用户集合“加入”到我可以运行 mapreduce 的第三个集合中

为此,J您需要使用 map-reduce 所需的“已连接”数据创建一个新集合。您可以为此使用多种策略:

  1. 更新您的应用程序以J在正常业务过程中插入/更新。这在您需要非常频繁地运行 MR 并使用最新数据的情况下是最好的。它可以大大增加代码的复杂性。从实现的角度来看,您可以直接(通过写入J)或间接(通过将更改写入日志集合L,然后将“新”更改应用于J)来执行此操作。如果您选择日志收集方法,您将需要确定更改内容的策略。常见的有两种:high-watermark(基于_id时间戳)和使用日志集合作为队列的findAndModify命令。

  2. 以批处理模式创建/更新J。在高性能系统的情况下,上述策略的多次更新会影响性能,这是要走的路。如果您不需要非常频繁地运行 MR 和/或您不必保证最新的数据准确性,这也是可行的方法。

如果您选择 (2),您将不得不遍历您需要加入的集合中的文档——正如您所知道的,Mongo map-reduce 在这里帮不了您。有很多可能的方法可以做到这一点:

  1. 如果您没有很多文档并且它们很小,则可以通过直接连接到数据库来在数据库之外进行迭代。

  2. 如果您不能执行 (1),您可以使用db.eval(). 如果文档数量不多,请确保默认使用nolock: trueas db.evalis blocking。这通常是我选择的策略,因为我倾向于处理非常大的文档集,并且我无法通过网络移动它们。

  3. 如果您不能执行 (1) 并且不想执行 (2),您可以将集合克隆到具有临时数据库的另一个节点。Mongo 为此提供了一个方便的cloneCollection命令。请注意,如果数据库需要身份验证,这将不起作用(不要问为什么;这是一个奇怪的 10gen 设计选择)。在这种情况下,您可以使用mongodumpmongorestore。一旦您拥有新数据库的本地数据,您就可以在您认为合适的时候使用它。完成 MR 后,您可以更新生产数据库中的结果集合。我将这种策略用于一次性 map-reduce 操作,并进行大量预处理,以免加载生产副本集。

祝你好运!

于 2012-09-03T00:40:34.963 回答