diff --git a/Mongo.Migration/ApplicationLogging.cs b/Mongo.Migration/ApplicationLogging.cs new file mode 100644 index 0000000..2fbbdba --- /dev/null +++ b/Mongo.Migration/ApplicationLogging.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Logging; + +public class ApplicationLogging +{ + private static ILoggerFactory _factory = null; + + public static void ConfigureLogger(ILoggerFactory factory) + { + factory.AddFile("logs/migration/log-migration.log"); + } + + /// + /// Log Factory instance + /// + public static ILoggerFactory LoggerFactory + { + get + { + if (_factory == null) + { + _factory = new LoggerFactory(); + ConfigureLogger(_factory); + } + return _factory; + } + } + + /// + /// Get a ILogger instance to be used in migrations + /// + /// ILogger instance + public static ILogger CreateLogger() => LoggerFactory.CreateLogger("logger"); +} \ No newline at end of file diff --git a/Mongo.Migration/Extensions/EnumerableExtensions.cs b/Mongo.Migration/Extensions/EnumerableExtensions.cs index bae1372..7c70996 100644 --- a/Mongo.Migration/Extensions/EnumerableExtensions.cs +++ b/Mongo.Migration/Extensions/EnumerableExtensions.cs @@ -48,5 +48,41 @@ internal static IDictionary> ToMigrationDi return dictonary; } + + internal static IEnumerable CheckForDuplicates(this IEnumerable list) + { + var uniqueHashes = new HashSet(); + foreach (var element in list) + { + var version = element.Version.ToString(); + var typeName = element.GetType().Name; + + if (uniqueHashes.Add(typeName+version)) + continue; + + throw new DuplicateVersionException(typeName, element.Version); + } + + return list; + } + + internal static IDictionary> ToMigrationDictionary( + this IEnumerable migrations) + { + var dictonary = new Dictionary>(); + var types = from m in migrations select m.Type; + + foreach (var type in types) + { + if (dictonary.ContainsKey(type)) + continue; + + var uniqueMigrations = + migrations.Where(m => m.Type == type).CheckForDuplicates().OrderBy(m => m.Version).ToList(); + dictonary.Add(type, uniqueMigrations); + } + + return dictonary; + } } } \ No newline at end of file diff --git a/Mongo.Migration/Migrations/AdvancedMigration.cs b/Mongo.Migration/Migrations/AdvancedMigration.cs new file mode 100644 index 0000000..36064fb --- /dev/null +++ b/Mongo.Migration/Migrations/AdvancedMigration.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Mongo.Migration.Migrations +{ + public abstract class AdvancedMigration : IAdvancedMigration + { + protected AdvancedMigration(string version, string collection) + { + Version = version; + CollectionName = collection; + } + + public string Version { get; } + + public string CollectionName { get; } + + public Type Type => typeof(AdvancedMigration); + + public virtual void Up(IMongoDatabase db) { + var bulk = new List>(); + var collection = db.GetCollection(CollectionName); + + collection.FindSync(_ => true).ToList().ForEach(document => + { + Up(document); + + var update = new ReplaceOneModel( + new BsonDocument {{"_id", document["_id"]}}, + document + ); + + bulk.Add(update); + }); + + if (bulk.Count > 0) + { + collection.BulkWrite(bulk); + } + } + + public virtual void Down(IMongoDatabase db) { + var bulk = new List>(); + var collection = db.GetCollection(CollectionName); + + collection.FindSync(_ => true).ToList().ForEach(document => + { + Down(document); + + var update = new ReplaceOneModel( + new BsonDocument {{"_id", document["_id"]}}, + document + ); + + bulk.Add(update); + }); + + if (bulk.Count > 0) + { + collection.BulkWrite(bulk); + } + } + + public abstract void Up(BsonDocument document); + + public abstract void Down(BsonDocument document); + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/AdvancedMigrationRunner.cs b/Mongo.Migration/Migrations/AdvancedMigrationRunner.cs new file mode 100644 index 0000000..06cc6eb --- /dev/null +++ b/Mongo.Migration/Migrations/AdvancedMigrationRunner.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using Mongo.Migration.Migrations.Locators; +using Mongo.Migration.Services; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Mongo.Migration.Migrations +{ + internal class AdvancedMigrationRunner : IAdvancedMigrationRunner + { + private ILogger _logger; + public IAdvancedMigrationLocator _migrationLocator { get; } + + private readonly IVersionService _versionService; + + private IMongoDatabase _db; + + private IMongoCollection _migrationshistory; + + public AdvancedMigrationRunner(IAdvancedMigrationLocator migrationLocator, IVersionService versionService) + { + _migrationLocator = migrationLocator; + _versionService = versionService; + _logger = ApplicationLogging.CreateLogger(); + } + + public void Run(IMongoDatabase db, string runningVersion) + { + _logger.LogInformation($"Starting the migrations ..."); + + _db = db; + _migrationshistory = db.GetCollection("_migrationshistory"); + + var migrations = _migrationLocator.GetMigrations(typeof(AdvancedMigration)) ?? Enumerable.Empty(); + var migrationsToDowngrade = new List(); + + foreach (var migration in migrations) + { + + var migrationsInDb = _migrationshistory.FindSync(CreateQueryForMigration(migration.GetType().ToString())).ToList(); + + if (migrationsInDb.Count() > 0) + { + foreach (var document in migrationsInDb) + { + if (document["productVersion"].ToString().CompareTo(runningVersion) <= 0) + continue; + else if (document["productVersion"].ToString().CompareTo(runningVersion) > 0) + { + migrationsToDowngrade.Add(migration); + } + } + } + else + { + if (runningVersion.CompareTo(migration.Version) >= 0) + { + try + { + _logger.LogInformation("Starting the migration Up: {0}:{1} ", migration.GetType().ToString(), migration.Version); + + migration.Up(_db); + _migrationshistory.InsertOne(new BsonDocument { { "migrationId", migration.GetType().ToString() }, { "productVersion", migration.Version } }); + + _logger.LogInformation("Successful migration Up: {0}:{1} ", migration.GetType().ToString(), migration.Version); + } + catch (Exception e) + { + _logger.LogError(e, "Error on migration Up {0}:{1} ", migration.GetType().ToString(), migration.Version); + } + } + } + } + + migrationsToDowngrade.Reverse(); + foreach (var migration in migrationsToDowngrade) + { + try + { + _logger.LogInformation("Starting the migration Down: {0}:{1} ", migration.GetType().ToString(), migration.Version); + + migration.Down(_db); + _migrationshistory.DeleteOne(Builders.Filter.Eq("migrationId", migration.GetType().ToString())); + + _logger.LogInformation("Successful migration Down: {0}:{1} ", migration.GetType().ToString(), migration.Version); + } + catch (Exception e) + { + _logger.LogError(e, "Error on migration Down {0}:{1} ", migration.GetType().ToString(), migration.Version); + } + } + + _logger.LogInformation($"Migrations has been done."); + } + + private FilterDefinition CreateQueryForMigration( + string type) + { + return Builders.Filter.Eq("migrationId", type); + } + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/CollectionMigrationRunner.cs b/Mongo.Migration/Migrations/CollectionMigrationRunner.cs index 8c817bc..9c88bdd 100644 --- a/Mongo.Migration/Migrations/CollectionMigrationRunner.cs +++ b/Mongo.Migration/Migrations/CollectionMigrationRunner.cs @@ -14,6 +14,8 @@ internal class CollectionMigrationRunner : ICollectionMigrationRunner { private readonly IMongoClient _client; + private IMongoDatabase _db; + private readonly ICollectionLocator _collectionLocator; private readonly string _databaseName; @@ -74,6 +76,7 @@ private CollectionMigrationRunner( public void RunAll() { + _db = _client.GetDatabase(_databaseName); var locations = _collectionLocator.GetLocatesOrEmpty(); foreach (var locate in locations) @@ -83,8 +86,7 @@ public void RunAll() var databaseName = GetDatabaseOrDefault(information); var collectionVersion = _versionService.GetCollectionVersion(type); - var collection = _client.GetDatabase(databaseName) - .GetCollection(information.Collection); + var collection = _db.GetCollection(information.Collection); var bulk = new List>(); @@ -105,6 +107,7 @@ public void RunAll() ); bulk.Add(update); + //migrationshistory.InsertOne(new BsonDocument {{"migrationId", nameof(type)}, {"productVersion", _runningVersion}}); } } } diff --git a/Mongo.Migration/Migrations/DatabaseMigrationRunner.cs b/Mongo.Migration/Migrations/DatabaseMigrationRunner.cs new file mode 100644 index 0000000..ec639cc --- /dev/null +++ b/Mongo.Migration/Migrations/DatabaseMigrationRunner.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Options; +using Mongo.Migration.Startup; +using Mongo.Migration.Startup.DotNetCore; +using MongoDB.Driver; + +namespace Mongo.Migration.Migrations +{ + internal class DatabaseMigrationRunner : IDatabaseMigrationRunner + { + private readonly IMongoClient _client; + + private readonly IAdvancedMigrationRunner _migrationRunner; + + private readonly string _databaseName; + + private readonly string _runningVersion; + + private readonly IOptions _options; + + private const int CONNECTION_CHECK_TIMEOUT = 1000; + public DatabaseMigrationRunner( + IOptions options, + IAdvancedMigrationRunner migrationRunner) + : this( + new MongoClient(options.Value.ConnectionString), + migrationRunner, + options.Value.RunningVersion) + { + _options = options; + _databaseName = options.Value.Database; + } + + public DatabaseMigrationRunner( + IMongoClient client, + IAdvancedMigrationRunner migrationRunner, + string runningVersion) + { + _runningVersion = runningVersion; + _client = client; + _migrationRunner = migrationRunner; + } + + public void RunAll() + { + _migrationRunner.Run(_client.GetDatabase(_databaseName), _runningVersion); + } + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/IAdvancedMigration.cs b/Mongo.Migration/Migrations/IAdvancedMigration.cs new file mode 100644 index 0000000..8ae0a06 --- /dev/null +++ b/Mongo.Migration/Migrations/IAdvancedMigration.cs @@ -0,0 +1,23 @@ +using System; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Mongo.Migration.Migrations +{ + public interface IAdvancedMigration + { + string Version { get; } + + string CollectionName { get; } + + Type Type { get; } + + void Up(IMongoDatabase db); + + void Down(IMongoDatabase db); + + void Up(BsonDocument document); + + void Down(BsonDocument document); + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/IAdvancedMigrationRunner.cs b/Mongo.Migration/Migrations/IAdvancedMigrationRunner.cs new file mode 100644 index 0000000..6b2a705 --- /dev/null +++ b/Mongo.Migration/Migrations/IAdvancedMigrationRunner.cs @@ -0,0 +1,12 @@ +using Mongo.Migration.Migrations.Locators; +using MongoDB.Driver; + +namespace Mongo.Migration.Migrations +{ + internal interface IAdvancedMigrationRunner + { + IAdvancedMigrationLocator _migrationLocator { get; } + + void Run(IMongoDatabase db, string runnigVersion); + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/IDatabaseMigrationRunner.cs b/Mongo.Migration/Migrations/IDatabaseMigrationRunner.cs new file mode 100644 index 0000000..d834c0a --- /dev/null +++ b/Mongo.Migration/Migrations/IDatabaseMigrationRunner.cs @@ -0,0 +1,7 @@ +namespace Mongo.Migration.Migrations +{ + internal interface IDatabaseMigrationRunner + { + void RunAll(); + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/IMigrationRunner.cs b/Mongo.Migration/Migrations/IMigrationRunner.cs index 78f5593..023e3a2 100644 --- a/Mongo.Migration/Migrations/IMigrationRunner.cs +++ b/Mongo.Migration/Migrations/IMigrationRunner.cs @@ -1,11 +1,14 @@ using System; using Mongo.Migration.Documents; +using Mongo.Migration.Migrations.Locators; using MongoDB.Bson; namespace Mongo.Migration.Migrations { internal interface IMigrationRunner { + IMigrationLocator _migrationLocator { get; } + void Run(Type type, BsonDocument document, DocumentVersion to); void Run(Type type, BsonDocument document); diff --git a/Mongo.Migration/Migrations/Locators/AdvancedMigrationLocator.cs b/Mongo.Migration/Migrations/Locators/AdvancedMigrationLocator.cs new file mode 100644 index 0000000..5e7e77a --- /dev/null +++ b/Mongo.Migration/Migrations/Locators/AdvancedMigrationLocator.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using Mongo.Migration.Documents; +using Mongo.Migration.Exceptions; +using Mongo.Migration.Extensions; + +namespace Mongo.Migration.Migrations.Locators +{ + public abstract class AdvancedMigrationLocator : IAdvancedMigrationLocator + { + private IEnumerable _assemblies; + + protected IEnumerable Assemblies => _assemblies ?? (_assemblies = GetAssemblies()); + + private IDictionary> _migrations; + + protected IDictionary> Migrations + { + get + { + if (_migrations == null) + Locate(); + + if (_migrations.NullOrEmpty()) + throw new NoMigrationsFoundException(); + + return _migrations; + } + set { _migrations = value; } + } + + public IEnumerable GetMigrations(Type type) + { + IReadOnlyCollection migrations; + Migrations.TryGetValue(type, out migrations); + + return migrations; + } + + public IEnumerable GetMigrationsGt(Type type, string version) + { + var migrations = GetMigrations(type); + + return + migrations + .Where(m => m.Version.CompareTo(version) > 0) + .ToList(); + } + + public IEnumerable GetMigrationsGtEq(Type type, string version) + { + var migrations = GetMigrations(type); + + return + migrations + .Where(m => m.Version.CompareTo(version) >= 0) + .ToList(); + } + + public DocumentVersion GetLatestVersion(Type type) + { + var migrations = GetMigrations(type); + + return migrations.Max(m => m.Version); + } + + public abstract void Locate(); + + private static IEnumerable GetAssemblies() + { + var location = AppDomain.CurrentDomain.BaseDirectory; + var path = Path.GetDirectoryName(location); + + if (string.IsNullOrWhiteSpace(path)) + throw new DirectoryNotFoundException(ErrorTexts.AppDirNotFound); + + var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); + var migrationAssemblies = Directory.GetFiles(path, "*.MongoMigrations*.dll").Select(Assembly.LoadFile); + + assemblies.AddRange(migrationAssemblies); + + return assemblies; + } + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/Locators/AdvancedTypeMigrationLocator.cs b/Mongo.Migration/Migrations/Locators/AdvancedTypeMigrationLocator.cs new file mode 100644 index 0000000..57b3081 --- /dev/null +++ b/Mongo.Migration/Migrations/Locators/AdvancedTypeMigrationLocator.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using Mongo.Migration.Extensions; + +namespace Mongo.Migration.Migrations.Locators +{ + internal class AdvancedTypeMigrationLocator : AdvancedMigrationLocator + { + public override void Locate() + { + var migrationTypes = + (from assembly in Assemblies + from type in assembly.GetTypes() + where typeof(IAdvancedMigration).IsAssignableFrom(type) && !type.IsAbstract + select type).Distinct(); + + Migrations = migrationTypes.Select(t => (IAdvancedMigration) Activator.CreateInstance(t)).ToMigrationDictionary(); + } + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/Locators/IAdvancedMigrationLocator.cs b/Mongo.Migration/Migrations/Locators/IAdvancedMigrationLocator.cs new file mode 100644 index 0000000..e7ad7cd --- /dev/null +++ b/Mongo.Migration/Migrations/Locators/IAdvancedMigrationLocator.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using Mongo.Migration.Documents; + +namespace Mongo.Migration.Migrations.Locators +{ + public interface IAdvancedMigrationLocator + { + IEnumerable GetMigrations(Type type); + + IEnumerable GetMigrationsGt(Type type, string version); + + IEnumerable GetMigrationsGtEq(Type type, string version); + + DocumentVersion GetLatestVersion(Type type); + + void Locate(); + } +} \ No newline at end of file diff --git a/Mongo.Migration/Migrations/MigrationRunner.cs b/Mongo.Migration/Migrations/MigrationRunner.cs index effd4ca..9431eb2 100644 --- a/Mongo.Migration/Migrations/MigrationRunner.cs +++ b/Mongo.Migration/Migrations/MigrationRunner.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Mongo.Migration.Documents; using Mongo.Migration.Migrations.Locators; @@ -10,7 +9,7 @@ namespace Mongo.Migration.Migrations { internal class MigrationRunner : IMigrationRunner { - private readonly IMigrationLocator _migrationLocator; + public IMigrationLocator _migrationLocator { get; } private readonly IVersionService _versionService; @@ -19,7 +18,7 @@ public MigrationRunner(IMigrationLocator migrationLocator, IVersionService versi _migrationLocator = migrationLocator; _versionService = versionService; } - + public void Run(Type type, BsonDocument document) { var documentVersion = _versionService.GetVersionOrDefault(document); @@ -30,7 +29,7 @@ public void Run(Type type, BsonDocument document) MigrateUpOrDown(type, document, documentVersion, currentOrLatest); } - + public void Run(Type type, BsonDocument document, DocumentVersion to) { var documentVersion = _versionService.GetVersionOrDefault(document); diff --git a/Mongo.Migration/Mongo.Migration.csproj b/Mongo.Migration/Mongo.Migration.csproj index 596c899..4fb9c37 100644 --- a/Mongo.Migration/Mongo.Migration.csproj +++ b/Mongo.Migration/Mongo.Migration.csproj @@ -11,8 +11,13 @@ + + + + + \ No newline at end of file diff --git a/Mongo.Migration/MongoMigration.cs b/Mongo.Migration/MongoMigration.cs index cd32032..df7814b 100644 --- a/Mongo.Migration/MongoMigration.cs +++ b/Mongo.Migration/MongoMigration.cs @@ -28,7 +28,6 @@ public void Run() _runtimeVersionLocator.Locate(); _collectionLocator.Locate(); _startUpVersionLocator.Locate(); - _migrationService.Migrate(); } } diff --git a/Mongo.Migration/Services/MigrationService.cs b/Mongo.Migration/Services/MigrationService.cs index 292517b..9fcaa6f 100644 --- a/Mongo.Migration/Services/MigrationService.cs +++ b/Mongo.Migration/Services/MigrationService.cs @@ -12,14 +12,16 @@ internal class MigrationService : IMigrationService { private readonly ILogger _logger; private readonly ICollectionMigrationRunner _migrationRunner; + private readonly IDatabaseMigrationRunner _dbMigrationRunner; private readonly MigrationInterceptorProvider _provider; private readonly DocumentVersionSerializer _serializer; public MigrationService(DocumentVersionSerializer serializer, MigrationInterceptorProvider provider, - ICollectionMigrationRunner migrationRunner) + ICollectionMigrationRunner migrationRunner, IDatabaseMigrationRunner dbMigrationRunner) : this(serializer, provider, NullLoggerFactory.Instance) { _migrationRunner = migrationRunner; + _dbMigrationRunner = dbMigrationRunner; } private MigrationService( @@ -43,6 +45,7 @@ public void Migrate() private void OnStartup() { _migrationRunner.RunAll(); + _dbMigrationRunner.RunAll(); } private void RegisterSerializer() diff --git a/Mongo.Migration/Startup/DotNetCore/MongoMigrationExtensions.cs b/Mongo.Migration/Startup/DotNetCore/MongoMigrationExtensions.cs index 463d774..b3307ee 100644 --- a/Mongo.Migration/Startup/DotNetCore/MongoMigrationExtensions.cs +++ b/Mongo.Migration/Startup/DotNetCore/MongoMigrationExtensions.cs @@ -24,9 +24,11 @@ public static void AddMigration( private static void RegisterDefaults(IServiceCollection services, IMongoMigrationSettings settings) { services.AddSingleton(settings); - + services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -36,7 +38,9 @@ private static void RegisterDefaults(IServiceCollection services, IMongoMigratio services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Mongo.Migration/Startup/DotNetCore/MongoMigrationStartupFilter.cs b/Mongo.Migration/Startup/DotNetCore/MongoMigrationStartupFilter.cs index 9fbd2af..22414b4 100644 --- a/Mongo.Migration/Startup/DotNetCore/MongoMigrationStartupFilter.cs +++ b/Mongo.Migration/Startup/DotNetCore/MongoMigrationStartupFilter.cs @@ -28,7 +28,11 @@ public Action Configure(Action next) { try { + _logger.LogInformation("Running migration. Please wait...."); + _migration.Run(); + + _logger.LogInformation("Migration has been done"); } catch (Exception ex) { diff --git a/Mongo.Migration/Startup/MongoMigrationSettings.cs b/Mongo.Migration/Startup/MongoMigrationSettings.cs index 8a96f03..8a92529 100644 --- a/Mongo.Migration/Startup/MongoMigrationSettings.cs +++ b/Mongo.Migration/Startup/MongoMigrationSettings.cs @@ -5,11 +5,13 @@ namespace Mongo.Migration.Startup public class MongoMigrationSettings : IMongoMigrationSettings { public string ConnectionString { get; set; } - + public string Database { get; set; } public string VersionFieldName { get; set; } - + + public string RunningVersion { get; set; } = "1.0.0"; + public MongoClientSettings ClientSettings { get; set; } } } \ No newline at end of file diff --git a/Mongo.Migration/Startup/Static/ComponentRegistry.cs b/Mongo.Migration/Startup/Static/ComponentRegistry.cs index e72650f..acbba3b 100644 --- a/Mongo.Migration/Startup/Static/ComponentRegistry.cs +++ b/Mongo.Migration/Startup/Static/ComponentRegistry.cs @@ -18,10 +18,10 @@ internal class ComponentRegistry : IComponentRegistry public ComponentRegistry(IMongoMigrationSettings settings, IContainerAdapter containerAdapter = null) { _settings = settings; - + if(containerAdapter == null) containerAdapter = new LightInjectAdapter(new ServiceContainer()); - + _containerAdapter = containerAdapter; } @@ -30,7 +30,7 @@ public void RegisterComponents(IMongoClient client) RegisterDefaults(); _containerAdapter.RegisterInstance(client); - + _containerAdapter.Register(); } @@ -42,22 +42,23 @@ public TComponent Get() where TComponent : class private void RegisterDefaults() { _containerAdapter.RegisterInstance(_containerAdapter); - - _containerAdapter.RegisterSingleton(); - _containerAdapter.RegisterInstance(_settings); - + + _containerAdapter.RegisterSingleton(); _containerAdapter.RegisterSingleton(); _containerAdapter.RegisterSingleton(); _containerAdapter.RegisterSingleton(); + _containerAdapter.Register(); + _containerAdapter.Register(); _containerAdapter.Register(); _containerAdapter.Register(); _containerAdapter.Register(); - _containerAdapter.Register(); _containerAdapter.Register(); _containerAdapter.Register(); + _containerAdapter.Register(); + _containerAdapter.Register(); _containerAdapter.Register(); } diff --git a/Mongo.Migration/Utils/MigrationUtils.cs b/Mongo.Migration/Utils/MigrationUtils.cs new file mode 100644 index 0000000..418cc8a --- /dev/null +++ b/Mongo.Migration/Utils/MigrationUtils.cs @@ -0,0 +1,145 @@ +using System; +using Microsoft.Extensions.Logging; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace Mongo.Migration.Utils +{ + public class MigrationUtils + { + private readonly ILogger logger; + + public MigrationUtils() + { + this.logger = ApplicationLogging.CreateLogger(); + } + + public long UpdateInnerDocuments( + IMongoDatabase db, + BsonDocument documentToUpdate, + string collection, + string documentField) => this.UpdateInnerDocuments(db, documentToUpdate, collection, documentField, "_id"); + + public long UpdateInnerDocuments( + IMongoDatabase db, + BsonDocument documentToUpdate, + string collection, + string documentField, + string fieldToFilter) + { + logger.LogInformation($"Updating inner documents: {collection}.{documentField} ({fieldToFilter})..."); + + var fieldToFilterValue = documentToUpdate.GetValue(fieldToFilter); + + var filter = Builders.Filter.Eq($"{documentField}.{fieldToFilter}", fieldToFilterValue); + var update = Builders.Update.Set(documentField, documentToUpdate); + var result = db.GetCollection(collection).UpdateMany(filter, update); + + logger.LogInformation($"Has been modified {result.ModifiedCount} register " + + $"da collection {collection}, {documentField}.{fieldToFilter}={fieldToFilterValue}."); + + return result.ModifiedCount; + } + + public static BsonValue TryGetValue(BsonDocument document, string attr, ILogger logger) + { + try + { + return document[attr]; + } + catch (Exception) + { + logger.LogWarning($"The attribute {attr} not found in document, returning value null"); + return BsonNull.Value; + } + } + + public static void UpdateUniqueIndex(IMongoDatabase db, string collectionName, string field, string indexName, ILogger logger) + { + try + { + logger.LogInformation($"Update index {collectionName}.{field}"); + + var collection = db.GetCollection(collectionName); + collection.Indexes.DropOne(indexName); + + var collation = new Collation("en", strength: new Optional(CollationStrength.Secondary)); + + var indexModel = new CreateIndexModel(Builders.IndexKeys.Ascending(field), new CreateIndexOptions + { + Unique = true, + Collation = collation, + Name = indexName + }); + + collection.Indexes.CreateOne(indexModel); + } + catch (Exception ex) + { + logger.LogError(ex, $"Error creating unique index for field {collectionName}.{field}"); + } + } + public static void CreateAscendingIndex(IMongoDatabase db, string collectionName, string field, string indexName, ILogger logger) + { + try + { + logger.LogInformation($"Creatinf ascending index: {collectionName}.{field}"); + + var collection = db.GetCollection(collectionName); + + var indexModel = new CreateIndexModel(Builders.IndexKeys.Ascending(field), new CreateIndexOptions + { + Name = indexName + }); + + collection.Indexes.CreateOne(indexModel); + } + catch (Exception ex) + { + logger.LogError(ex, $"Error creating ascending index for field {collectionName}.{field}"); + } + } + + public static void CreateDescendingIndex(IMongoDatabase db, string collectionName, string field, string indexName, ILogger logger) + { + try + { + logger.LogInformation($"Creating descending index: {collectionName}.{field}"); + + var collection = db.GetCollection(collectionName); + + var indexModel = new CreateIndexModel(Builders.IndexKeys.Descending(field), new CreateIndexOptions + { + Name = indexName + }); + + collection.Indexes.CreateOne(indexModel); + } + catch (Exception ex) + { + logger.LogError(ex, $"Error creating descending index for field {collectionName}.{field}"); + } + } + + public static void CreateTextIndex(IMongoDatabase db, string collectionName, string field, string indexName, ILogger logger) + { + try + { + logger.LogInformation($"Creating text index: {collectionName}.{field}"); + + var collection = db.GetCollection(collectionName); + + var indexModel = new CreateIndexModel(Builders.IndexKeys.Text(field), new CreateIndexOptions + { + Name = indexName + }); + + collection.Indexes.CreateOne(indexModel); + } + catch (Exception ex) + { + logger.LogError(ex, $"Error creating text index for field {collectionName}.{field}"); + } + } + } +} \ No newline at end of file