假设我有一个这样的用户集合:-
{
"_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"并进行计数,但我现在想做的是按组计数事件。
如果我的事件文档中有一个"Groups"数组,那么这将很容易,但我没有,这只是一个例子,它的实际应用要复杂得多,我不想将所有数据复制到事件文档中。
我在上看到了一个例子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
每种方法都有哪些可能,有哪些好处/问题?
您的第三种方法是:
找出如何将事件和用户集合"连接"到第三个集合中我可以在上运行mapreduce
要做到这一点,您需要创建一个新的集合J
,其中包含地图缩减所需的"联接"数据。有几种策略可以用于此:
-
更新您的应用程序以在正常业务过程中插入/更新
J
。这在您需要非常频繁地使用最新数据运行MR的情况下是最好的。它会大大增加代码的复杂性。从实现的角度来看,您可以直接(通过写入J
)或间接(通过将更改写入日志集合L
,然后将"新"更改应用于J
)执行此操作。如果您选择日志收集方法,您将需要一个策略来确定发生了什么变化。有两种常见的方法:高水印(基于_id
或时间戳)和使用findAndModify
命令将日志集合用作队列。 -
以批处理模式创建/更新
J
。在高性能系统的情况下,上述策略的多次更新会影响性能。如果您不需要非常频繁地运行MR和/或不必保证高达第二级的数据准确性,这也是一种方法。
如果使用(2),您将不得不迭代需要加入的集合中的文档——正如您所发现的,Mongo-map-reduce在这里对您没有帮助。有很多可能的方法可以做到这一点:
-
如果您没有太多的文档,并且它们很小,那么您可以通过与数据库的直接连接在数据库之外进行迭代。
-
如果不能执行(1),则可以使用
db.eval()
在DB内部进行迭代。如果文档数量不小,请确保使用nolock: true
,因为默认情况下db.eval
是阻塞的。这通常是我选择的策略,因为我倾向于处理非常大的文档集,而且我负担不起在网络上移动它们的费用。 -
如果不能执行(1),也不想执行(2),则可以使用临时DB将集合克隆到另一个节点。Mongo为此提供了一个方便的cloneCollection命令。请注意,如果DB需要身份验证,这是不起作用的(不要问为什么;这是一个奇怪的10gen设计选择)。在这种情况下,您可以使用
mongodump
和mongorestore
。一旦你有了新数据库的本地数据,你就可以随心所欲地参与其中。完成MR后,您可以更新生产数据库中的结果集合。我将此策略用于具有大量预处理的一次性映射缩减操作,以便不加载生产副本集。
祝你好运!