重命名位于数组中的复杂类型的字段
我正在对生产数据库进行重构,需要进行一些重命名。 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”的匹配项。
据我了解,简单地使用 Update.Rename()
不会给出结果,因为正如文档所说“重命名 - 不会扩展数组以查找旧字段名称的匹配项”
我应该使用什么 C# 代码写入将 NestedField2
重命名为 NestedField3
?
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: "" },
...
]
}
I Need to rename NestedField2
into NestedField3
, for example.
MongoDB documentation says:
$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'.
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"
What C# code I should write to rename NestedField2
into NestedField3
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我已经实现了特殊类型来重命名 MongoDB 中的任意字段。就是这样:
用于保留路径段的辅助类型:
为了分隔嵌套级别,我使用“$”符号 - 这是 mongo 中唯一禁止用于集合名称的符号。
用法可以是这样的:
此代码将在集合
, ' '}) .Split(new [] {'schools
FoobarTypesCustom
属性中找到。它可以是复杂类型,也可以是数组。然后将找到所有FoobarDefaultName
属性(如果FoobarTypesCustom
是数组,那么它将迭代它)并将其重命名为FoobarName
。嵌套级别和嵌套数组的数量并不重要。为了分隔嵌套级别,我使用“$”符号 - 这是 mongo 中唯一禁止用于集合名称的符号。
用法可以是这样的:
此代码将在集合
: "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 } } } }schools
FoobarTypesCustom
属性中找到。它可以是复杂类型,也可以是数组。然后将找到所有FoobarDefaultName
属性(如果FoobarTypesCustom
是数组,那么它将迭代它)并将其重命名为FoobarName
。嵌套级别和嵌套数组的数量并不重要。用于保留路径段的辅助类型:
为了分隔嵌套级别,我使用“$”符号 - 这是 mongo 中唯一禁止用于集合名称的符号。
用法可以是这样的:
此代码将在集合
}, 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]; } } } }schools
FoobarTypesCustom
属性中找到。它可以是复杂类型,也可以是数组。然后将找到所有FoobarDefaultName
属性(如果FoobarTypesCustom
是数组,那么它将迭代它)并将其重命名为FoobarName
。嵌套级别和嵌套数组的数量并不重要。为了分隔嵌套级别,我使用“$”符号 - 这是 mongo 中唯一禁止用于集合名称的符号。
用法可以是这样的:
此代码将在集合
: "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 } } } }schools
FoobarTypesCustom
属性中找到。它可以是复杂类型,也可以是数组。然后将找到所有FoobarDefaultName
属性(如果FoobarTypesCustom
是数组,那么它将迭代它)并将其重命名为FoobarName
。嵌套级别和嵌套数组的数量并不重要。用于保留路径段的辅助类型:
为了分隔嵌套级别,我使用“$”符号 - 这是 mongo 中唯一禁止用于集合名称的符号。
用法可以是这样的:
此代码将在集合
schools
FoobarTypesCustom
属性中找到。它可以是复杂类型,也可以是数组。然后将找到所有FoobarDefaultName
属性(如果FoobarTypesCustom
是数组,那么它将迭代它)并将其重命名为FoobarName
。嵌套级别和嵌套数组的数量并不重要。I have implemented special type to do renaming of arbitrary field in MongoDB. Here is it:
And helper type to keep path segments:
To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo.
Usage can be something like this:
This code will find in collection
, ' '}) .Split(new [] {'schools
FoobarTypesCustom
property. It can be as complex type so array. Then will find allFoobarDefaultName
properties (ifFoobarTypesCustom
is array then it will iterate through it) and rename it toFoobarName
. Nesting levels and number of nested arrays no matters.To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo.
Usage can be something like this:
This code will find in collection
: "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 } } } }schools
FoobarTypesCustom
property. It can be as complex type so array. Then will find allFoobarDefaultName
properties (ifFoobarTypesCustom
is array then it will iterate through it) and rename it toFoobarName
. Nesting levels and number of nested arrays no matters.And helper type to keep path segments:
To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo.
Usage can be something like this:
This code will find in collection
}, 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]; } } } }schools
FoobarTypesCustom
property. It can be as complex type so array. Then will find allFoobarDefaultName
properties (ifFoobarTypesCustom
is array then it will iterate through it) and rename it toFoobarName
. Nesting levels and number of nested arrays no matters.To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo.
Usage can be something like this:
This code will find in collection
: "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 } } } }schools
FoobarTypesCustom
property. It can be as complex type so array. Then will find allFoobarDefaultName
properties (ifFoobarTypesCustom
is array then it will iterate through it) and rename it toFoobarName
. Nesting levels and number of nested arrays no matters.And helper type to keep path segments:
To separate nest levels I use '$' sign - the only sign that is forbidden for collection names in mongo.
Usage can be something like this:
This code will find in collection
schools
FoobarTypesCustom
property. It can be as complex type so array. Then will find allFoobarDefaultName
properties (ifFoobarTypesCustom
is array then it will iterate through it) and rename it toFoobarName
. Nesting levels and number of nested arrays no matters.