Tuesday, March 15, 2016

Sitecore faceting search results based on top level site architecture

Many web sites will have a structure/site architecture which contains a number of top level categories of which each contains the relevant pages/content. When a user is searching that same web site, it can be a useful feature to have a facet which allows the user to narrow down the results by category (aka the top level architecture).

An example of this would be the following diagram:

It's a simple example, yet illustrates a site with three categories at the top level (about, products and services). The goal here would then to have a feature on the search results page that allows faceting on these categories (dynamically). So if a given search does not have results available under about, that category won't show and you also see a results count for available categories.

 This is actually quite simple to implement in Sitecore using Lucene and improves the user experience.

Indexing the category

To allow us to facet on this category, we will need to create a computed index field for Lucene. This field will store the top level category as text (rather than an item GUID).

In the computed index field code, we first check to see if the current item is a child of the Sitecore home node, if so we check if the template is a landing page (this is the template for top level items). If it is a landing page we return it's display name, as this is the category it falls under, if not we return null as the page is a system page and won't appear in search.

For all other items, we use a recursive function that navigates to the parent item until we hit a landing page and then return this. There is also a check here to ensure we don't go past the top level Sitecore item in the tree (if so a null is returned).

The end result is that each item in the index (non media items of course) will have a field called categoryfacet which contains the display name for the top level item the given page falls under.

You may notice that the computed index field is actually defined twice in the search index XML, this is because we need to use the LowerCaseKeywordAnalyzer to ensure the facet name is not stored untokenized (two words for the word New Zealand for example).

Getting the facets for a given search

Now that we have indexed the content for this facet, we need to get the available facets (with counts) for a given search.

In the code example we are using predicate logic to query our search index. We use the Page function on the search results to return the first X results but it's the following line which gets us the facets for all search results:
var searchFacets = searchContext.GetQueryable<SearchModel>().Where(searchPredicate).FacetOn(x => x.Type).FacetOn(x => x.Category).GetFacets();
We then get the facet named categoryfacet (as this is our index fields name) and loop through the results (which are not null), to get each facet and a count.

Faceting the search

Once we display the category facets on the front end to the user, they can then narrow the search results by selecting which category/categories their results should appear under. The search code can then include additional logic, to only show results which fall under the selected categories:

Now the users have an additional facet to further narrow their results, which empowers them to find the information they require faster and with a better experience.

No comments:

Post a Comment