using Microsoft.EntityFrameworkCore; using Modules.Rating.Api.Database; using Modules.Rating.Api.Database.Entities; using System.Linq.Expressions; namespace Modules.Rating.Api.Repositories; public class RatingRepository(RatingDbContext context) { private readonly RatingDbContext _context = context; public async Task AddAsync(Vote entity, CancellationToken cancellationToken) { if (await IsVoteExists(entity.ObjectId, entity.SubjectId)) throw new Exception("Object is already rated by subject"); _context.Votes.Add(entity); await _context.SaveChangesAsync(cancellationToken); } public async Task UpdateAsync(Vote entity, CancellationToken cancellationToken) { var vote = await _context.Votes .FirstOrDefaultAsync(q => q.ObjectId == entity.ObjectId && q.SubjectId == entity.SubjectId, cancellationToken); if (vote == null) return false; vote.RatingPercentage = entity.RatingPercentage; await _context.SaveChangesAsync(cancellationToken); return true; } public async Task DeleteAsync(Guid objectId, Guid subjectId) { var rate = await _context.Votes.FirstOrDefaultAsync(q => q.ObjectId == objectId && q.SubjectId == subjectId); if (rate != null) _context.Votes?.Remove(rate); await _context.SaveChangesAsync(); return rate != null; } public async Task GetFirstOrDefaultWhere(Expression> predicate) => await _context.Votes.Where(predicate).SingleOrDefaultAsync(); public async Task> GetWhere(Expression> predicate) => await _context.Votes.Where(predicate).ToListAsync(); internal async Task> GetVotes(IEnumerable objectIds, Guid? subjectId) { var query = _context.Votes.AsQueryable(); return await query .Where(q => objectIds.Contains(q.ObjectId)) .GroupBy(q => q.ObjectId, (o, r) => new { ObjectId = o, Rate = r.Average(q => q.RatingPercentage) }) .GroupJoin(query.Where(q => q.SubjectId == subjectId), q => q.ObjectId, q => q.ObjectId, (g, r) => new { g.ObjectId, g.Rate, SubjecrRates = r }) .SelectMany(q => q.SubjecrRates.DefaultIfEmpty(), (r, s) => new VoteItem { ObjectId = r.ObjectId, Rate = r.Rate, SubjectRate = s != null ? s.RatingPercentage : null }) .ToListAsync().ConfigureAwait(false); //var builder = Builders.SetFields //await _collection //.Aggregate() //.Match(q => objectIds.Contains(q.Key.ObjectId)) //.Group(q => q.Key.ObjectId, q => new { ObjectId = q.Key, Rate = q.Average(q => q.RatePercentage) }) //.Lookup(nameof(Rate), q => q.ObjectId, q) //.FirstOrDefaultAsync().ConfigureAwait(false); } //internal async Task> GetRates(IEnumerable objectIds, Guid? subjectId) //{ // var matchStage = new BsonDocument("$match", new BsonDocument("Key.ObjectId", new BsonDocument("$in", new BsonArray(objectIds)))); // var groupStage = new BsonDocument("$group", new BsonDocument // { // { "_id", "$Key.ObjectId" }, // { "AverageRate", new BsonDocument("$avg", "$RatePercentage") } // }); // var lookupStage = new BsonDocument("$lookup", new BsonDocument // { // { "from", _collection.CollectionNamespace.CollectionName }, // { "let", new BsonDocument("objectId", "$_id") }, // { "pipeline", new BsonArray // { // new BsonDocument("$match", new BsonDocument("$expr", new BsonDocument("$and", new BsonArray // { // new BsonDocument("$eq", new BsonArray { "$Key.ObjectId", "$$objectId" }), // //new BsonDocument("$eq", new BsonArray { "$Key.SubjectId", subjectId.ToString() ?? "" }) // new BsonDocument("$eq", new BsonArray { "$Key.SubjectId", subjectId.ToString() ?? "" }) // }))) // } // }, // { "as", "SubjectRates" } // }); // var projectStage = new BsonDocument("$project", new BsonDocument // { // { "ObjectId", "$_id" }, // { "Rate", "$AverageRate" }, // { "SubjectRate", new BsonDocument("$arrayElemAt", new BsonArray { "$SubjectRates.RatePercentage", 0 }) } // }); // var pipeline = new[] { matchStage, groupStage, lookupStage, projectStage }; // var result = await _collection.AggregateAsync(pipeline).ConfigureAwait(false); // var rateItems = new List(); // //await result.ForEachAsync(doc => // //{ // // rateItems.Add(new RateItem // // { // // ObjectId = doc["ObjectId"].AsGuid, // // Rate = doc["Rate"].AsDouble, // // SubjectRate = doc["SubjectRate"].IsBsonNull ? (ushort?)null : doc["SubjectRate"].AsNullableInt32 // // }); // //}).ConfigureAwait(false); // await result.ForEachAsync(doc => // { // var subjectRate = doc.GetValue("SubjectRate", BsonNull.Value); // rateItems.Add(new RateItem // { // ObjectId = doc.GetValue("ObjectId").AsGuid, // Rate = doc.GetValue("Rate").AsDouble, // SubjectRate = subjectRate.IsBsonNull ? null : subjectRate.AsNullableInt32 // }); // }).ConfigureAwait(false); // return rateItems; //} internal class VoteItem { internal Guid ObjectId { get; set; } = default!; internal double Rate { get; set; } internal int? SubjectRate { get; set; } } public async Task GetAverageObjectRating(Guid objectId) { if (!await _context.Votes.AnyAsync(q => q.ObjectId == objectId)) return null; return await _context.Votes .Where(q => q.ObjectId == objectId) .GroupBy(q => q.ObjectId) .Select(q => q.Average(q => q.RatingPercentage)) .FirstOrDefaultAsync().ConfigureAwait(false); } public async Task IsVoteExists(Guid objectId, Guid subjectId) => await _context.Votes.Where(q => q.ObjectId == objectId && q.SubjectId == subjectId).AnyAsync(); }