#10 Displaying the Search Results
Saturday, December 22, 2018
In this article we will create a razor page that we can use to add to, delete from, and clear our Lucene.Net search index. Once we have that in place and we have created our index we will then modify our search page to actually perform and display the search results. And finally we will modify our read page so that when a user clicks on a search result they will actually be able to read the article they have selected.
Parts
- Part 29: Offset Pager Urls
- Part 28: Offset Pager Start
- Part 27: Mock Context Builder
- Part 26: Mock Repository
- Part 25: Mock Async
- Part 24: Picture Tag Helper
- Part 23: Img DPR Tag Helper
- Part 22: Img Responsive Tag Helper
- Part 21: Img Optimized Display
- Part 20: Img Optimization
- Part 19: Img Lazy Loading
- Part 18: Img Responsive
- Part 17: Bottom Nav
- Part 16: Main Nav Cookie
- Part 15: Main Nav Mobile
- Part 14: Main Nav Search
- Part 13: Main Nav Auth
- Part 12: Main Nav Anchors
- Part 11: Main Nav Logo
- Part 10: Search Results
- Part 9: Search Manager
- Part 8: Search Start
- Part 7: Seeding the Database
- Part 6: Domain Database
- Part 5: Emailing Exceptions
- Part 4: Mailkit
- Part 3: View Renderer
- Part 2: Upgrade to 2.1
- Part 1: Quick Start
Injecting our search manager
- WebUi
- Startup.cs
As we have done several times in the past it is now time to modify our startup class so that we are able to inject our search manager into any of our pages that need it (a).
Startup.cs
...
using WebUi.Features.Search;
...
namespace WebUi
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<ISearchManager, SearchManager>();
...
}
...
}
}
Testing the search manager
- WebUi
- Pages
- Lucene.cshtml.cs
- Pages
In the previous article we added all the functionality that we currently need and now it is time to be able to check and see if everything is working the way it is supposed to be. First off we will create the code behind for a new razor page (b).
Index.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
using WebUi.Domain;
using WebUi.Features.Search;
using WebUi.Features.Search.Searchables;
using WebUi.Infrastructure;
namespace WebUi.Pages
{
public class IndexModel : PageModel
{
private readonly IDomainRepository _domainRepository;
private readonly ISearchManager _searchManager;
private readonly IViewRenderer _viewRenderer;
public IndexModel(IDomainRepository domainRepository, ISearchManager searchManager, IViewRenderer viewRenderer)
{
_domainRepository = domainRepository;
_searchManager = searchManager;
_viewRenderer = viewRenderer;
}
public string Action { get; set; }
public void OnGet()
{
Action = "Get";
}
public async Task OnPostIndexAsync()
{
Action = "index";
var articles = await _domainRepository.Articles.ToListAsync();
var searchables = articles.Select(x => new SearchableArticle(x, _viewRenderer)).ToArray<Searchable>();
_searchManager.AddToIndex(searchables);
}
public void OnPostClear()
{
Action = "clear";
_searchManager.Clear();
}
public async Task OnPostDeleteAsync()
{
Action = "delete";
var article = await _domainRepository.Articles.FirstAsync();
var searchable = new SearchableArticle(article, _viewRenderer);
_searchManager.DeleteFromIndex(searchable);
}
}
}
- WebUi
- Pages
- Lucene.cshtml
- Pages
Next up is the view for the page (c). Its not much to look at but it will give us the ability to interact with our search manager.
Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Lucene";
}
<h2>@Model.Action</h2>
<form asp-page="Index" method="post">
<button type="submit" asp-page-handler="Index">Index</button>
</form>
<form asp-page="Index" method="post">
<button type="submit" asp-page-handler="Delete">Delete</button>
</form>
<form asp-page="Index" method="post">
<button type="submit" asp-page-handler="Clear">Clear</button>
</form>
If we press the index button now we should see a few files being created in our Lucene_Index folder (d).
Time to actually search the index and display the results
- WebUi
- Pages
- Search.cshtml.cs
- Pages
Now we will return to our search page and modify it so that an actual call to the Search
method of our search manager will be made and the results, if any, will be parsed (e).
Search.cshtml.cs
...
using System.Linq;
using WebUi.Features.Search.Searchables;
namespace WebUi.Pages
{
public class SearchModel : PageModel
{
private readonly ISearchManager _searchManager;
public SearchModel(ISearchManager searchManager)
{
_searchManager = searchManager;
}
...
public void OnGet()
{
Results = _searchManager.Search(Search, 0, 100, Searchable.AnalyzedFields.Values.ToArray());
foreach (var result in Results.Data)
{
result.Parse(x =>
{
result.DescriptionPath = x.Get(Searchable.FieldStrings[Searchable.Field.DescriptionPath]);
result.LinkText = x.Get(Searchable.FieldStrings[Searchable.Field.Title]);
result.LinkHref = x.Get(Searchable.FieldStrings[Searchable.Field.Href]);
});
}
}
}
}
Modifying our read page
- WebUi
- Pages
- Articles
- Read.cshtml
- Articles
- Pages
When the search results are displayed we will be allowing the user to click on one and be directed to the read page to be able to read the article. For this to work we need to update our read page so that it will be accepting an id parameter (f).
Read.cshtml
@page "{id:int}"
...
- WebUi
- Pages
- Articles
- Read.cshtml.cs
- Articles
- Pages
Last but not least we need to update the code behind so that a request is made to the database and depending on the result of that request the appropriate response is returned (g).
Read.cshtml.cs
using Microsoft.AspNetCore.Mvc;
...
namespace WebUi.Pages.Articles
{
public class ReadModel : PageModel
{
...
[BindProperty(SupportsGet = true)] public int Id { get; set; }
...
public async Task<IActionResult> OnGetAsync()
{
var article = await _repository.Articles
.FirstOrDefaultAsync(x => x.Id == Id);
if (article == null)
{
return NotFound();
}
Author = article.Author;
Created = article.Created;
Id = article.Id;
Title = article.Title;
ContentPath = $"Content{Id}.cshtml";
IntroPath = $"Intro{Id}.cshtml";
return Page();
}
}
}