@page "/search" @page "/search/{SearchTerms}" @using Microsoft.EntityFrameworkCore @using Microsoft.Extensions.Logging @using MudBlazor @using NpgsqlTypes @using OpenArchival.DataAccess @using Persic @using Npgsql.EntityFrameworkCore.PostgreSQL @using System.Linq @using System.Linq.Expressions @using OpenArchival.Blazor.ArtifactGroupingDisplay All Tags Defects Listed Names Title Description Filenames Artifact Transcriptions @if (_totalResults > 0) { @_totalResults results found } @if (_artifactGroupings.Count > 0) { @foreach (var grouping in _artifactGroupings) { } } @inject IDbContextFactory ContextFactory; @inject ILogger Logger; @inject NavigationManager NavigationManager; @code { public enum SearchFilterType { All, Tags, Defects, ListedNames, Title, Description, Filenames, ArtifactTranscriptions } [Parameter] public string SearchTerms { get; set; } = ""; private SearchFilterType _selectedFilter = SearchFilterType.All; private List _artifactGroupings { get; set; } = []; private int _currentPage { get; set; } = 1; private int _totalPages { get; set; } = 0; private int _totalResults { get; set; } = 0; private const int PageSize = 20; // Field to store the current filter logic private Expression> _currentFilterPredicate; protected override async Task OnParametersSetAsync() { if (!string.IsNullOrWhiteSpace(SearchTerms)) { await PerformSearchAsync(); } } private async Task HandleSearchKeyDown(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs args) { if (args.Key != "Enter" || string.IsNullOrWhiteSpace(SearchTerms)) { return; } NavigationManager.NavigateTo($"/search/{SearchTerms}"); await PerformSearchAsync(); } /// /// Sets the search predicate, calculates total results, and loads the first page. /// private async Task PerformSearchAsync() { // Determine which filter expression to use based on the radio button selection switch (_selectedFilter) { case SearchFilterType.Tags: _currentFilterPredicate = x => x.TagsSearchVector.Matches(SearchTerms); break; case SearchFilterType.Title: _currentFilterPredicate = x => x.TitleSearchVector.Matches(SearchTerms); break; case SearchFilterType.Description: _currentFilterPredicate = x => x.DescriptionSearchVector.Matches(SearchTerms); break; case SearchFilterType.Defects: _currentFilterPredicate = x => x.DefectsSearchVector.Matches(SearchTerms); break; case SearchFilterType.Filenames: _currentFilterPredicate = x => x.FilenamesSearchVector.Matches(SearchTerms); break; case SearchFilterType.ArtifactTranscriptions: _currentFilterPredicate = x => x.FileContentSearchVector.Matches(SearchTerms); break; case SearchFilterType.ListedNames: _currentFilterPredicate = x => x.ListedNamesSearchVector.Matches(SearchTerms); break; case SearchFilterType.All: default: _currentFilterPredicate = x => x.AllSearchVector.Matches(SearchTerms); break; } // Get the total count using the chosen filter await using var context = await ContextFactory.CreateDbContextAsync(); _totalResults = await context.ArtifactGroupings.Where(_currentFilterPredicate).CountAsync(); _totalPages = (int)Math.Ceiling(_totalResults / (double)PageSize); // Load the first page with the chosen filter await LoadPageAsync(1); } private async Task OnPageChangedAsync(int page) { await LoadPageAsync(page); } /// /// Fetches a specific page of data from the database using the currently set filter. /// private async Task LoadPageAsync(int page) { _currentPage = page; if (_currentFilterPredicate == null) // Don't run if no search has been performed { return; } await using var context = await ContextFactory.CreateDbContextAsync(); // The query uses the dynamically set filter predicate _artifactGroupings = await context.ArtifactGroupings .Where(_currentFilterPredicate) .Include(x => x.ChildArtifactEntries) .ThenInclude(x => x.Files) .OrderBy(x => x.Id) .Skip((_currentPage - 1) * PageSize) .Take(PageSize) .ToListAsync(); StateHasChanged(); } }