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;
}
}