MVCPostall/Postall.Infra.MongoDB/Repositories/VideoRepository.cs
2025-03-08 19:13:24 -03:00

253 lines
9.2 KiB
C#

using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Postall.Domain;
using Postall.Domain.Entities;
using Postall.Infra.MongoDB.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Postall.Infra.MongoDB.Repositories
{
public class VideoRepository : IVideoRepository
{
private readonly IMongoCollection<VideoData> _videosCollection;
public VideoRepository(IOptions<MongoDbSettings> mongoDbSettings)
{
var client = new MongoClient(mongoDbSettings.Value.ConnectionString);
var database = client.GetDatabase(mongoDbSettings.Value.DatabaseName);
_videosCollection = database.GetCollection<VideoData>(mongoDbSettings.Value.VideosCollectionName);
// Cria índice composto para videoId e userId
var indexKeysDefinition = Builders<VideoData>.IndexKeys
.Ascending(v => v.VideoId)
.Ascending(v => v.UserId);
_videosCollection.Indexes.CreateOne(new CreateIndexModel<VideoData>(indexKeysDefinition, new CreateIndexOptions { Unique = true }));
// Cria índice para buscas por texto
var textIndexDefinition = Builders<VideoData>.IndexKeys
.Text(v => v.Title)
.Text(v => v.Description);
_videosCollection.Indexes.CreateOne(new CreateIndexModel<VideoData>(textIndexDefinition));
// Cria índice para buscas por channelId
var channelIndexDefinition = Builders<VideoData>.IndexKeys
.Ascending(v => v.ChannelId);
_videosCollection.Indexes.CreateOne(new CreateIndexModel<VideoData>(channelIndexDefinition));
// Cria índice para userId + channelId
var userChannelIndexDefinition = Builders<VideoData>.IndexKeys
.Ascending(v => v.UserId)
.Ascending(v => v.ChannelId);
_videosCollection.Indexes.CreateOne(new CreateIndexModel<VideoData>(userChannelIndexDefinition));
}
/// <summary>
/// Obtém todos os vídeos
/// </summary>
public async Task<IEnumerable<VideoData>> GetAllAsync()
{
return await _videosCollection.Find(v => true).ToListAsync();
}
/// <summary>
/// Obtém um vídeo pelo ID do MongoDB
/// </summary>
public async Task<VideoData> GetByIdAsync(string id)
{
if (!ObjectId.TryParse(id, out _))
return null;
return await _videosCollection.Find(v => v.Id == id).FirstOrDefaultAsync();
}
/// <summary>
/// Obtém um vídeo pelo ID do YouTube
/// </summary>
public async Task<VideoData> GetByVideoIdAsync(string videoId)
{
return await _videosCollection.Find(v => v.VideoId == videoId).FirstOrDefaultAsync();
}
/// <summary>
/// Obtém os vídeos pelo ID do usuário
/// </summary>
public async Task<IList<VideoData>> GetByUserIdAsync(string userId)
{
return await _videosCollection.Find(v => v.UserId == userId).ToListAsync();
}
/// <summary>
/// Obtém os vídeos pelo ID do canal
/// </summary>
public async Task<IList<VideoData>> GetByChannelIdAsync(string channelId)
{
return await _videosCollection.Find(v => v.ChannelId == channelId).ToListAsync();
}
/// <summary>
/// Obtém os vídeos pelo ID do usuário e ID do canal
/// </summary>
public async Task<IList<VideoData>> GetByUserIdAndChannelIdAsync(string userId, string channelId)
{
return await _videosCollection.Find(v => v.UserId == userId && v.ChannelId == channelId).ToListAsync();
}
/// <summary>
/// Obtém um vídeo pelo ID do usuário e ID do vídeo
/// </summary>
public async Task<VideoData> GetByUserIdAndVideoIdAsync(string userId, string videoId)
{
return await _videosCollection.Find(v => v.UserId == userId && v.VideoId == videoId).FirstOrDefaultAsync();
}
/// <summary>
/// Adiciona um novo vídeo
/// </summary>
public async Task<VideoData> AddAsync(VideoData videoData)
{
var existingVideo = await GetByUserIdAndVideoIdAsync(videoData.UserId, videoData.VideoId);
if (existingVideo != null)
return existingVideo;
if (string.IsNullOrEmpty(videoData.Id) || !ObjectId.TryParse(videoData.Id, out _))
{
videoData.Id = ObjectId.GenerateNewId().ToString();
}
videoData.CreatedAt = DateTime.UtcNow;
try
{
await _videosCollection.InsertOneAsync(videoData);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
return await GetByUserIdAndVideoIdAsync(videoData.UserId, videoData.VideoId);
}
else
{
throw;
}
}
return videoData;
}
/// <summary>
/// Adiciona vários vídeos de uma vez
/// </summary>
public async Task<IEnumerable<VideoData>> AddManyAsync(IEnumerable<VideoData> videos)
{
if (videos == null || !videos.Any())
return new List<VideoData>();
var videosList = videos.ToList();
var now = DateTime.UtcNow;
// Gera novos IDs para o MongoDB se necessário
foreach (var videoData in videosList)
{
if (string.IsNullOrEmpty(videoData.Id) || !ObjectId.TryParse(videoData.Id, out _))
{
videoData.Id = ObjectId.GenerateNewId().ToString();
}
videoData.CreatedAt = now;
}
// Verifica vídeos existentes pelo ID do YouTube e usuário
var userIds = videosList.Select(v => v.UserId).Distinct().ToList();
var videoIds = videosList.Select(v => v.VideoId).ToList();
var filter = Builders<VideoData>.Filter.And(
Builders<VideoData>.Filter.In(v => v.UserId, userIds),
Builders<VideoData>.Filter.In(v => v.VideoId, videoIds)
);
var existingVideos = await _videosCollection.Find(filter).ToListAsync();
// Filtra apenas os vídeos que ainda não foram adicionados
var existingPairs = existingVideos
.Select(v => $"{v.UserId}:{v.VideoId}")
.ToHashSet();
var newVideos = videosList
.Where(v => !existingPairs.Contains($"{v.UserId}:{v.VideoId}"))
.ToList();
if (newVideos.Any())
{
try
{
await _videosCollection.InsertManyAsync(newVideos);
}
catch (MongoBulkWriteException)
{
// Em caso de erro, adiciona um por um
foreach (var video in newVideos)
{
try { await AddAsync(video); }
catch { /* Ignora erros individuais */ }
}
}
}
return newVideos;
}
/// <summary>
/// Atualiza um vídeo existente
/// </summary>
public async Task<bool> UpdateAsync(VideoData videoData)
{
if (string.IsNullOrEmpty(videoData.Id) || !ObjectId.TryParse(videoData.Id, out _))
return false;
videoData.UpdatedAt = DateTime.UtcNow;
var result = await _videosCollection.ReplaceOneAsync(
v => v.Id == videoData.Id,
videoData,
new ReplaceOptions { IsUpsert = false });
return result.IsAcknowledged && result.ModifiedCount > 0;
}
/// <summary>
/// Remove um vídeo pelo ID do MongoDB
/// </summary>
public async Task<bool> DeleteAsync(string id)
{
if (!ObjectId.TryParse(id, out _))
return false;
var result = await _videosCollection.DeleteOneAsync(v => v.Id == id);
return result.IsAcknowledged && result.DeletedCount > 0;
}
/// <summary>
/// Remove um vídeo pelo ID do YouTube
/// </summary>
public async Task<bool> DeleteByVideoIdAsync(string videoId)
{
var result = await _videosCollection.DeleteOneAsync(v => v.VideoId == videoId);
return result.IsAcknowledged && result.DeletedCount > 0;
}
/// <summary>
/// Busca vídeos com base em um termo de pesquisa no título ou descrição
/// </summary>
public async Task<IEnumerable<VideoData>> SearchAsync(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
return await GetAllAsync();
var filter = Builders<VideoData>.Filter.Text(searchTerm);
return await _videosCollection.Find(filter).ToListAsync();
}
}
}