Basically you need to set maxResults to the number of results per page (multiplied by the current pagenumber), and then skip through the ones you have already shown.
//for example purposes, we want to show page #4 (which is pageIndex of 3)
var pageIndex = 3;
//for this example, the page size is 10 items
var pageSize = 10;
var searchResult = searchProvider.Search(criteria,
//don't return more results than we need for the paging
//this is the 'trick' - we need to load enough search results to fill
//all pages from 1 to the current page of 4
maxResults: pageSize*(pageIndex + 1));
//then we use the Skip method to tell Lucene to not allocate search results
//for the first 3 pages
var pagedResults = searchResult.Skip(pageIndex*pageSize);
var totalResults = searchResult.TotalItemCount;
Here is a complete solution. Hope it helps someone.
This is the way I like to handle this.
I create a model called BlogSearch. This will contain the search fields, the paging information, and the blog article results
using System.Collections.Generic;
namespace Website.Core.Models.EntityModels
{
public class BlogSearch
{
public string Tag { get; set; }
public string Keywords { get; set; }
//return fields
public List<BlogDetailsPage> Articles { get; set; }
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public BlogSearch()
{
CurrentPage = 1;
}
}
}
I then have an Umbraco helper / service / component. Whatever you want to call it. This performs the search login and handles the paging.
using System;
using Umbraco.Core;
using Umbraco.Examine;
using Examine;
using Umbraco.Web;
using Website.Core.Models.EntityModels;
using Examine.Search;
using System.Linq;
using System.Collections.Generic;
using Website.Core.Models;
using Umbraco.Web.PublishedCache;
namespace Website.Core.Helpers
{
public interface IBlogHelper
{
BlogSearch Search(BlogSearch blogSearch);
}
public class BlogHelper : IBlogHelper
{
private readonly IUmbracoContextFactory _umbracoContextFactory;
private readonly IExamineManager _examineManager;
public BlogHelper(IUmbracoContextFactory umbracoContextFactory, IExamineManager examineManager)
{
_umbracoContextFactory = umbracoContextFactory;
_examineManager = examineManager;
}
public BlogSearch Search(BlogSearch blogSearch)
{
IExamineManager examineManager = ExamineManager.Instance;
if (!examineManager.TryGetIndex(Constants.UmbracoIndexes.ExternalIndexName, out IIndex index))
throw new InvalidOperationException($"No index found by name {Constants.UmbracoIndexes.ExternalIndexName}");
var searcher = index.GetSearcher();
var criteria = searcher.CreateQuery(IndexTypes.Content, BooleanOperation.And);
var examineQuery = criteria.NodeTypeAlias("blogDetailsPage");
if (!string.IsNullOrEmpty(blogSearch.Tag))
examineQuery.And().Field("tags", blogSearch.Tag);
if (!string.IsNullOrEmpty(blogSearch.Keywords))
{
if (blogSearch.Keywords.Contains(" "))
{
string[] terms = blogSearch.Keywords.Split(' ');
examineQuery.And().GroupedOr(new[] { "contents" }, terms);
}
else
{
examineQuery.And().Field("contents", blogSearch.Keywords.MultipleCharacterWildcard());
}
}
examineQuery.OrderByDescending(new SortableField[] { new SortableField("sortableDate") });
int pageIndex = blogSearch.CurrentPage - 1;
int pageSize = blogSearch.ItemsPerPage;
ISearchResults searchResult = examineQuery.Execute(maxResults: pageSize * (pageIndex + 1));
IEnumerable<ISearchResult> pagedResults = searchResult.Skip(pageIndex * pageSize);
int totalResults = Convert.ToInt32(searchResult.TotalItemCount);
blogSearch.TotalItems = totalResults;
blogSearch.TotalPages = (totalResults + blogSearch.ItemsPerPage - 1) / blogSearch.ItemsPerPage;
if(pagedResults != null && pagedResults.Count() > 0)
blogSearch.Articles = GetBlogArticlesFromSearch(pagedResults);
return blogSearch;
}
private List<BlogDetailsPage> GetBlogArticlesFromSearch(IEnumerable<ISearchResult> pagedResults)
{
List<BlogDetailsPage> blogDetailsPages = new List<BlogDetailsPage>();
using (UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext())
{
foreach (ISearchResult result in pagedResults)
{
if (int.TryParse(result.Id, out int nodeId))
{
IPublishedContentCache contentHelper = umbracoContextReference.UmbracoContext.Content;
BlogDetailsPage blogDetailsPage = contentHelper.GetById(nodeId) as BlogDetailsPage;
if(blogDetailsPage != null)
{
blogDetailsPages.Add(blogDetailsPage);
}
}
}
}
return blogDetailsPages;
}
}
}
Here is the composer to go along with the service above.
using Umbraco.Core;
using Umbraco.Core.Composing;
namespace Website.Core.Events
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ExamineComposerPublic : IUserComposer
{
public void Compose(Composition composition)
{
composition.Components().Append<ExamineComponents>();
}
}
}
And finally this is an example of my page controller which calls my BlogHelper service.
using Website.Core.Models;
using Website.Core.Helpers;
using Website.Core.Models.EntityModels;
using System.Web.Mvc;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace Website.Core.Controllers
{
public class BlogListPageController : RenderMvcController
{
private readonly ISiteHelper _siteHelper;
private readonly IBlogHelper _blogHelper;
public BlogListPageController(ISiteHelper siteHelper, IBlogHelper blogHelper)
{
_siteHelper = siteHelper;
_blogHelper = blogHelper;
}
[HttpGet]
public override ActionResult Index(ContentModel model)
{
string tag = string.IsNullOrEmpty(Request["tag"]) ? string.Empty : Request["tag"];
string keywords = string.IsNullOrEmpty(Request["keywords"]) ? string.Empty : Request["keywords"];
string page = string.IsNullOrEmpty(Request["page"]) ? "1" : Request["page"];
if(!int.TryParse(page, out int pageNumber))
pageNumber = 1;
var vm = new BlogListPageViewModel(_siteHelper, model.Content) { };
BlogSearch blogSearch = new BlogSearch();
blogSearch.CurrentPage = pageNumber;
blogSearch.ItemsPerPage = vm.PageContent.ItemsPerPage;
blogSearch.Tag = tag;
blogSearch.Keywords = keywords;
vm.BlogSearch = _blogHelper.Search(blogSearch);
return View("/Views/blogListPage.cshtml", vm);
}
}
}
How can paginate with Umbraco 8 Examine
I could not find out any overload of ISearcher.Search or IQueryExecutor.Execute that support returning paging.
Any idea about the search result's pagination with Umbraco 8?
Note that this is a custom search - I want to search on some specific document types.
Hi there :)
Check out this article: https://shazwazza.com/post/paging-with-examine/
Basically you need to set maxResults to the number of results per page (multiplied by the current pagenumber), and then skip through the ones you have already shown.
Thank Søren Kottal!
How ISearchResult can be casting to IPublishedContent for rendering in view? I could not see ISearchResult has its structure for easy rendering.
Hi again
var pagedResultsAsContent = Umbraco.Content(pagedResults.Select(x => x.Id));
Gives you a list of IPublishedContent from the ids in your ISearchResult
Here is a complete solution. Hope it helps someone.
This is the way I like to handle this.
I create a model called BlogSearch. This will contain the search fields, the paging information, and the blog article results
I then have an Umbraco helper / service / component. Whatever you want to call it. This performs the search login and handles the paging.
Here is the composer to go along with the service above.
And finally this is an example of my page controller which calls my BlogHelper service.
And a very simple view would look something like this.
is working on a reply...