Update search provider sort logic, and added testing for circle search provider.
This commit is contained in:
@@ -16,7 +16,7 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
|
||||
int total = await filteredQuery.CountAsync(cancellationToken);
|
||||
|
||||
IOrderedQueryable<TBaseQuery> orderedQuery = ApplySorting(filteredQuery, options.SortOptions);
|
||||
IOrderedQueryable<TItem> selectQuery = GetSelectQuery(orderedQuery);
|
||||
IQueryable<TItem> selectQuery = GetSelectQuery(orderedQuery);
|
||||
|
||||
TItem[] items = await selectQuery
|
||||
.Skip((options.PageNumber - 1) * options.PageSize)
|
||||
@@ -50,10 +50,40 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
|
||||
ordered = (i == 0) ? applyFirst(selector) : applyNext(selector);
|
||||
}
|
||||
|
||||
return ordered ?? GetDefaultSortExpression(query);
|
||||
//return ordered ?? GetDefaultSortExpression(query);
|
||||
|
||||
// Always add the default as the final tiebreaker
|
||||
var chain = GetDefaultSortChain();
|
||||
|
||||
if (ordered is null)
|
||||
{
|
||||
using var e = chain.GetEnumerator();
|
||||
if (!e.MoveNext()) throw new InvalidOperationException("No default sort provided.");
|
||||
|
||||
var (Selector, Dir) = e.Current;
|
||||
var res = Dir == SortDirection.Descending
|
||||
? query.OrderByDescending(Selector)
|
||||
: query.OrderBy(Selector);
|
||||
|
||||
while (e.MoveNext())
|
||||
res = e.Current.Dir == SortDirection.Descending
|
||||
? res.ThenByDescending(e.Current.Selector)
|
||||
: res.ThenBy(e.Current.Selector);
|
||||
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
var res = ordered;
|
||||
foreach (var (sel, dir) in chain)
|
||||
res = dir == SortDirection.Descending ? res.ThenByDescending(sel) : res.ThenBy(sel);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract Expression<Func<TBaseQuery, object>> GetSortExpression(TSortField field);
|
||||
protected abstract IOrderedQueryable<TBaseQuery> GetDefaultSortExpression(IQueryable<TBaseQuery> query);
|
||||
protected abstract IOrderedQueryable<TItem> GetSelectQuery(IOrderedQueryable<TBaseQuery> query);
|
||||
//protected abstract (Expression<Func<TBaseQuery, object>> Selector, SortDirection Direction) GetDefaultSortExpression();
|
||||
protected abstract IEnumerable<(Expression<Func<TBaseQuery, object>> Selector, SortDirection Dir)> GetDefaultSortChain();
|
||||
protected abstract IQueryable<TItem> GetSelectQuery(IOrderedQueryable<TBaseQuery> query);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using JSMR.Application.Circles.Queries.Search;
|
||||
using JSMR.Application.Common.Search;
|
||||
using JSMR.Infrastructure.Common.Queries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Linq.Expressions;
|
||||
@@ -102,22 +103,22 @@ public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleS
|
||||
|
||||
protected override Expression<Func<CircleSearchItem, object>> GetSortExpression(CircleSortField field) => field switch
|
||||
{
|
||||
//CircleSortField.MakerId => x => x.MakerId,
|
||||
//CircleSortField.Downloads => x => x.Downloads,
|
||||
//CircleSortField.Releases => x => x.Releases,
|
||||
//CircleSortField.Pending => x => x.Pending,
|
||||
//CircleSortField.FirstReleaseDate => x => x.FirstReleaseDate ?? DateTime.MinValue,
|
||||
//CircleSortField.LatestReleaseDate => x => x.LatestReleaseDate ?? DateTime.MinValue,
|
||||
CircleSortField.Favorite => x => x.Favorite,
|
||||
CircleSortField.Blacklisted => x => x.Blacklisted,
|
||||
CircleSortField.Spam => x => x.Spam,
|
||||
CircleSortField.Favorite => x => !x.Favorite,
|
||||
CircleSortField.Blacklisted => x => !x.Blacklisted,
|
||||
CircleSortField.Spam => x => !x.Spam,
|
||||
_ => x => x.Name
|
||||
};
|
||||
|
||||
protected override IOrderedQueryable<CircleSearchItem> GetDefaultSortExpression(IQueryable<CircleSearchItem> query)
|
||||
=> query.OrderBy(x => x.Name).ThenBy(x => x.CircleId);
|
||||
|
||||
protected override IOrderedQueryable<CircleSearchItem> GetSelectQuery(IOrderedQueryable<CircleSearchItem> query)
|
||||
protected override IEnumerable<(Expression<Func<CircleSearchItem, object>> Selector, SortDirection Dir)> GetDefaultSortChain()
|
||||
{
|
||||
yield return (x => x.Name, SortDirection.Ascending);
|
||||
yield return (x => x.MakerId, SortDirection.Ascending);
|
||||
}
|
||||
|
||||
protected override IQueryable<CircleSearchItem> GetSelectQuery(IOrderedQueryable<CircleSearchItem> query)
|
||||
{
|
||||
// Join to VoiceWorks by LatestProductId to fill HasImage / SalesDate
|
||||
var selected =
|
||||
@@ -142,9 +143,6 @@ public class CircleSearchProvider(AppDbContext context) : SearchProvider<CircleS
|
||||
LatestVoiceWorkSalesDate = latest != null ? latest.SalesDate : null
|
||||
};
|
||||
|
||||
// Preserve existing ordering; add stable tiebreaker
|
||||
return selected.OrderBy(x => 0).ThenBy(x => x.Name).ThenBy(x => x.CircleId);
|
||||
// NOTE: If your base class re-applies ordering after Select, you can just:
|
||||
// return selected.OrderBy(x => x.Name).ThenBy(x => x.CircleId);
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using JSMR.Application.Creators.Queries.Search.Contracts;
|
||||
using JSMR.Application.Common.Search;
|
||||
using JSMR.Application.Creators.Queries.Search.Contracts;
|
||||
using JSMR.Application.Creators.Queries.Search.Ports;
|
||||
using JSMR.Infrastructure.Common.Queries;
|
||||
using System.Linq.Expressions;
|
||||
@@ -54,6 +55,11 @@ public class CreatorSearchProvider(AppDbContext context) : SearchProvider<Creato
|
||||
return query.OrderBy(x => x.Name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<(Expression<Func<CreatorSearchItem, object>> Selector, SortDirection Dir)> GetDefaultSortChain()
|
||||
{
|
||||
yield return (x => x.Name ?? string.Empty, SortDirection.Ascending);
|
||||
}
|
||||
|
||||
protected override IOrderedQueryable<CreatorSearchItem> GetSelectQuery(IOrderedQueryable<CreatorSearchItem> query)
|
||||
{
|
||||
return query;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using JSMR.Application.Tags.Queries.Search.Contracts;
|
||||
using JSMR.Application.Common.Search;
|
||||
using JSMR.Application.Creators.Queries.Search.Contracts;
|
||||
using JSMR.Application.Tags.Queries.Search.Contracts;
|
||||
using JSMR.Application.Tags.Queries.Search.Ports;
|
||||
using JSMR.Infrastructure.Common.Queries;
|
||||
using System.Linq.Expressions;
|
||||
@@ -59,6 +61,11 @@ public class TagSearchProvider(AppDbContext context) : SearchProvider<TagSearchI
|
||||
return query.OrderBy(x => x.Name);
|
||||
}
|
||||
|
||||
protected override IEnumerable<(Expression<Func<TagSearchItem, object>> Selector, SortDirection Dir)> GetDefaultSortChain()
|
||||
{
|
||||
yield return (x => x.Name ?? string.Empty, SortDirection.Ascending);
|
||||
}
|
||||
|
||||
protected override IOrderedQueryable<TagSearchItem> GetSelectQuery(IOrderedQueryable<TagSearchItem> query)
|
||||
{
|
||||
return query;
|
||||
|
||||
Reference in New Issue
Block a user