Updated search logic. More UI updates.
All checks were successful
ci / build-test (push) Successful in 2m17s
ci / publish-image (push) Has been skipped

This commit is contained in:
2025-11-17 21:05:55 -05:00
parent 9ef1972472
commit 2418bd0a8f
13 changed files with 430 additions and 108 deletions

View File

@@ -14,6 +14,8 @@ public class CircleQuery
public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleSearchItem, CircleSearchCriteria, CircleSortField, CircleQuery>, ICircleSearchProvider
{
protected override bool UseSelectIdQuery => false;
protected override IQueryable<CircleQuery> GetBaseQuery()
{
// Project from Circles so we can use correlated subqueries per CircleId.
@@ -120,7 +122,7 @@ public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleS
return query;
}
protected override Expression<Func<CircleQuery, object>> GetSortExpression(CircleSortField field) => field switch
protected override Expression<Func<CircleQuery, object?>> GetSortExpression(CircleSortField field) => field switch
{
CircleSortField.Favorite => x => !x.Circle.Favorite,
CircleSortField.Blacklisted => x => !x.Circle.Blacklisted,
@@ -134,6 +136,11 @@ public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleS
yield return (x => x.Circle.MakerId, SortDirection.Ascending);
}
protected override IQueryable<int> GetSelectIdQuery(IOrderedQueryable<CircleQuery> query)
{
return query.Select(x => x.Circle.CircleId);
}
protected override IQueryable<CircleSearchItem> GetSelectQuery(IOrderedQueryable<CircleQuery> query)
{
// Join to VoiceWorks by LatestProductId to fill HasImage / SalesDate
@@ -205,4 +212,77 @@ public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleS
return selected;
}
protected override async Task<Dictionary<int, CircleSearchItem>> GetItems(int[] ids)
{
// Join to VoiceWorks by LatestProductId to fill HasImage / SalesDate
var selected =
from circle in context.Circles.AsNoTracking()
//join vw in context.VoiceWorks.AsNoTracking() on item.LatestProductId equals vw.ProductId into vws
//from latest in vws.DefaultIfEmpty()
where ids.Contains(circle.CircleId)
select new CircleSearchItem
{
CircleId = circle.CircleId,
Name = circle.Name,
MakerId = circle.MakerId,
Favorite = circle.Favorite,
Blacklisted = circle.Blacklisted,
Spam = circle.Spam,
// Aggregates
Downloads = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.Select(v => (int?)v.Downloads) // make nullable for Sum over empty set
.Sum() ?? 0,
Releases = context.VoiceWorks
.Count(v => v.CircleId == circle.CircleId && v.SalesDate != null),
Pending = context.VoiceWorks
.Count(v => v.CircleId == circle.CircleId && v.ExpectedDate != null),
FirstReleaseDate = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.Select(v => v.SalesDate)
.Min(),
LatestReleaseDate = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.Select(v => v.SalesDate)
.Max(),
// "Latest" by ProductId length, then value
LatestProductId = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.OrderByDescending(v => v.ProductId.Length)
.ThenByDescending(v => v.ProductId)
.Select(v => v.ProductId)
.FirstOrDefault(),
// If you want these two in base query too:
LatestVoiceWorkHasImage = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.OrderByDescending(v => v.ProductId.Length)
.ThenByDescending(v => v.ProductId)
.Select(v => (bool?)v.HasImage)
.FirstOrDefault(),
LatestVoiceWorkSalesDate = context.VoiceWorks
.Where(v => v.CircleId == circle.CircleId)
.OrderByDescending(v => v.ProductId.Length)
.ThenByDescending(v => v.ProductId)
.Select(v => v.SalesDate)
.FirstOrDefault(),
//Downloads = item.Downloads,
//Releases = item.Releases,
//Pending = item.Pending,
//FirstReleaseDate = item.FirstReleaseDate,
//LatestReleaseDate = item.LatestReleaseDate,
//LatestProductId = item.LatestProductId,
//LatestVoiceWorkHasImage = latest != null ? latest.HasImage : (bool?)null,
//LatestVoiceWorkSalesDate = latest != null ? latest.SalesDate : null
};
return await selected.ToDictionaryAsync(x => x.CircleId);
}
}