generated from ricardo/MVCLogin
253 lines
9.2 KiB
C#
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();
|
|
}
|
|
}
|
|
} |