Utilizing Vulcan for Listing Pages in Episerver
John McKillip#Episerver, #Code
Learn how utilizing Vulcan for the article listing page allowed us to quickly build a simple, flexible way to return sets of paginated Episerver content.
Listing pages are a pretty common type that you find in most Episerver applications. Examples would be news article or event listing pages. A common feature on all listing pages is pagination, or, the ability to break the data into digestible chunks to navigate through. When developing a listing page, performance is a big thing to consider. If you are dealing with large amounts of data, you want to be as efficient as possible when listing it.
I was working on an Episerver build recently that was using Vulcan as the search client. One of the page types that I had to build was a news article listing. Since I was already using Vulcan for the search functionality, I decided to also use it for my listing pages. It ended up being a really nice solution that was fast and worked well for pagination of large data sets.
There isn't a ton of documentation on how to use Vulcan like this, so, I wanted to share a bit of code. This article assumes that you are familiar with Episerver and know how to install the Vulcan search client.
First, I created two interfaces, one for each article item and one for the article item list.
using EPiServer.Core;
namespace Diagram.Business.Interfaces
{
public interface IArticleListItem
{
ContentReference Image { get; set; }
DateTime Date { get; set; }
string Title { get; set; }
string Teaser { get; set; }
ContentReference Link { get; set; }
}
public interface IArticleList
{
IEnumerable<IArticleListItem> Items { get; set; }
long Total { get; set; }
}
}
Next, I built the service that gets the paginated data and returns it to the controller. Something to point out here is that this is essentially the same thing you would do to build a keyword type search with Vulcan. Since there is no keyword to search, we pass an empty string to our query so that it returns everything that is of type ArticleDetailPageData.
The caveat to that and what makes this whole thing so good is that you can pass Vulcan .Skip and .Take, which works like the LINQ extensions of the same name, except that you don't have to get all the data first, then sort. All you have to do in the code is tell Vulcan what page you are on and how many items per page and it returns only what you need, already sorted the way you tell it to. This example service has a very simple sort descriptor that returns the articles sorted by descending release date.
namespace Diagram.Business.Services
{
[EPiServer.ServiceLocation.ServiceConfiguration(typeof(IArticleService))]
public class ArticleService : IArticleService
{
private readonly IArticleList _articleList;
private readonly IContentLoader _contentLoader;
private readonly IVulcanHandler _vulcanHandler;
private readonly Func<IArticleListItem> _articleListItemFactory;
public ArticleService(IArticleList articleList, IContentLoader contentLoader, IVulcanHandler vulcanHandler, Func<IArticleListItem> articleListItemFactory)
{
_articleList = articleList;
_contentLoader = contentLoader;
_vulcanHandler = vulcanHandler;
_articleListItemFactory = articleListItemFactory
}
public IArticleList GetArticles(int pageSize, int pageNumber, ContentReference scope)
{
var client = _vulcanHandler.GetClient();
var typesToSearchFor = typeof(ArticleDetailPageData).GetSearchTypesFor();
var searchScope = new ContentReference[] { scope };
SortDescriptor<ArticleDetailPageData> sort = new SortDescriptor<ArticleDetailPageData>().Descending(f => f.ReleaseDate);
QueryContainer query = new QueryContainerDescriptor<ArticleDetailPageData>().FilterForPublished<IContent>();
var articleHits = client.SearchContent<ArticleDetailPageData>(d => d
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.Fields(fs => fs.Field(p => p.ContentLink).Field(p => p.Name)) // only return contentLink
.Query(q => query)
.Sort(s => sort),
principleReadFilter: PrincipalInfo.Current.Principal,
typeFilter: typesToSearchFor.Distinct(),
rootReferences: searchScope
);
_articleList.Total = articleHits.Total;
_articleList.Items = articleHits.Hits.SelectMany(CreateArticleModelFromVulcan).ToList();
return _articleList;
}
private IEnumerable<IArticleListItem> CreateArticleModelFromVulcan(IHit<IContent> responseItem)
{
ContentReference contentReference = null;
if (ContentReference.TryParse(responseItem.Id, out contentReference))
{
var article = _contentLoader.Get<ArticleDetailPageData>(contentReference);
var articleItem = _articleListItemFactory();
articleItem.Date = article.ReleaseDate;
articleItem.Image = article?.FeaturedImage ?? SiteHelper.SiteSettings.FallbackArticleImage;
articleItem.Link = article.ContentLink;
articleItem.Teaser = article?.Teaser ?? string.Empty;
articleItem.Title = article?.Title ?? article.Name;
yield return articleItem;
}
}
}
}
Now this service can be injected into any controller that needs to list articles.
Utilizing Vulcan for the article listing page allowed me to quickly build a simple, flexible way to return sets of paginated Episerver content. If you're interested in getting started using Vulcan Search in Episerver, check out Brad McDavid's blog. Want to know how we can use approaches like this to make your website more efficient? Leave a comment below.
Related Posts
Custom Fields and ElasticSearch
Diagram's Ryan Duffing offers a tutorial on indexing and retrieving custom fields with Epinova.Elasticsearch for Optimizely (formerly Episerver).
What is Umbraco CMS?
Migrating your content management system? Why not consider Umbraco? Learn about this .NET CMS, pricing and why it's the right choice for your organization.
Results Matter.
We design creative digital solutions that grow your business, strengthen your brand and engage your audience. Our team blends creativity with insights, analytics and technology to deliver beauty, function, accessibility and most of all, ROI. Do you have a project you want to discuss?
Like what you read?
Subscribe to our blog "Diagram Views" for the latest trends in web design, inbound marketing and mobile strategy.