Three Computed Fields that Every Sitecore Project Needs

Data access in Sitecore is nearly always done through Sitecore's search APIs. This is a best practice in Sitecore as it significantly increases performance over the more direct item APIs when loading more than a few items at a time. Out of the box, Sitecore doesn't index some of the commonly used fields that exist on the items themselves. That means that search queries can't be run against them.

Sort Order
Adding the sort order from the Content Tree is critical. Content Editors often order items in the order they want them to display. The search results will return in order of relevance without a sort order computed field.

namespace AgileReaction.Foundation.Search.ComputedFields
{
    using Sitecore.ContentSearch;
    using Sitecore.ContentSearch.ComputedFields;
    using Sitecore.Data.Items;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    public class SortOrderComputedField : IComputedIndexField
    {
        private const int DefaultSortOrderForStringValues = 100;

        public object ComputeFieldValue(IIndexable indexable)
        {
            var item = (Item)(indexable as SitecoreIndexableItem);
            if (item == null) return null;

            int sortOrder;
            return int.TryParse(item[Sitecore.FieldIDs.Sortorder], out sortOrder)
                ? sortOrder
                : DefaultSortOrderForStringValues;
        }

        public string FieldName { get; set; }

        public string ReturnType { get; set; }
    }
}

Inherited Templates
If a developer needs to write a query to filter on a specific template like carousel items, Call to Actions, or anything else really, they will need this computed field.

namespace AgileReaction.Foundation.Search.ComputedFields
{
    using Sitecore.ContentSearch;
    using Sitecore.ContentSearch.ComputedFields;
    using Sitecore.ContentSearch.Utilities;
    using Sitecore.Data.Items;
    using Sitecore.Diagnostics;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    public class TemplatePathComputedField : IComputedIndexField
    {
        public string FieldName { get; set; }

        public string ReturnType { get; set; }

        public object ComputeFieldValue(IIndexable indexable)
        {
            return GetAllTemplates(indexable as SitecoreIndexableItem);
        }

        private object GetAllTemplates(Item item)
        {
            Assert.ArgumentNotNull(item, "item");
            Assert.IsNotNull(item.Template, "Item template not found.");
            List<string> list = new List<string> { IdHelper.NormalizeGuid(item.TemplateID) };
            AddTemplateToList(item.Template, ref list);
            return list;
        }

        private void AddTemplateToList(TemplateItem templateItem, ref List<string> list)
        {
            if (templateItem.BaseTemplates == null)
            {
                return;
            }

            foreach (var baseTemplateItem in templateItem.BaseTemplates)
            {
                list.Add(IdHelper.NormalizeGuid(baseTemplateItem.ID));
                AddTemplateToList(baseTemplateItem, ref list);
            }
        }
    }
}

Lowercase Item Names
At first glance this computed field may look like it isn't needed. You'll realize it's importance though the first time you try to find an item from a wildcard url. Think items in a bucket that map to a specific route like /events/{item-name}. Often times the item name in the url will be lowercase but the item itself will be title case in Sitecore.

namespace AgileReaction.Foundation.Search.ComputedFields
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Sitecore.ContentSearch;
    using Sitecore.ContentSearch.ComputedFields;
    using Sitecore.Data.Items;

    public class ItemNameLowerCaseComputedField : IComputedIndexField
    {
        public object ComputeFieldValue(IIndexable indexable)
        {
            var item = (Item)(indexable as SitecoreIndexableItem);
            return item?.Name.ToLowerInvariant();
        }

        public string FieldName { get; set; }

        public string ReturnType { get; set; }
    }
}

Putting it all together
Add this config file to the /App_Config/Include folder to get the new computed index fields up and running. Don't forget to re-index after publishing :)

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <contentSearch>
      <indexConfigurations>
        <defaultSolrIndexConfiguration>
          <documentOptions type="Sitecore.ContentSearch.SolrProvider.SolrDocumentBuilderOptions, Sitecore.ContentSearch.SolrProvider">
            <fields hint="raw:AddComputedIndexField">
              <field fieldName="sortorder" returnType="int">AgileReaction.Foundation.Search.ComputedFields.SortOrderComputedField, AgileReaction.Foundation.Search</field>
              <field fieldName="_templates" storageType="yes" indexType="untokenized">AgileReaction.Foundation.Search.ComputedFields.TemplatePathComputedField, AgileReaction.Foundation.Search</field>
              <field fieldName="itemnamelowercase" stored="true" indexed="true" indexType="untokenized" returnType="string">AgileReaction.Foundation.Search.ComputedFields.ItemNameLowerCaseComputedField, AgileReaction.Foundation.Search</field>
            </fields>
          </documentOptions>
        </defaultSolrIndexConfiguration>
      </indexConfigurations>
    </contentSearch>
  </sitecore>
</configuration>