MongoDB重命名数组中的数据库字段

Posted

技术标签:

【中文标题】MongoDB重命名数组中的数据库字段【英文标题】:MongoDB rename database field within array 【发布时间】:2012-02-25 17:38:54 【问题描述】:

我需要在此重命名indentifier

 "general" : 
   "files" : 
     "file" : 
      [  
          "version" : 
            "software_program" : "MonkeyPlus",      
             "indentifier" : "6.0.0" 
           
         
      ] 
     
   

我试过了

db.nrel.component.update(
  ,
   $rename: 
    "general.files.file.$.version.indentifier" : "general.files.file.$.version.identifier"
   ,
  false, true
)

但它返回:$rename source may not be dynamic array

【问题讨论】:

$rename 不展开数组,doc @Alexander Azarov,有什么解决这个问题的想法吗?我听说有人复制到 $rename 可以去的字段... 就我个人而言,我正在编写遍历集合并进行迁移的脚本 如果您希望使用数据库命令执行此操作:How to rename a field inside an array with database commands? 【参考方案1】:

如文档中所述,无法使用单个命令直接重命名数组中的字段。您唯一的选择是遍历您的集合文档,阅读它们并使用$unset old/$set new 操作更新每个文档。

【讨论】:

卡在 MongoDB... 这样的事情让我很难过 :( 我不反对。这是我想象的相对容易实现的目标。 此答案已过时,请参阅 Sudhesh 的答案 which IMO is best【参考方案2】:

对于它的价值,虽然听起来很糟糕,但解决方案实际上非常简单。这当然取决于你有多少记录。但这是我的例子:

db.Setting.find( 'Value.Tiers.0.AssetsUnderManagement':  $exists: 1  ).snapshot().forEach(function(item)
    
    for(i = 0; i != item.Value.Tiers.length; ++i)
    
        item.Value.Tiers[i].Aum = item.Value.Tiers[i].AssetsUnderManagement;
        delete item.Value.Tiers[i].AssetsUnderManagement;
    
    
    db.Setting.update(_id: item._id, item);
);

我遍历了我的集合,其中找到了数组并找到了“错误”的名称。然后我遍历子集合,设置新值,删除旧值,并更新整个文档。这是相对无痛的。当然,我只有几万行要搜索,其中只有几十行符合条件。

不过,我希望这个答案对某人有所帮助!

编辑:在查询中添加了snapshot()。在 cmets 中了解原因。

在从数据库中检索任何文档之前,您必须将snapshot() 应用于游标。 您只能将 snapshot() 与非分片集合一起使用。

从 MongoDB 3.4 中删除了 snapshot() 函数。所以如果使用 Mongo 3.4+ ,上面的例子应该去掉 snapshot() 函数。

【讨论】:

恕我直言,这应该是公认的答案。我在几个包含多达 1200 万份文档的集合上执行了这种方法,效果非常好! 当然。这很好用。可惜 MongoDB 还不能原生地包含这种行为。 最好使用snapshot():db.Setting.find( 'Value.Tiers.0.AssetsUnderManagement': $exists: 1 ).snapshot().forEach(...)否则你可能会陷入无限循环。另见docs.mongodb.org/v3.0/reference/method/cursor.snapshot 对我来说是新的。感谢您提供详细信息,@PatrycjaK!更新我的答案。 @Andrew Samuelsen 我认为这应该是选择的答案 - 一个解决“按设计”问题的脚本【参考方案3】:

我遇到了类似的问题。在我的情况下,我发现以下内容要容易得多:

    我将集合导出为 json:
mongoexport --db mydb --collection modules --out modules.json

    我使用我喜欢的文本编辑实用程序对 json 进行了查找和替换。

    我重新导入了编辑后的文件,同时删除了旧集合:

mongoimport --db mydb --collection modules --drop --file modules.json

【讨论】:

我有点怀疑这种方法在大型数据集上的效果如何。假设我的数据库是 5gb,这个过程听起来很慢。或者我没有使用正确的文本编辑工具 =- 还要注意实时数据库。如果在执行这些步骤所需的时间内添加/修改了记录,则更改将丢失。 如果您有一些只能在笔记本电脑上运行但如果您在生产环境中工作则不是可行的解决方案的解决方案。【参考方案4】:

我也想重命名数组中的属性:并且我使用了 thaht

db.getCollection('YourCollectionName').find().snapshot().forEach(function(a)
    a.Array1.forEach(function(b)
        b.Array2.forEach(function(c)
            c.NewPropertyName = c.OldPropertyName;
            delete c["OldPropertyName"];                   
        );
    );
    db.getCollection('YourCollectionName').save(a)  
);

【讨论】:

snapshot is deprecated 从 MongoDB 4.0 开始。 您也可以添加 if(b.hasOwnProperty("xxx")) 来验证属性或数组是否存在。【参考方案5】:

Mongo 4.2开始,db.collection.update()可以接受一个聚合管道,最后允许根据自己的值更新一个字段:

//  general:  files:  file: [
//    version:  software_program: "MonkeyPlus", indentifier: "6.0.0"  
// ]   
db.collection.updateMany(
  ,
  [ $set:  "general.files.file": 
       $map: 
         input: "$general.files.file",
         as: "file",
         in: 
           version: 
             software_program: "$$file.version.software_program",
             identifier: "$$file.version.indentifier" // fixing the typo here
           
         
       
  ]
)
//  general:  files:  file: [
//    version:  software_program: "MonkeyPlus", identifier: "6.0.0"  
// ]   

从字面上看,这个updates 通过(重新)$setting 记录"general.files.file" 数组,通过$mapping 其"file" 元素in 一个包含相同"software_program" 字段的"version" 对象和重命名的 "identifier" 字段包含以前的 "indentifier" 的值。


一些额外的细节:

第一部分 是匹配查询,过滤要更新的文档(在本例中为所有文档)。

第二部分[ $set: "general.files.file": ... ]是更新聚合管道(注意方括号表示使用聚合管道):

$set 是一个新的聚合运算符,在这种情况下替换了 "general.files.file" 数组的值。 使用$map 操作,我们将"general.files.file" 数组中的所有元素替换为基本相同的元素,但使用"identifier" 字段而不是"indentifier"input 是要映射的数组。 as 是赋予循环元素的变量名 in 是应用于元素的实际转换。在这种情况下,它将元素替换为由"software_program""identifier" 字段组成的"version" 对象。这些字段是通过使用$$file.xxxx 表示法(其中file 是给来自as 部分的元素的名称)提取它们以前的值来填充的。

【讨论】:

您手动保留software_program。但是如果版本字段有很多子字段,并且可能是每个文档中的不同字段呢? 回答@HaidangNguyen 的问题,一般来说是一个好习惯 - 在$mapin 中使用$mergeObjects :) 在上面的例子中:in: $mergeObjects: ['$$file', / * changes here */] 。跨度> 【参考方案6】:

我的建议是这个:

db.nrel.component.aggregate([
    $unwind: "$general.files.file" ,
   
      $set: 
         "general.files.file.version.identifier": 
            $ifNull: ["$general.files.file.version.indentifier", "$general.files.file.version.identifier"]
         
      
   ,
    $unset: "general.files.file.version.indentifier" ,
    $set:  "general.files.file": ["$general.files.file"]  ,
    $out: "nrel.component"  // carefully - it replaces entire collection.
])

但是,这仅适用于数组 general.files.file 仅具有单个文档的情况。很可能并非总是如此,那么您可以使用这个:

db.nrel.componen.aggregate([
    $unwind: "$general.files.file" ,
   
      $set: 
         "general.files.file.version.identifier": 
            $ifNull: ["$general.files.file.version.indentifier", "$general.files.file.version.identifier"]
         
      
   ,
    $unset: "general.files.file.version.indentifier" ,
    $group:  _id: "$_id", general_new:  $addToSet: "$general.files.file"   ,
    $set:  "general.files.file": "$general_new"  ,
    $unset: "general_new" ,
    $out: "nrel.component"  // carefully - it replaces entire collection.
])

【讨论】:

【参考方案7】:

使用聚合(Mongo 4.0+)的最简单和最短的解决方案。

db.myCollection.aggregate([
  
    $addFields: 
      "myArray.newField": $arrayElemAt: ["$myArray.oldField", 0] 
    
  ,
  $project:  "myArray.oldField": false,
  $out: db: "myDb", coll: "myCollection"
])

上面提到的使用forEach循环的问题是当集合很大时性能很差。

【讨论】:

【参考方案8】:

我不得不面对相同架构的问题。因此,此查询对于想要重命名嵌入式数组中的字段的人会有所帮助。

db.getCollection("sampledocument").updateMany(, [
  
    $set: 
      "general.files.file": 
        $map: 
          input: "$general.files.file",
          in: 
            version: 
              $mergeObjects: [
                "$$this.version",
                 identifer: "$$this.version.indentifier" ,
              ],
            ,
          ,
        ,
      ,
    ,
  ,
   $unset: "general.files.file.version.indentifier" ,
]);

【讨论】:

这是迄今为止最简单、最优雅的解决方案,应该是公认的答案

以上是关于MongoDB重命名数组中的数据库字段的主要内容,如果未能解决你的问题,请参考以下文章

mongodb aggregate

重命名数组中的子文档字段

如何使用 pymongo 在 mongodb 中重命名父字段名称和嵌套字段值?

MongoDB update集合字段名修改 $rename

重命名数组 PHP 中的键

对数据库中的表或字段重命名