我正在对生产数据库进行重构,需要进行一些重命名。 mongodb的版本是1.8.0。我使用 C# 驱动程序来重构数据库。当我尝试重命名位于数组中的复杂类型的字段时遇到问题。
例如我有这样的文件:
FoobarCollection:
{
Field1: "",
Field2: [
{ NestedField1: "", NestedField2: "" },
{ NestedField1: "", NestedField2: "" },
...
]
}
例如,我需要将 NestedField2
重命名为 NestedField3
。
MongoDB 文档说:
$rename
Version 1.7.2+ only.
{ $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()
不会给出结果,因为正如文档所说“重命名 - 不会扩展数组以找到旧字段名称的匹配项”
我应该编写什么 C# 代码来将 NestedField2
重命名为 NestedField3
?
最佳答案
我已经实现了特殊类型来重命名 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
。嵌套级别和嵌套数组的数量无关紧要。
关于mongodb - 重命名位于数组中的复杂类型的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7109654/