MyBookmark/Modules.Rating.Api/Repositories/RateRepository.cs
2024-10-21 01:52:41 +03:00

145 lines
5.8 KiB
C#

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<Rate> _collection = context.GetCollection<Rate>(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<bool> 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<bool> 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<Rate> GetFirstOrDefaultWhere(Expression<Func<Rate, bool>> predicate) =>
await _collection.Find(predicate).SingleOrDefaultAsync();
public async Task<List<Rate>> GetWhere(Expression<Func<Rate, bool>> predicate) =>
await _collection.Find(predicate).ToListAsync();
//internal async Task<List<RateItem>> GetRates(IEnumerable<Guid> 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<BsonDocument>.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<Rate, RateItem>(nameof(Rate), q => q.ObjectId, q)
// .FirstOrDefaultAsync().ConfigureAwait(false);
//}
internal async Task<List<RateItem>> GetRates(IEnumerable<Guid> 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<BsonDocument>(pipeline).ConfigureAwait(false);
var rateItems = new List<RateItem>();
//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<double?> 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<bool> IsRateExists(RateKey key) => await _collection.Find(q => q.Key == key).AnyAsync();
}