Monday, 15 August 2011

mongodb - Rename field of complex type that is located in array -



mongodb - Rename field of complex type that is located in array -

i'm doing refactoring on production database , need create renamings. version of mongodb 1.8.0. utilize c# driver refactoring of database. have faced problem when seek rename field of complex type located in array.

for illustration have such document:

foobarcollection: { field1: "", field2: [ { nestedfield1: "", nestedfield2: "" }, { nestedfield1: "", nestedfield2: "" }, ... ] }

i need rename nestedfield2 nestedfield3, example. mongodb documentation says:

$rename

version 1.7.2+ only.

{ $rename : { old_field_name : new_field_name } } renames field name 'old_field_name' 'new_field_name'. does not expand arrays find match 'old_field_name'.

as understand, using update.rename() wouldn't give result, because documentation says "rename - doesn't expand arrays find match old field name"

what c# code should write rename nestedfield2 nestedfield3?

i have implemented special type renaming of arbitrary field in mongodb. here 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, in array. separate nest level '$': "foofield1$foofieldnested$foofieldnestednested"</param> /// <param name="newfieldname">specify field name without path it: "newfieldname", 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); // document modified in memory - replace old document 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 maintain 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 '$'. 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); homecoming pathsegments.tolist(); } public ienumerator<string> getenumerator() { homecoming segments.getenumerator(); } ienumerator ienumerable.getenumerator() { homecoming getenumerator(); } public string this[int index] { { homecoming segments[index]; } } } }

to separate nest levels utilize '$' sign - sign forbidden collection names in mongo. usage can this:

mongodbrefactorer mongodbrefactorer = new mongodbrefactorer(mongo.database); mongodbrefactorer.renamefield("schools", "foobartypescustom$foobardefaultname", "foobarname");

this code find in collection schools foobartypescustom property. can complex type array. find foobardefaultname properties (if foobartypescustom array iterate through it) , rename foobarname. nesting levels , number of nested arrays no matters.

mongodb mongodb-csharp

No comments:

Post a Comment