2

我们从 2.0 开始使用 Mongo。作为我们经常运行的查询的一部分,我们运行 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 );
4

3 回答 3

3

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

话虽如此,提供的补丁应该可以工作,但由于锁定问题(以及上述安全问题),我会警告不要使用它。

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

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

于 2013-05-02T23:02:23.977 回答
2

听起来您在描述需要加入 2 个集合。我过去成功地做到了这一点,并在此处编写了一个示例,其中包括来自 data.gov 和 cia.gov 的测试数据:

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

这种方法有两点需要注意:

  1. 集合之间的连接条件需要是发出的键。
  2. 发出的文件的形状应该相同。

然后只需在 reducer 中组合发出的文档即可。这样做应该可以跨分片很好地扩展,甚至可能跨内核在本地扩展(请参阅https://jira.mongodb.org/browse/SERVER-4258)。

于 2013-05-02T23:49:22.487 回答
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);
  }
于 2014-09-18T21:18:44.910 回答