Thursday, May 21, 2015

Sitecore 8 Lucene search index and implementation

Creating the index

In the "Website\App_Config\Include" folder in your Sitecore web site you will find some example files containing search index XML. 
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Core.config.example
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Master.config.example
  • Sitecore.ContentSearch.Lucene.Indexes.Sharded.Web.config.example
  • Sitecore.ContentSearch.Solr.DefaultIndexConfiguration.config.example
  • Sitecore.ContentSearch.Solr.Index.Analytics.config.example
  • Sitecore.ContentSearch.Solr.Index.Core.config.example
  • Sitecore.ContentSearch.Solr.Index.Master.config.example
  • Sitecore.ContentSearch.Solr.Index.Web.config.example
There are examples present for both Lucene and Solr and a seperate file for each Sitecore database (web, core master and analytics). In this example, I will be using Lucene for search and wish to index the web database so will begin with "Sitecore.ContentSearch.Lucene.Indexes.Sharded.Web.config.example". 
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <!-- Change this to Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex, Sitecore.ContentSearch.LuceneProvider if you would like indexes to be
               built in a temporary directory i.e. while rebuilding is happening, your old indexes work like normal until the rebuild is finished. -->
          <index id="CustomSearch" type="Sitecore.ContentSearch.LuceneProvider.LuceneIndex, Sitecore.ContentSearch.LuceneProvider">
            <param desc="name">$(id)</param>
            <param desc="folder">$(id)</param>
            <!-- This initializes index property store. Id has to be set to the index id -->
            <param desc="propertyStore" ref="contentSearch/indexConfigurations/databasePropertyStore" param1="$(id)" />
            <configuration ref="contentSearch/indexConfigurations/defaultLuceneIndexConfiguration">
    <include hint="list:IncludeTemplate">
     <template>{555CE94D-C625-4A07-9E6B-980E514BF7A0}</template>
     <template>{1F9C0E9E-BAE3-4B42-A3F2-E311D68C5B92}</template>
    </include>
    <IndexAllFields>true</IndexAllFields>
   </configuration>
            <strategies hint="list:AddStrategy">
              <!-- NOTE: order of these is controls the execution order -->
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/onPublishEndAsync" />
            </strategies>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.ContentSearch.SitecoreItemCrawler, Sitecore.ContentSearch">
                <Database>web</Database>
                <Root>/sitecore/content</Root>
              </crawler>
            </locations>
          </index>
        </indexes>
      </configuration>
    </contentSearch>
  </sitecore>
</configuration>
The first step is to give the index a name, this is set on the id attribute of index. This name is what you use to refer to the index in code and in Sitecore, so make it descriptive. Under the configuration section I will also add XML to specify which template(s) should be indexed and which fields.
<include hint="list:IncludeTemplate">
<template>{555CE94D-C625-4A07-9E6B-980E514BF7A0}</template>
<template>{1F9C0E9E-BAE3-4B42-A3F2-E311D68C5B92}</template>
</include>
<IndexAllFields>true</IndexAllFields>
In  this case I am indexing two templates (referred to by GUID) and the fact that I want all fields indexed. The index is also left to the default of "onPublishEndAsync" which means it will execute automatically at the end of a publish.

An XML file can contain multiple indexes, and can be given any name. It should be saved in the same location as the example files ("Website\App_Config\Include"), and Sitecore will automatically pick it up (if the XML is formed correctly).

Running the index

To give the index an initial run, navigate to the Sitecore desktop. Click on the main menu icon and select "Control Panel", here you will find a section on indexing.

Click on the "Indexing manager" option and you should see the index created earlier and when it was last run. You can also select the index, click "Rebuild", to manually run the index.

Accessing the index in code

Now that the index has been defined, and run it can be queried in code to return relevant items (aka a custom search on a Sitecore 8 web site). In this example I have a custom template for content pages, which contains a number of custom fields related to navigation, metadata and content. For the purposes of search the two fields that will be queried are: Title (which is the title of the page) and Content (which is the main block of HTML content for the page).
public partial class SearchResults : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var searchQuery = Request.QueryString["s"]; // search query
        var searchResults = new List<SearchResult>();

        if (!string.IsNullOrEmpty(searchQuery))
        {
            // sitecore search index
            var index = ContentSearchManager.GetIndex("CustomSearch");

            using (var context = index.CreateSearchContext())
            {
                var results =
                    context.GetQueryable<MyResultItem>()
                        .Where(
                            resultItem =>
                                resultItem.PageTitle.Like(searchQuery) ||
                                resultItem.PageContent.Contains(searchQuery))
                        .GetResults();

                foreach (var result in results)
                {
                    var searchResult = new SearchResult();
                    var item = result.Document.GetItem();

                    searchResult.Title = item.Fields["Title"].ToString();
                    string content = Common.StripTags(item.Fields["Content"].ToString()); // get page content and strip HTML tags
                    searchResult.Summary = Common.TruncateString(content, 300); // truncate content to first 300 characters

                    searchResult.Link = Sitecore.Links.LinkManager.GetItemUrl(item);
                    searchResults.Add(searchResult);
                }
            }

            // bind search results to frontend
            rptSearchResults.DataSource = searchResults;
            rptSearchResults.DataBind();
        }
        else
        {
            // handle no search query
        }
    }

    /// <summary>
    /// Search results object for binding to frontend
    /// </summary>
    public class SearchResult
    {
        public string Title { get; set; }

        public string Summary { get; set; }

        public string Link { get; set; }
    }

    /// <summary>
    /// Custom sitecore search results item with required fields
    /// </summary>
    public class MyResultItem : SearchResultItem
    {
        [IndexField("Title")]
        public string PageTitle { get; set; }

        [IndexField("Content")]
        public string PageContent { get; set; }
    }
}
In the above code sample I am accessing the CustomSearch index and getting the items where the page title is like the search query and where the page content contains the search query. These results are then cast to the list of MyResultItem object which can then be enumerated to build a list of custom object SearchResult which is then bound to the front end.

Further reading

2 comments:

  1. so followed your directions and I can rebuild in the index in the sitecore cms. I am in Sitecore 8.0. However when I try and add it to the code behind for the page. I get an error on this line.

    var index = ContentSearchManager.GetIndex("CustomSearch");

    Server Error in '/' Application.

    Could not find configuration node: search/configuration

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.InvalidOperationException: Could not find configuration node: search/configuration

    ------
    Anything I need to do in web.config to set it up?

    ReplyDelete
    Replies
    1. In your search index XML, was the index labeled: ?

      Delete