2011-08-18 3 views
2

프로덕션 데이터베이스에서 리팩토링을하고 있으며 일부 이름을 변경해야합니다. mongodb의 버전은 1.8.0입니다. C# 드라이버를 사용하여 데이터베이스 리팩토링을 수행합니다. 배열에있는 복잡한 유형의 필드의 이름을 바꿀 때 문제가 발생했습니다. 예를 들어배열에있는 복잡한 유형의 필드 이름 바꾸기

나는 그런 문서가 :

내가 예를 들어, NestedField3NestedField2의 이름을 바꿀 필요
FoobarCollection: 

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

. 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]; } 
     } 
    } 
} 

중첩 수준 내가 '$'기호를 사용 분리하기 - 몽고의 콜렉션 이름에 금지되어있는 유일한 기호. 이 코드는 수집 schoolsFoobarTypesCustom 속성에 찾을 수

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

: 사용법은 다음과 같이 할 수 있습니다. 배열과 같은 복잡한 유형이 될 수 있습니다. 그런 다음 FoobarDefaultName 속성을 모두 찾습니다 (FoobarTypesCustom이 배열이면 반복됩니다). 이름을 FoobarName으로 바꿉니다. 중첩 수준 및 중첩 배열 수는 중요하지 않습니다.

+0

안녕하세요 이것은 정확하게 찾고 있습니다! 고맙습니다 ! 데이터 유형을 업데이트하는 코드가 있습니까? – user636525

+1

나는 현재 일반적인 해결책이 없다. 그러나 구체적인 예를 들어보십시오. 나는 자바 스크립트 실행을 사용하여 그것을한다. 왜냐하면, 제 생각에는 C#과 mondodb C# 드라이버만을 사용해서는 안됩니다. 당신은 예를 찾을 수 있습니다 [여기] (http://stackoverflow.com/a/11726630/459485). – Dao

+0

이것은 희소 컬렉션이 있지만 생명의 은인 이었기 때문에 if (document == BsonNull.Value) {return; } 제대로 작동하려면 RenameDirect 맨 위에. – dariusriggins