重命名数组中复杂类型的字段 [英] Rename field of complex type that is located in array

查看:75
本文介绍了重命名数组中复杂类型的字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在对生产数据库进行重构,需要进行一些重命名. mongodb的版本是1.8.0.我使用C#驱动程序来进行数据库重构.当我尝试重命名位于数组中的复杂类型的字段时遇到了问题.

I'm doing refactoring on production database and need to make some renamings. Version of mongodb is 1.8.0. I use C# driver to do refactoring of database. Have faced with problem when I try to rename field of complex type that is located in array.

例如,我有这样的文件:

For example I have such document:

FoobarCollection:

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

例如,我需要将NestedField2重命名为NestedField3. MongoDB文档说:

I Need to rename NestedField2 into NestedField3, for example. MongoDB documentation says:

$ rename

$rename

仅1.7.2+版.

{$ rename:{old_field_name:new_field_name}} 将名称为"old_field_name"的字段重命名为"new_field_name". 不扩展数组来查找"old_field_name"的匹配项.

{ $rename : { old_field_name : new_field_name } } Renames the field with name 'old_field_name' to 'new_field_name'. Does not expand arrays to find a match for 'old_field_name'.

据我了解,仅使用Update.Rename()不会产生结果,因为如文档所述,重命名-不会扩展数组以查找与旧字段名称的匹配项"

As I understand, simply using Update.Rename() wouldn't give result, because as documentation says "rename - doesn't expand arrays to find a match for old field name"

我应该写什么C#代码将NestedField2重命名为NestedField3?

What C# code I should write to rename NestedField2 into NestedField3?

推荐答案

我实现了特殊类型来在MongoDB中重命名任意字段.就是这样:

I have implemented special type to do renaming of arbitrary field in MongoDB. Here is it:

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
            }
        }
    }
}

和帮助程序类型以保留路径段:

And helper type to keep path segments:

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中唯一禁止集合名称使用的符号. 用法可以是这样的:

To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo. Usage can be something like this:

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

此代码将在集合schools FoobarTypesCustom属性中找到.它可以像数组一样复杂.然后将找到所有FoobarDefaultName属性(如果FoobarTypesCustom是数组,则将对其进行迭代)并将其重命名为FoobarName.嵌套级别和嵌套数组的数量无关紧要.

This code will find in collection schools FoobarTypesCustom property. It can be as complex type so array. Then will find all FoobarDefaultName properties (if FoobarTypesCustom is array then it will iterate through it) and rename it to FoobarName. Nesting levels and number of nested arrays no matters.

这篇关于重命名数组中复杂类型的字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆