Friday, November 6, 2015

Indexing a computed field with Sitecore and Lucene

Out of the box it can be easy to configure simple Sitecore Lucene Search Indexes, that is using the standard data types for the fields you want indexed. However when it comes to more complex and even custom types you may need to setup a computed field to be indexed.

Take for example an image field, perhaps each page on your Sitecore web site has a primary image/thumbnail that you would like to expose in the results of your search engine. You could get the full item for each search result to get access to this field. However to save that call and have better performance it is possible to index the image url (and even a version of it with maximum width/height using Sitecore's media library). It is also worth noting that each field added to an index will cause that index to grow (which leads to more serve resources to generate, update and store the index itself). 

The computed field code

The Lucene index will need a custom class which inherits from IComputedIndexField, this class is what Lucene will use to index the field. The class will need to follow a set structure (at minimum):
  • String property: FieldName
  • String property: ReturnType
  • Method: ComputeFieldValue()
namespace My.Project.Web.ComputedFields
    public class ComputedImageField : IComputedIndexField
        /// <inheritdoc />
        public string FieldName { get; set; }
        /// <inheritdoc />
        public string ReturnType { get; set; }

        /// <inheritdoc />
        public object ComputeFieldValue(IIndexable indexable)
            Item item = indexable as SitecoreIndexableItem;
            if (item != null)
                ImageField field = item.Fields[FieldName];
                if (field != null)
                    //return SitecoreMediaFunctions.GetMediaPath(field, 100, 100);

                    var options = new MediaUrlOptions { MaxHeight = 100, MaxWidth = 100 };
                    var mediaPath = string.Empty;
                    if (field.MediaItem != null)
                        var resizePath = MediaManager.GetMediaUrl(field.MediaItem, options);
                        mediaPath = HashingUtils.ProtectAssetUrl(resizePath);
                        return mediaPath;
            return null;
In the above example, we are getting the field as an ImageField and using Sitecore media functions to return the image URL with a maximum height/width of 100px. This means that if a field is set to use this computed field class in a Sitecore lucene index definition, the URL for that image will be stored in the index and available for querying and displaying in search results (without calling for the full item from Sitecore). A null is returned in the case that no image field is available for indexing.

The lucene search index

In the search index under  a <fields hint="raw:AddComputedIndexField"> section you will need to index a field and reference the class file created above.
<fieldMap type="Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch">
<fieldNames hint="raw:AddFieldByFieldName">
  <field fieldName="title" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String"
        settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider"
        patch:after="field[last()]" />
<fields hint="raw:AddComputedIndexField">
  <field fieldName="thumbnail" storageType="yes" indexType="untokenized"
        patch:after="field[last()]">My.Project.Web.ComputedFields.ComputedImageField, My.Project.Web</field>
In the extract (from a Lucene index configuration), you will notice that computed fields are referenced in their own section. In this example the field name is thumbnail, the class of the computed field is My.Project.Web.ComputedFields.ComputedImageField and the assembly for the DLL which contains the class is My.Project.Web.

The search code

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

using (var context = index.CreateSearchContext())
  var searchResults = context.GetQueryable<SearchItem>().Where(x => x.ThumbnailUrl != null)
public class SearchItem
  public string ThumbnailUrl { get; set; }
In the code sameple above (which is been greatly simplified), you can see the thumbnail field is a string in the custom SearchItem class (this is the class your search results are returned as, so all indexed fields you want available would need to be added here). The search code is simply getting all Sitecore items from the index with a thumbnail, normally the search logic would go here.

Final notes

Out of the box there are a number of fields which can be indexed without the need of custom code. Computed indexes are great for custom logic that does not work out of the box, but can lead to slower performance if used with every other field.

No comments:

Post a Comment