【问题标题】:mongoDB map/reduce minus the reducemongoDB map/reduce 减去 reduce
【发布时间】:2010-08-27 03:05:03
【问题描述】:

我有一些 25k 文档(原始 json 中为 4 GB)的数据,我想对其执行一些 javascript 操作,以使我的最终数据消费者 (R) 更容易访问它,我想排序通过为每个更改添加一个新集合来“版本控制”这些更改,但如果没有reduce,我无法弄清楚如何map/reduce。我想要一个一对一的文档映射 - 我从 collection_1 中的 25,356 个文档开始,我希望以 collection_2 中的 25,356 个文档结束。

我可以用这个破解它:

var reducer = function(key, value_array) {
    return {key: value_array[0]}
}

然后这样称呼它:

db.flat_1.mapReduce(mapper, reducer, {keeptemp: true, out: 'flat_2'})

(我的映射器只调用了一次发射,第一个参数是字符串,第二个参数是最终文档。这是我真正想要的第二个参数的集合。)

但这看起来很尴尬,我不知道它为什么会起作用,因为我的映射器中的emit 调用参数不等于我的reducer 的返回参数。另外,我最终得到了一个像

这样的文件
{
    "_id": "0xWH4T3V3R", 
    "value": {
        "key": {
            "finally": ["here"],
            "thisIsWhatIWanted": ["Yes!"]
        }
    }
}

这似乎没有必要。

此外,执行自己的插入操作的游标速度甚至不及mapReduce 的十分之一。我不太了解 MongoDB,无法对其进行基准测试,但我猜它大约是 50x 慢。有没有办法并行运行游标?我不在乎我的collection_2 中的文档是否与collection_1 中的文档顺序不同。

【问题讨论】:

  • 之所以起作用,是因为你的 emit 和 reducer 调用 是一样的。由于您使用 value[0] 作为减速器的输出,因此它必须完全相同,因为您没有更改它(它只是通过您的减速器)。

标签: mongodb


【解决方案1】:

当使用 map/reduce 时,你总是会得到

{ "value" : { <reduced data> } }

要删除value 键,您必须使用finalize 函数。

这是将数据从一个集合复制到另一个集合的最简单方法:

map = function() { emit(this._id, this ); }
reduce = function(key, values) { return values[0]; }
finalize = function(key, value) { db.collection_2.insert(value); }

那么当你正常运行时:

db.collection_1.mapReduce(map, reduce, { finalize: finalize });

【讨论】:

  • "finalize 函数不应出于任何原因访问数据库。" — Official MongoDB docs
  • 是的.. 但这样做还是很有用的。
  • 这是一个完全的性能瓶颈,与 Map-Reduce 背道而驰!!!请不要这样做。
【解决方案2】:

但这看起来很尴尬,我不知道它为什么会起作用,因为我的映射器中的emit 调用参数不等于我的reducer 的返回参数。

它们是等价的。 reduce 函数接受T 值的数组,并应以相同的T 格式返回单个值。 T 的格式由您的地图函数定义。您的 reduce 函数只返回 values 数组中的第一项,它始终是 T 类型。这就是它起作用的原因:)

您似乎走在正确的轨道上。我做了一些实验,您似乎无法从 map 函数中执行 db.collection.save(),但您可以从 reduce 函数中执行此操作。您的地图功能应该简单地构建您需要的文档格式:

function map() {
  emit(this._id, { _id: this.id, heading: this.title, body: this.content });
}

map 函数重用了原始文档的 ID。这应该防止任何重新减少步骤,因为没有值将共享相同的键。

reduce 函数可以简单地返回null。但此外,您可以将值写入单独的集合

function reduce(key, values) {
  db.result.save(values[0]);

  return null;
}

现在db.result 应该包含转换后的文档,而临时集合中没有任何额外的 map-reduce 噪音。我实际上并没有在大量数据上对此进行过测试,但这种方法应该利用 map-reduce 函数的并行执行。

【讨论】:

  • 这种方式花费了 523 秒,最终得到了我想要的集合,而我在问题中描述的 hackish 方式需要 319 秒。不幸的是,我不能只打电话给db.coll.mapReduce(myMapperFunc, null, {'out': 'output'})。我认为 reduce 能够批量保存/插入一整套项目;我认为这里的瓶颈是每个 reduce 中调用的save()
  • @chbrown:是的,save() 对每个文档执行两次;标准减少保存到临时集合,显式保存到单独的集合。只是好奇,这个解决方案真的比使用单个光标更快吗?
  • 大家好,我们在处理大型数据集时遇到了类似的问题,并且由于在 reduce 中数组连接和 rruning 大型文档不起作用,我们遵循上述将文档保存在单独的集合中并返回的方法减少为空。它工作正常,但是当我们在运行 mapreduce 时执行任何其他操作时,db 会被挂起。有没有更好的方法。
【解决方案3】:

当您访问 mongo shell 时,它会接受一些 Javascript 命令,然后就更简单了:

map = function(item){
        db.result.insert(item);
}

db.collection.find().forEach(map);

【讨论】:

    【解决方案4】:
    【解决方案5】:

    我遇到了同样的情况。我能够通过 Mongo 查询和投影来实现这一点。见Mongo Query

    【讨论】:

      猜你喜欢
      • 2013-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多