【问题标题】:Rename field of complex type that is located in array重命名位于数组中的复杂类型的字段
【发布时间】:2011-08-18 15:04:21
【问题描述】:

我正在对生产数据库进行重构,需要进行一些重命名。 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

【问题讨论】:

    标签: mongodb mongodb-.net-driver


    【解决方案1】:

    我已经实现了特殊类型来重命名 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]; }
            }
        }
    }
    

    为了分隔嵌套级别,我使用“$”符号 - mongo 中集合名称中唯一禁止使用的符号。 用法可以是这样的:

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

    此代码将在集合 schools FoobarTypesCustom 属性中找到。它可以像数组这样复杂的类型。然后将找到所有FoobarDefaultName 属性(如果FoobarTypesCustom 是数组,那么它将遍历它)并将其重命名为FoobarName。嵌套级别和嵌套数组的数量无关紧要。

    【讨论】:

    • 您好,这正是我要找的!谢谢 !你也有更新数据类型的代码吗?
    • 我暂时没有通用的解决方案。但有具体的例子。我使用javascript执行来做到这一点。因为,我认为,仅使用 clear C# 和 mondodb C# 驱动程序是不可能做到这一点的。您可以查看例如here
    • 这是一个救命稻草,虽然我有一个稀疏的集合,所以我需要添加一个检查 if (document == BsonNull.Value) { return; } 在 RenameDirect 的顶部使其正常工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    • 2015-10-19
    相关资源
    最近更新 更多