2013-05-02 27 views
2

我们在2.0开始使用Mongo。作为我们经常运行的查询的一部分,我们运行map/reduce作业,这些作业也会在这些作业执行时从其他集合中提取数据。Mongo 2.4 - 使服务器端代码中的db对象可用(map/reduce)

由于mongo 2.4,此功能被删除(http://docs.mongodb.org/manual/release-notes/2.4/#additional-limitations-for-map-reduce-and-where-operations),唯一的建议是“重构你的代码”。

那么,有没有一种方法来重构代码?我知道这是一个普遍的问题,但我要求提供一个通用的应用程序。涉及的集合和交叉查询的大小和使用具有足够的多样性。

在这一点上,我甚至会采取一个补丁,将db对象带回服务器代码范围。看来,下面的补丁就足够了(是的,我知道它有这些安全隐患,但我还没有使用的碎片这是最快捷的方式为我们夺回我们赖以生存的功能。):

diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp 
index 742392f..225a2b7 100644 
--- a/src/mongo/db/commands/mr.cpp 
+++ b/src/mongo/db/commands/mr.cpp 
@@ -623,6 +623,7 @@ namespace mongo { 
     void State::init() { 
      // setup js 
      _scope.reset(globalScriptEngine->getPooledScope(_config.dbname, "mapreduce").release()); 
+   _scope->localConnect(_config.dbname.c_str()); 

      if (! _config.scopeSetup.isEmpty()) 
       _scope->init(&_config.scopeSetup); 
+0

的问题是,由于数据库对象分片怎么跑envos这个功能实际上是一个安全漏洞,可能被用来绕过对其他数据库的认证来攻击你的数据。至于如何重构你的代码;你到底在做什么?你能给我们一些逻辑吗? – Sammaye 2013-05-02 17:47:51

+0

@Sammaye不要让我开始这个“安全”的事情。这不是一个修复,它是一个明显的插件解决方案,而不是正确地重新设计/修复安全架构。我知道我总是可以重构我的查询,以预先加入客户端的数据,或将数据预加载到服务器范围中。但我不想,而且性价比很高。 – 2013-05-02 18:25:15

回答

3

从概念上讲,MapReduce只对输入文档集和发射值进行操作。严格来说,执行数据库操作超出了MapReduce作业的范围。有关更改信息的更多信息,请参阅SERVER-8104SERVER-3130

说了那么,提供的补丁应该可以工作,但我会提醒不要使用它由于锁定影响(和上述安全问题)。

至于如何重构可能在过去版本中使用过这个特性的代码的一般化问题,想到的一个建议是使用多个利用'reduce'输出类型的MapReduce作业。 incremental MapReduce的文档在这里可能会有所帮助。

如果目标是服务器端执行,另一个选项可能是将MapReduce逻辑重构为可以使用db.eval()执行的脚本。由于问题的性质,这可能有点复杂。

+0

谢谢您验证该修补程序。就我而言,至少在其中的一些中,实际上不可能使用基于map-reduce的连接(与渐进式方法相同)。连接查找可以将关键空间从数百万个值减少到少数。我不相信在'map'函数或客户端查找关键字之间锁定使用的区别很大。 – 2013-05-03 00:27:48

2

听起来像你正在描述需要加入2个集合。我在成功的过去做到了这一点,并写了包括来自data.gov测试数据和cia.gov这里一个例子:

http://tebros.com/2011/07/using-mongodb-mapreduce-to-join-2-collections/

有2周要注意的这种方法主要的事情:

  1. 您的集合之间的连接条件将需要是发出的密钥。
  2. 正在排出的文件的形状应该是相同的。

然后,它只是一个结合你的reducer发出的文件的问题。这样做应该在整个碎片上很好地扩展,甚至可以在整个核心上进行本地扩展(请参阅https://jira.mongodb.org/browse/SERVER-4258)。

+0

这是一个很好的伎俩,我从来没有考虑过它。当我不得不这样做简单的连接时,我仍然会使用客户端连接。但是,我不可能通过键入所有m/r的应用程序。 – 2013-05-03 00:19:31

+0

@PawelVeselov问题在于你在两个表中进行了无用的查找,然后只是将两个MR的输出合并为一个集合,实际上并没有进行连接,并且也不会获得任何受限数据集的好处 – Sammaye 2013-05-03 09:52:31

+0

@Sammaye减少地图的强大功能是映射函数可以固定在输出中需要的精确键上。派生这些密钥的函数可能相当复杂和精细。但有时(也是@vrtx)这些函数不能被编码,并且必须查找一些外部数据源。我将这种查找能力看作是任何其他函数使用的逻辑扩展。 – 2013-05-05 06:55:16

0

我知道一些古老的问题,为了他人的利益在这里登录。这适用于2.6.4。

根据您的要求,您仍然可以在mapreduce期间从其他集合中获取文档。您可以声明dbref并从您的收藏中获取数据。即使文档说$ id字段需要id,我已经尝试过标准mongo查询,似乎正在工作。基于dbref文档,你也可以指向不同的数据库,虽然我还没有尝试过。

这里是我从其他两个集合中获取数据,并没有任何共同的字段名称我的地图功能的一个简单的例子

function() { 
var values = { 
    providerAccount: this, 
}; 

if (!(typeof(this['lastStatusLogId']) === 'undefined')) { 

     var v = new Object(); 
     v["$ref"] = "statuslog"; 
     v["$id"] = new ObjectId(this['lastStatusLogId']); 
     values['lastStatusLog'] = v; 
    } 

    v = new Object(); 
    v["$ref"] = "provider"; 
    v["$id"] = new ObjectId(this.providerId); 
    values["provider"] = v; 

    emit(this._id, values); 
    }