使用 DBCollection.mapReduce 更改 MongoDB 架构:虚假嵌套 "value" 属性

Using DBCollection.mapReduce to change MongoDB schema: spurious nested "value" attribute

我需要创建一个数据库补丁,将 MongoDB 集合的所有元素更新为新格式。 例如,粗略地简化一下,旧格式有这样的文件:

{
  "_id" : ObjectId("572a7f30200cd11355083cd9"),
  "_class" : "Domain",
  "oldAttribute": 123
}

新格式需要这样的文件:

{
  "_id" : ObjectId("572a7f30200cd11355083cd9"),
  "_class" : "Domain",
  "newAttribute": 123
}

我正在使用 MongoDB 的 Java API,数据库补丁也必须用 Java 编写,而不是写一堆Java 通过先读取文档然后将它们写回来修改文档的代码,我想我可以利用 DBCollection.mapReduce()MapReduceCommand.OutputType.REPLACE 来用一小段 Java直接在MongoDB里面写脚本,像这样:

myCollection.mapReduce(map, reduce, "myCollection", MapReduceCommand.OutputType.REPLACE, null);

对于 map 函数,我传递了如下内容:

function () {
  var copy = {'_id': this._id, '_class': this._class, newAttribute: this.oldAttribute};
  emit(this._id, copy);
}

reduce 函数在技术上应该永远不会被调用,因为键是唯一的,所以我只是在那里传递一个虚拟函数。

这似乎可行,但存在一个问题:应用 map-reduce 后,集合中的所有文档现在都有一个嵌套的 value 属性:

{
  "_id": { "$oid" : "56c2371a200cd11088252111"},
  "value":
  {
    "_id": { "$oid" : "56c2371a200cd11088252111"},
    "_class": "Domain",
    "newAttribute": 123.0
  }
}

(此输出是从 Java 控制台粘贴的,因此格式略有不同)

简而言之,我的问题是:如何摆脱嵌套的 value 属性并使所有属性(包括 _id)位于顶层?

如果您想就地修改文档,可以使用更新 $rename modifier. Running update with multi:true 将更新集合中的每个文档。

如果您希望更改出现 "atomic" 并且集合不分片,您可以使用 aggregate$out 以及此管道:

db.collection.aggregate([ 
    {$project:{_class:1, newAttribute:"$oldAttribute"}}, 
    {$out:"collection"}
]) 

$out 提供与原始集合名称相同的名称将用它的新转换版本替换该集合。

如果你想创建新版本的集合而不删除旧版本,那么只需给$out一个新的集合名称即可。