This is part two in a series on making an awesome search experience in Sitecore. In the first part, I showed the basics of getting some demo data, which can be used for the search application. I've decided on using "The Movie DB" and its open API for that purpose. Check out this first part

In this part, it's time to define the index, making the data available for the Sitecore Content search API. To do this, lets first look at what fields are in items that should be indexed. The items are based on the Movie template, which has these fields:

  • Title, single-line, text
  • Original title, single-line, text
  • Tagline, single-line, text
  • Body, rich text, text
  • Image, image
  • Vote average, single-line, double
  • Vote count, single-line, int
  • Genres, multilist
  • Tmdb id, single-line, int
  • Imdb id, single-line
  • Runtime, single-line, int
  • Status, single-line
  • Release data, date
  • Production Companies, multilist
  • Production countries, multilist

Most fields are straightforward. Fields that are single-line and rich text, and should just be treated as text, should be added to the index in the same way. I've based the index definition on my earlier blog post on Defining a custom index in Sitecore, the absolute minimum.

The fieldMap section of the index definition looks like this (check out the entire MovieShopSearch.config at GitHub):

<fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
  <fieldNames hint="raw:AddFieldByFieldName">
    <field fieldName="_uniqueid"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="original title"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="title"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="tagline"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="body"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="vote average"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.Double" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="vote count"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.Int32" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="genres"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="tmdb id"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.Int32" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="imdb id"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="runtime"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.Int32" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="status"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="release date"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="production companies"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
    <field fieldName="production contries"            storageType="YES" indexType="TOKENIZED"    vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider">
      <analyzer type="Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider" />
    </field>
  </fieldNames>
</fieldMap>

The ones that are a little special are the ones that should be treated as integers and doubles. I've defined them in the index as System.Int32 and System.Double. They are Vote average, Vote Count, tmdb id and runtime. Also notice that fields like release date (which is a date), and the multilist fields (genres, production Companies and production countries) are stored as a string. Sitecore has converters that will help keeping track on the fields and their types. One of the cool things about that is that when you define your custom classes for searching, Sitecore will handle the conversion between the index in Lucene and your .NET classes. Let's look at that.

I've created a class that drives from Sitecore.ContentSearch.SearchTypes.SearchResultItem, and that implements the fields like this:

public class MovieSearchResultItem:SearchResultItem
{
    [IndexField("original title")]
    public string OriginalTitle { get; set; }

    [IndexField("title")]
    public string Title{ get; set; }

    [IndexField("tagline")]
    public string Tagline { get; set; }

    [IndexField("body")]
    public string Body { get; set; }

    [IndexField("vote average")]
    public double VoteAverage { get; set; }

    [IndexField("vote count")]
    public int VoteCount { get; set; }

    [IndexField("genres")]
    public List<ID> Genres { get; set; }

    [IndexField("tmdb id")]
    public int TmdbId { get; set; }

    [IndexField("imdb id")]
    public string ImdbId { get; set; }

    [IndexField("runtime")]
    public int Runtime { get; set; }

    [IndexField("status")]
    public string Status{ get; set; }

    [IndexField("release date")]
    public DateTime ReleaseDate { get; set; }

    [IndexField("production companies")]
    public List<ID> ProductionCompanies { get; set; }

    [IndexField("production contries")]
    public List<ID> ProductionContries { get; set; }
}

The class defines which fields from the index that should be mapped to which .NET properties. When you define your .NET classes, simply specifying the property you need, Sitecore will do the conversion.

With the code above, and the search index defined, we can now utilize search in our application. I.e. the following will give us top 10 of items sorted by average votes.

var indexName = "movieshop_master";
            using (var context = ContentSearchManager.GetIndex(indexName).CreateSearchContext())
            {
                var query = context.GetQueryable<MovieSearchResultItem>()
                    .OrderByDescending(x => x.VoteAverage)
                    .Take(10);
                return query.ToList();
            }

 Awesome and simple.

In the next part of these blogposts, I'll show a custom field for an imageurl. Custom fields are dead simple to implement, and very powerful, as it takes some custom data (meaning not a field value), and storing it in the index, making it searchable.

Check out the repository at for a full source view