2011-08-18 60 views
2

我正在對生產數據庫進行重構並需要進行一些重構。 mongodb的版本是1.8.0。我使用C#驅動程序來重構數據庫。當我嘗試重命名位於數組中的複雜類型的字段時遇到問題。位於數組中的複雜類型的重命名字段

例如,我有這樣的文件:

FoobarCollection: 

{ 
    Field1: "", 
    Field2: [ 
    { NestedField1: "", NestedField2: "" }, 
    { NestedField1: "", NestedField2: "" }, 
    ... 
    ] 
} 

我需要NestedField2重命名爲NestedField3,例如。 MongoDB的文件說:

$重命名

版本1.7.2+只。

{$ rename:{old_field_name:new_field_name}} 將名稱爲'old_field_name'的字段重命名爲'new_field_name'。 不擴展數組以找到'old_field_name'的匹配項

據我瞭解,只要使用Update.Rename()不會放棄的結果,因爲文件說,「重命名 - 不展開陣列找到老字段名稱匹配」

什麼C#代碼,我應該寫將NestedField2重命名爲NestedField3

回答

2

我已經實現了特殊類型來重命名MongoDB中的任意字段。下面是它:

using System.Linq; 
using MongoDB.Bson; 
using MongoDB.Driver; 

namespace DatabaseManagementTools 
{ 
    public class MongoDbRefactorer 
    { 
     protected MongoDatabase MongoDatabase { get; set; } 

     public MongoDbRefactorer(MongoDatabase mongoDatabase) 
     { 
      MongoDatabase = mongoDatabase; 
     } 

     /// <summary> 
     /// Renames field 
     /// </summary> 
     /// <param name="collectionName"></param> 
     /// <param name="oldFieldNamePath">Supports nested types, even in array. Separate nest level with '$': "FooField1$FooFieldNested$FooFieldNestedNested"</param> 
     /// <param name="newFieldName">Specify only field name without path to it: "NewFieldName", but not "FooField1$NewFieldName"</param> 
     public void RenameField(string collectionName, string oldFieldNamePath, string newFieldName) 
     { 
      MongoCollection<BsonDocument> mongoCollection = MongoDatabase.GetCollection(collectionName); 
      MongoCursor<BsonDocument> collectionCursor = mongoCollection.FindAll(); 

      PathSegments pathSegments = new PathSegments(oldFieldNamePath); 

      // Rename field in each document of collection 
      foreach (BsonDocument document in collectionCursor) 
      { 
       int currentSegmentIndex = 0; 
       RenameField(document, pathSegments, currentSegmentIndex, newFieldName); 

       // Now document is modified in memory - replace old document with new in mongo: 
       mongoCollection.Save(document); 
      } 
     } 

     private void RenameField(BsonValue bsonValue, PathSegments pathSegments, int currentSegmentIndex, string newFieldName) 
     { 
      string currentSegmentName = pathSegments[currentSegmentIndex]; 

      if (bsonValue.IsBsonArray) 
      { 
       var array = bsonValue.AsBsonArray; 
       foreach (var arrayElement in array) 
       { 
        RenameField(arrayElement.AsBsonDocument, pathSegments, currentSegmentIndex, newFieldName); 
       } 
       return; 
      } 

      bool isLastNameSegment = pathSegments.Count() == currentSegmentIndex + 1; 
      if (isLastNameSegment) 
      { 
       RenameDirect(bsonValue, currentSegmentName, newFieldName); 
       return; 
      } 

      var innerDocument = bsonValue.AsBsonDocument[currentSegmentName]; 
      RenameField(innerDocument, pathSegments, currentSegmentIndex + 1, newFieldName); 
     } 

     private void RenameDirect(BsonValue document, string from, string to) 
     { 
      BsonElement bsonValue; 
      bool elementFound = document.AsBsonDocument.TryGetElement(from, out bsonValue); 
      if (elementFound) 
      { 
       document.AsBsonDocument.Add(to, bsonValue.Value); 
       document.AsBsonDocument.Remove(from); 
      } 
      else 
      { 
       // todo: log missing elements 
      } 
     } 
    } 
} 

和輔助型保持路徑段:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 

namespace DatabaseManagementTools 
{ 
    public class PathSegments : IEnumerable<string> 
    { 
     private List<string> Segments { get; set; } 

     /// <summary> 
     /// Split segment levels with '$'. For example: "School$CustomCodes" 
     /// </summary> 
     /// <param name="pathToParse"></param> 
     public PathSegments(string pathToParse) 
     { 
      Segments = ParseSegments(pathToParse); 
     } 

     private static List<string> ParseSegments(string oldFieldNamePath) 
     { 
      string[] pathSegments = oldFieldNamePath.Trim(new []{'$', ' '}) 
       .Split(new [] {'$'}, StringSplitOptions.RemoveEmptyEntries); 

      return pathSegments.ToList(); 
     } 

     public IEnumerator<string> GetEnumerator() 
     { 
      return Segments.GetEnumerator(); 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return GetEnumerator(); 
     } 

     public string this[int index] 
     { 
      get { return Segments[index]; } 
     } 
    } 
} 

爲了區分嵌套的水平我用「$」符號 - 這是禁止在蒙戈集合名稱的唯一標誌。 用途可以是這樣的:

MongoDbRefactorer mongoDbRefactorer = new MongoDbRefactorer(Mongo.Database); 
mongoDbRefactorer.RenameField("schools", "FoobarTypesCustom$FoobarDefaultName", "FoobarName"); 

此代碼將在收集schoolsFoobarTypesCustom財產找到。它可以像數組一樣複雜。然後將找到所有FoobarDefaultName屬性(如果FoobarTypesCustom是數組,則它將遍歷它)並將其重命名爲FoobarName。嵌套層次和嵌套數組的數量沒有關係。

+0

你好這正是我一直在尋找!謝謝 !你有代碼來更新數據類型嗎? – user636525

+1

我目前沒有通用的解決方案。但有具體的例子。我使用JavaScript執行它。因爲,正如我想的那樣,僅使用清晰的C#和mondodb C#驅動程序就無法做到這一點。您可以查找示例[here](http://stackoverflow.com/a/11726630/459485)。 – Dao

+0

雖然我有一個稀疏的集合,但這是一個生命保護程序,所以我需要添加一個if(document == BsonNull.Value){return; }在RenameDirect的頂部讓它正常工作。 – dariusriggins