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 RateRepository(RatingDbContext context) { private readonly RatingDbContext _context = context; public async Task AddAsync(Rate entity) { if (await IsRateExists(entity.ObjectId, entity.SubjectId)) throw new Exception("Object is already rated by subject"); _context.Rates.Add(entity); await _context.SaveChangesAsync(); } public async Task UpdateAsync(Rate entity) { var rate = await _context.Rates .FirstOrDefaultAsync(q => q.ObjectId == entity.ObjectId && q.SubjectId == entity.SubjectId) ?? throw new Exception("Rate not found"); rate.RatePercentage = entity.RatePercentage; await _context.SaveChangesAsync(); return rate != null; } public async Task DeleteAsync(Guid objectId, Guid subjectId) { var rate = await _context.Rates.FirstOrDefaultAsync(q => q.ObjectId == objectId && q.SubjectId == subjectId); if (rate != null) _context.Rates?.Remove(rate); await _context.SaveChangesAsync(); return rate != null; } public async Task GetFirstOrDefaultWhere(Expression> predicate) => await _context.Rates.Where(predicate).SingleOrDefaultAsync(); public async Task> GetWhere(Expression> predicate) => await _context.Rates.Where(predicate).ToListAsync(); internal async Task> GetRates(IEnumerable objectIds, Guid? subjectId) { var query = _context.Rates.AsQueryable(); return await query .Where(q => objectIds.Contains(q.ObjectId)) .GroupBy(q => q.ObjectId, (o, r) => new { ObjectId = o, Rate = r.Average(q => q.RatePercentage) }) .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 RateItem { ObjectId = r.ObjectId, Rate = r.Rate, SubjectRate = s != null ? s.RatePercentage : 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 RateItem { internal Guid ObjectId { get; set; } = default!; internal double Rate { get; set; } internal int? SubjectRate { get; set; } } public async Task GetAverageObjectRate(Guid objectId) { if (!await _context.Rates.AnyAsync(q => q.ObjectId == objectId)) return null; return await _context.Rates .Where(q => q.ObjectId == objectId) .GroupBy(q => q.ObjectId) .Select(q => q.Average(q => q.RatePercentage)) .FirstOrDefaultAsync().ConfigureAwait(false); } public async Task IsRateExists(Guid objectId, Guid subjectId) => await _context.Rates.Where(q => q.ObjectId == objectId && q.SubjectId == subjectId).AnyAsync(); }