How to Wire up Autocomplete in Sitecore | Perficient Digital

How to Wire up Autocomplete in Sitecore

Popular search engines make use of autocomplete. It helps predict what users are looking for. If a user is unsure of what they are trying to find, autocomplete can help users find exactly what they are trying to find. This blog post will show you how to provide this predictive feature for your search box in a Sitecore solution.

Autocompleteexample

Step 1 – Register the Route in the Initialize Pipeline

Patch into the Sitecore config a definition for the route registration process you are going to create like so:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor
          type="SampleProject.Feature.Search.Pipelines.Initialize.RegisterAutocompleteRoute, SampleProject.Feature.Search"
          patch:before="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc']"
          resolve="true" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>

Note that we are patching this process to be performed before the Sitecore InitializeRoutes process.

What does this RegisterAutocompleteRoute processor code look like? It can look something like this:

using System.Web.Mvc;
using System.Web.Routing;
using Sitecore.Pipelines;

namespace SampleProject.Feature.Search.Pipelines.Initialize
{
  public class RegisterSearchSuggestionsRoute
  {
    public virtual void Process(PipelineArgs args)
    {
      Register(RouteTable.Routes);
    }

    private static void Register(RouteCollection routes)
    {
      routes.MapRoute("GetAutocompleteSuggestions",
            "sampleprojectapi/search/{action}",
            new {controller = "Autocomplete" });
    }
  }
}

The code above adds my custom route the RouteTable. Once my C# code is all set up, I will be calling this route in the front-end in our Javascript. Warning: When you register your route and specify the controller to find the method to execute, be sure to drop the Controller suffix. In the code above, I am requesting to call a method in the AutocompleteController. I do not need to worry about adding the Controller suffix because .NET works its magic behind the scenes to find the controller with the Autocomplete name.

Step 2 – Create an HttpGet Method in the Controller

Now that we have properly registered our route, we need to make the method that will be called when the route is requested. In Step 1 above, I specified that the method must live in the AutocompleteController, so let’s make that controller now.

namespace SampleProject.Feature.Search.Controllers
{
  public class AutocompleteController
  {     
    private const int NumSuggestionsToProvide = 4;
    private const int NumSuggestionsToCheck = 12;

    [HttpGet]
    public JsonResult GetAutoCompleteSuggestions(string searchTerms)
    {
      // whatever logic you need to bring back suggestions
      // my example is below

      return !string.IsNullOrWhiteSpace(searchTerms) 
             ? Json(GetSuggestions(searchTerms), JsonRequestBehavior.AllowGet) 
             : Json(new EmptyResult(), JsonRequestBehavior.AllowGet);
    }

    private IEnumerable<string> GetSuggestions(string searchTerms)
    {
      using (var context = GetSearchContext())
      {
        var baseQuery = PredicateBuilder.True<SearchResultItem>();                

        var searchTermsTokens = !string.IsNullOrEmpty(searchTerms) 
          ? searchTerms.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries) 
          : new string[] { "" };

        foreach (var searchTermsToken in searchTermsTokens)
        {
          if (!string.IsNullOrEmpty(searchTermsToken))
          {
            // build a predicate that requires item names to match user-input
            var containsOrEqualsSearchTermQuery = PredicateBuilder.True<SearchResultItem>();

            containsOrEqualsSearchTermQuery = containsOrEqualsSearchTermQuery
              .Or(i => i.Name.Contains(searchTermsToken));
            containsOrEqualsSearchTermQuery = containsOrEqualsSearchTermQuery
              .Or(i => i.Name.Equals(searchTermsToken));

            baseQuery = baseQuery.And(containsOrEqualsSearchTermQuery);
          }
        }

        return context.GetQueryable<SearchResultItem>().Where(baseQuery)
          .Select(s => s.Name).Take(NumSuggestionsToCheck)
          .ToList().Distinct().Take(NumSuggestionsToProvide);
      }
    }
  }
}

Your HttpGet method can contain whatever logic you need to get the proper autocomplete suggestions you want. In the example above, I am going to the search index to compare user-input to the names of Sitecore items and returning at most 4 suggestions. I provide this example for a number of reasons. First, I wanted to show that I am returning a JSON response that the front-end can easily digest and retrieve whatever information is needed. Second, the user may type in multiple search terms into the search box. You will need to perform a .Split() on the user-input to get each individual term from the search box. You will then need to build the predicate to make sure the autocomplete includes every term the user inputted. Third, in my example, there may be multiple Sitecore items with the same name in the index. That’s why I needed to take a subset of the results (12 in the example above) and make a .Distinct() call to make sure I have 4 unique suggestions to provide. Lastly, take note of the parameter in the GetAutocompleteSuggestions() method signature. When the front-end calls this method, the query string parameter must match the parameter found in this signature: searchTerms.

Step 3 – Calling the Route in the Front-End

The final step to wiring up autocomplete is to make the call to the route in the front-end on the keyup event. On the keyup event, you will construct an XMLHttpRequest to make a call to the route we created above and retrieve the JSON from that call (Example: https://<home-page-url>/sampleprojectapi/search/getautocompletesuggestions?searchTerms=<user-input>). You can then display the values found in the JSON somewhere in your markup to show the predictive text. There are two recommendations I would like to call out here. First, I would recommend not making any calls to the route until the user types in at least 3 characters. Providing suggestions for a shorter length may prove useless since a single vowel keystroke would match on a lot of items. Second, set a slight delay on making a call to the route in case the user is typing quickly.

Conclusion

Setting up autocomplete for a search box with Sitecore is as simple as that! Your users will greatly appreciate this predictive feature to help them find what they are trying to find. Have any questions about this blog post? Hit up the comment section, and I will get back to you.

Leave a Reply