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

@@ -8,6 +8,8 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
where TCriteria : new()
where TSortField : struct, Enum
{
protected abstract bool UseSelectIdQuery { get; }
public async Task<SearchResult<TItem>> SearchAsync(SearchOptions<TCriteria, TSortField> options, CancellationToken cancellationToken = default)
{
IQueryable<TBaseQuery> baseQuery = GetBaseQuery();
@@ -16,12 +18,7 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
int total = await filteredQuery.CountAsync(cancellationToken);
IOrderedQueryable<TBaseQuery> orderedQuery = ApplySorting(filteredQuery, options.SortOptions);
IQueryable<TItem> selectQuery = GetSelectQuery(orderedQuery);
TItem[] items = await selectQuery
.Skip((options.PageNumber - 1) * options.PageSize)
.Take(options.PageSize)
.ToArrayAsync(cancellationToken);
TItem[] items = await GetItemsAsync(options, orderedQuery, cancellationToken);
await PostLoadAsync(items, cancellationToken);
@@ -44,10 +41,10 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
var (field, direction) = (sortOptions[i].Field, sortOptions[i].Direction);
bool isDescending = direction == SortDirection.Descending;
IOrderedQueryable<TBaseQuery> applyFirst(Expression<Func<TBaseQuery, object>> selector) => isDescending ? query.OrderByDescending(selector) : query.OrderBy(selector);
IOrderedQueryable<TBaseQuery> applyNext(Expression<Func<TBaseQuery, object>> selector) => isDescending ? ordered!.ThenByDescending(selector) : ordered!.ThenBy(selector);
IOrderedQueryable<TBaseQuery> applyFirst(Expression<Func<TBaseQuery, object?>> selector) => isDescending ? query.OrderByDescending(selector) : query.OrderBy(selector);
IOrderedQueryable<TBaseQuery> applyNext(Expression<Func<TBaseQuery, object?>> selector) => isDescending ? ordered!.ThenByDescending(selector) : ordered!.ThenBy(selector);
Expression<Func<TBaseQuery, object>> selector = GetSortExpression(field);
Expression<Func<TBaseQuery, object?>> selector = GetSortExpression(field);
ordered = (i == 0) ? applyFirst(selector) : applyNext(selector);
}
@@ -83,8 +80,34 @@ public abstract class SearchProvider<TItem, TCriteria, TSortField, TBaseQuery> :
}
}
protected abstract Expression<Func<TBaseQuery, object>> GetSortExpression(TSortField field);
private async Task<TItem[]> GetItemsAsync(SearchOptions<TCriteria, TSortField> options, IOrderedQueryable<TBaseQuery> orderedQuery, CancellationToken cancellationToken)
{
if (UseSelectIdQuery)
{
int[] ids = await GetSelectIdQuery(orderedQuery)
.Skip((options.PageNumber - 1) * options.PageSize)
.Take(options.PageSize)
.ToArrayAsync(cancellationToken);
Dictionary<int, TItem> items = await GetItems(ids);
return [.. ids.Select(uniqueId => items[uniqueId])];
}
else
{
IQueryable<TItem> selectQuery = GetSelectQuery(orderedQuery);
return await selectQuery
.Skip((options.PageNumber - 1) * options.PageSize)
.Take(options.PageSize)
.ToArrayAsync(cancellationToken);
}
}
protected abstract Expression<Func<TBaseQuery, object?>> GetSortExpression(TSortField field);
protected abstract IEnumerable<(Expression<Func<TBaseQuery, object>> Selector, SortDirection Dir)> GetDefaultSortChain();
protected abstract IQueryable<int> GetSelectIdQuery(IOrderedQueryable<TBaseQuery> query);
protected abstract IQueryable<TItem> GetSelectQuery(IOrderedQueryable<TBaseQuery> query);
protected abstract Task<Dictionary<int, TItem>> GetItems(int[] ids);
protected virtual Task PostLoadAsync(IList<TItem> items, CancellationToken cancellationToken) => Task.CompletedTask;
}