145 lines
5.8 KiB
C#
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();
|
|
} |