using Modules.Rating.Api.Database; using Modules.Rating.Api.Database.Entities; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.Linq; using System.Linq; using System.Linq.Expressions; namespace Modules.Rating.Api.Repositories; public class RateRepository(MongoDbContext context) { private readonly IMongoCollection _collection = context.GetCollection(nameof(Rate)); public async Task AddAsync(Rate entity) { if (await IsRateExists(entity.Key)) throw new Exception("Object is already rated by subject"); await _collection.InsertOneAsync(entity); } public async Task UpdateAsync(Rate entity) { if (!await IsRateExists(entity.Key)) throw new Exception("Rate not found"); var document = await _collection.FindOneAndReplaceAsync(q => q.Key == entity.Key, entity); return document != null; } public async Task DeleteAsync(RateKey key) { if (!await IsRateExists(key)) throw new Exception("Rate not found"); var document = await _collection.FindOneAndDeleteAsync(q => q.Key == key); return document != null; } public async Task GetFirstOrDefaultWhere(Expression> predicate) => await _collection.Find(predicate).SingleOrDefaultAsync(); public async Task> GetWhere(Expression> predicate) => await _collection.Find(predicate).ToListAsync(); //internal async Task> GetRates(IEnumerable objectIds, Guid? subjectId) //{ // //var query = _collection.AsQueryable(); // //return await query // // .Where(q => objectIds.Contains(q.Key.ObjectId)) // // .GroupBy(q => q.Key.ObjectId, (o, r) => new { ObjectId = o, Rate = r.Average(q => q.RatePercentage) }) // // .GroupJoin(query.Where(q => q.Key.SubjectId == subjectId), // // q => q.ObjectId, // // q => q.Key.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) => await _collection .Aggregate() .Match(q => q.Key.ObjectId == objectId) .Group(q => q.Key.ObjectId, q => q.Average(q => q.RatePercentage)) .FirstOrDefaultAsync().ConfigureAwait(false); public async Task IsRateExists(RateKey key) => await _collection.Find(q => q.Key == key).AnyAsync(); }