using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; namespace adas_core.Infrastructure.Utils; /// /// Provides utility methods for interacting with MongoDB. /// public class MongoUtils { /// /// Ensures that the indexes defined in are present on the supplied via . For each expected index, an existing index with the same key specification is left unchanged when its options match, or dropped and recreated when they differ (except for the built-in _id_ index, which is never dropped); if no matching index exists the index is created, and MongoDB conflicts reported with code 85 are logged and tolerated. /// /// The whose indexes are inspected and reconciled. /// The set of definitions that the collection should contain. /// public static async Task EnsureIndexes(IMongoCollection collection, List> expectedIndexes) { var existingIndexes = await (await collection.Indexes.ListAsync()).ToListAsync(); var documentSerializer = BsonSerializer.SerializerRegistry.GetSerializer(); var serializerRegistry = BsonSerializer.SerializerRegistry; var renderArgs = new RenderArgs(documentSerializer, serializerRegistry); foreach (var expectedIndexModel in expectedIndexes) { var expectedIndexKeys = expectedIndexModel.Keys .Render(renderArgs).ToString(); var existingIndexWithSameKeys = existingIndexes.FirstOrDefault(index => { var keys = index.Elements.FirstOrDefault(e => e.Name == "key").Value?.ToString(); return keys == expectedIndexKeys; }); if (existingIndexWithSameKeys != null) { // Existe un índice con las mismas claves, verificar las opciones if (!IndexOptionsMatch(expectedIndexModel, existingIndexWithSameKeys, renderArgs)) { // Las opciones no coinciden, eliminar el índice existente y crear el nuevo var indexName = existingIndexWithSameKeys.Elements.FirstOrDefault(e => e.Name == "name").Value ?.AsString; if (!string.IsNullOrEmpty(indexName) && indexName != "_id_") try { await collection.Indexes.DropOneAsync(indexName); await collection.Indexes.CreateOneAsync(expectedIndexModel); Console.WriteLine($"Info: Índice con claves '{expectedIndexKeys}' actualizado."); } catch (MongoCommandException ex) when (ex.Code == 85) { Console.WriteLine( $"Warning: No se pudo actualizar el índice con claves '{expectedIndexKeys}'. Error: {ex.Message}"); } else try { await collection.Indexes.CreateOneAsync(expectedIndexModel); } catch (MongoCommandException ex) when (ex.Code == 85) { Console.WriteLine( $"Warning: El índice con claves '{expectedIndexKeys}' ya existe con diferentes opciones y no se pudo actualizar automáticamente."); } } // Si las opciones coinciden, no se hace nada } else { // No existe un índice con estas claves, crear el nuevo try { await collection.Indexes.CreateOneAsync(expectedIndexModel); } catch (MongoCommandException ex) when (ex.Code == 85) { Console.WriteLine($"Warning: El índice con claves '{expectedIndexKeys}' ya existe."); } } } } /// /// Determines whether the options of an existing index match the expected options by comparing the unique, background, and partialFilterExpression values. /// Missing boolean fields in are treated as false. /// /// The type of the document the index is defined on. /// The containing the expected Unique, Background, and PartialFilterExpression values. /// The representing the existing index whose options should be checked. /// The used to render the expected partial filter expression for comparison. /// true if the unique flag, background flag, and rendered partial filter expression all match the expected values; otherwise false. /// private static bool IndexOptionsMatch(CreateIndexModel expectedIndexModel, BsonDocument existingIndex, RenderArgs renderArgs) { var expectedIndexOptions = expectedIndexModel.Options; var unique = existingIndex.Elements.FirstOrDefault(e => e.Name == "unique").Value?.AsBoolean ?? false; var background = existingIndex.Elements.FirstOrDefault(e => e.Name == "background").Value?.AsBoolean ?? false; var partialFilter = existingIndex.Elements.FirstOrDefault(e => e.Name == "partialFilterExpression").Value ?.ToString(); var expectedPartialFilter = expectedIndexOptions.PartialFilterExpression ?.Render(renderArgs).ToString(); return unique == expectedIndexOptions.Unique && background == expectedIndexOptions.Background && partialFilter == expectedPartialFilter; } }