allBlogsList

Sitecore Bucket Search - Custom Views and Data

Did you know you can customize the bucket search results view in the Content Editor? Recently, I discovered this when working on building a better author experience in Sitecore. My goal was to avoid having to build an external dashboard when I was pretty sure that Sitecore could do everything I needed OOTB. Read on to learn how to create custom views for bucket search results, and how to fill those views with custom data.

Creating a custom bucket search results view in Sitecore is a documented feature, but the documentation does not go into all of the details for how to hook up the view up. To create a custom view, you just need to create a new View item under the /sitecore/system/Settings/Buckets/Views folder in your content tree. When the new View item is created, there are four (4) important fields to fill out:

  1. Header Template - this is the "open" wrapper for the HTML that will display your results. This particular field is processed once when displaying results.
  2. Item Template - this is the HTML for each result of your search. This field will be processed once for each search result.
  3. Footer Template - this is the "close" wrapper for the HTML that will display your results. This particular field is processed once with displaying results.
  4. Enabled - if this box is not checked, then the view will not be displayed as one of the available view options in the search results area.
    ViewOptions

Think of the first three fields like an ASP.NET Repeater. If you put a <table> tag in the Header Template field, you will need to remember to include a corresponding </table> tag in the Footer Template field. My recommendation is to construct the entire view using a text editor and then just copy/paste the appropriate pieces into each field.
FullHtml

Once you have the new view created, you can attach it to your bucket via the root bucket item directly. Under the Item Buckets section of your root bucket item, there is an Enabled Views field. You should be able to see your newly created view in the list of options available to choose from. Once added, provided the view is enabled, you can choose the new view option from the list of views in the search results area.

This is all straight forward, but how do you get extra information into the view? Sitecore provides a few OOTB fields you can use via the {fieldname}Placeholder nomenclature; I am not using any of those in the above HTML. Instead, I make reference to several "DynamicPlaceholder" values. Unfortunately, you cannot add other fields to the content without a bit of customization. When I first implemented this solution, I used a configuration approach to specify the fields I wanted to get back for each template. Configurations are cool, but I am not a fan of being unable to do things without needing a deployment or app pool reset. For this post, I have implemented a content driven mechanism for including additional fields in your bucket search results.

Additional data can be added to your bucket search results via the buckets.fillItem pipeline. This pipeline creates a list of key/value pairs to provide a name/value for additional information. For my purposes, I wanted a single processor that would automatically grab the information I needed based on items that have been created in Sitecore. To start, I needed to define an item to trigger the processor.

Item Template: Fill Item
Fields:

  • Result Template - specify when you want the field to be included. This is used to determine if we need to grab the extra information at all.
  • Solr Field Name - the name of the field you want to pull your extra information from. This can be a normal Solr field or a computed field. When you fill it out, you would just use the name without the type suffix (e.g. category_facet).

FillItem

Most everything else for bucket configuration lives under the /sitecore/Settings/Buckets folder, so this a good place for these items, too. Create a new folder called "Fill Item"; we will be able to put our fields in this folder.

FillItemFolder

Now that we have some fields we want to include in the search results, we need to implement a processor to use them.

public class FillExtraFields : FillItemProcessor
{
  protected readonly IBucketsFillItemRepository BucketsFillItemRepository;

  public FillExtraFields(IBucketsFillItemRepository bucketsFillItemRepository)
  {
    BucketsFillItemRepository = bucketsFillItemRepository;
  }

  public override void Process(FillItemArgs args)
  {
    var fillItems = BucketsFillItemRepository.GetFillItems();

    if (fillItems.Any() == false)
    {
      return;
    }

    foreach (var searchResultItem in args.ResultItems.OfType<SitecoreUISearchResultItem>())
    {
      var templateId = new ID(searchResultItem.TemplateId);

      if (!fillItems.ContainsKey(templateId))
      {
        continue;
      }

      var fields = fillItems[templateId];
      var dynamicFields = new List<KeyValuePair<string, string>>();

      foreach (var field in fields)
      {
        var fieldValue = searchResultItem.Fields.FirstOrDefault(resultField => resultField.Key == field).Value;
        var dynamicFieldKey = GetFieldKey(field);

        dynamicFields.Add(new KeyValuePair<string, string>(dynamicFieldKey, fieldValue?.ToString() ?? string.Empty));
      }

      searchResultItem.DynamicFields.AddRange(dynamicFields);
    }
  }

  /// <summary>
  /// Converts a solr field name to a usable DynamicPlaceholder name for rendering in a buckets view.
  /// </summary>
  /// <param name="field">Solr field name (e.g. category_facet)</param>
  /// <returns>Title case version of name, with no underscores (e.g. CategoryFacet)</returns>
  protected virtual string GetFieldKey(string field)
  {
    var fieldParts = field.Split('_');
    var builder = new StringBuilder();

    foreach (var fieldPart in fieldParts)
    {
      builder.Append(char.ToUpper(fieldPart[0]));
      builder.Append(fieldPart.Substring(1));
    }

    return builder.ToString();
  }
}

 What's going on here? Let's break it down.

  • In our bucket repository, we perform a Solr search to grab all Fill Items that have been created. The return value for this method is a Dictionary<ID, List<string>>, where ID is the template id and the list includes all the fields we want to get back.
  • Once we have the Fill Items, we iterate over each search result.
  • If the template for the search result matches any of the templates in the fillItems dictionary, then we iterate over each of the related fields and add their values to the DynamicFields list on the item.
  • Each field we retrieve is translated to TitleCase with no underscores. We will be able to reference a given field as follows: category_facet -> CategoryFacetDynamicPlaceholder.

We register the processor via a normal patch file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:search="http://www.sitecore.net/xmlconfig/search/">
  <sitecore role:require="Standalone or ContentManagement">
    <pipelines>
      <buckets.fillItem>
        <processor type="Foundation.Buckets.Pipelines.BucketsFillItem.FillExtraFields, Foundation.Buckets" resolve="true" />
      </buckets.fillItem>
    </pipelines>
    <services>
      <!-- Calendar Services -->
      <register serviceType="Foundation.Buckets.Repository.IBucketsFillItemRepository, Foundation.Buckets" implementationType="Foundation.Buckets.Repository.BucketsFillItemRepository, Foundation.Buckets" />
    </services>
  </sitecore>
</configuration>

 Once you have the processor and patch file deployed, you should be able to see your custom view with all of the data from the fields you have added to the content tree. If you want to add a new field, it is as easy as just creating a new item in Sitecore.

SearchResults

That's pretty much it. Once you have the processor in place, you should be able to create and customize search result views in the Content Editor without code deployments. We used this approach to provide a better "dashboard" experience for content authors who were primarily working within two large buckets of items. This saved us a decent about of time on the project, as we did not have to do a custom implementation of an external dashboard. 

A few final notes on custom views:

  • You can use the buckets.fillItem processor to pull information from other sources besides Sitecore. We did use it to get data from an external database. If you're going to do this, make sure to include an early exit check in your processor. The pipeline will run on EVERY search request that goes through the CM and can cause performance degradation if not handled carefully.
  • While this approach does offer the capability for no-code/no-deployment updates, I do recommend that any changes to the view or tracked fields be backported to source control.

You can check out all of the code for my demo over on GitHub: Full Code

Happy Sitecore'ing!!