How to create a Computed index field

In Sitecore 7.1 the new indexes are very powerful and allow you to do stuff easily that in Sitecore 6 took quite an effort. An example of that is if you want to tag items and show lists of the items based on the tagging. This can be achieved with a computed index field and some good old Sitecore work. So lets look at the first part of the task and add a tag to the index at hand.

Tagging items

In order to tag items in Sitecore we first need to create a template for the tag itself. In this case I am creating a tag called “ProductType”. But remember, this is not only useful for products, anything could be tagged and listed. For example articles could be tagged with keywords or a region it applies to and so on.

So lets start by creating the data template for the product type. I called mine __ProductType. Remember that data templates always should be used in order to store the fields on a separate template from the template that is actually used in the content tree, this is to make the most of the inheritance of templates and to keep the solution clean.

ProductTypeData

And lets also add the template that inherits the data template. I called the template ProductType and in the content tab of the template I added __ProductType in the base templates.

ProductType

The repository of tags

On to the content. Create a repository of tags somewhere in your content tree. I have created mine under /sitecore/content/Settings/Tagging/ProductTypes and I have added three product types.

ProductTypeRepo

Tagging template

Back to the templates. Create a data template that allows tagging of products. It should contain one field that allows tagging of products. I will only allow one tag per product so I chose a Droplink with the source set to /sitecore/content/Settings/Tagging/ProductTypes and called the template __ProductTagging.

ProductTypeTaggingData

And then just go ahead and add the data template to the items that you want to be taggable. This is the advantage of using pure data templates, now you are free to add the tagging to any other template. I added mine to the product template.

ProductTypeTagging

Create some products somewhere and add the tags to them.

ProductRepository

The code for the computed index field

Now lets leave Sitecore for a moment and enter visual studio. Create the code that is needed for the index to be able to get the values of the chosen tags on your tagged products. Remember that it is just the ids that are actually stored as content in the Product type field of the product so a lookup of the tag field itself would be useless (unless you are remarkably good at remembering GUIDs).

Create a class called ProductTypeValue and let it implement IComputedIndexField.

using System.Linq;
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.ComputedFields;
using Sitecore.Data;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;

public class ProductTypeValue : IComputedIndexField
    {
        public object ComputeFieldValue(IIndexable indexable)
        {
            return GetProductType(indexable as SitecoreIndexableItem);
        }

        private static string GetProductType(Item item)
        {
            Assert.ArgumentNotNull(item, "item");

            // Check if the template inherits the tagging template
            if (!item.Template.BaseTemplates.Any(bt => bt.ID.Equals(Constants.Templates.ProductTagging)))
                return null;

            // Check that the field can be found
            Assert.IsNotNull(item.Fields[Constants.Fields.ProductTagging.ProductTypeTag], "Field is null");

            // Check that there is a target item
            var internalLinkField = new InternalLinkField(item.Fields[Constants.Fields.ProductTagging.ProductTypeTag]);
            var targetItem = internalLinkField.TargetItem;
            Assert.IsNotNull(targetItem, "Field value is null");

            // Get the name of the linked product type
            var productType = targetItem.Fields[Constants.Fields.ProductType.Name].Value;

            if (!string.IsNullOrEmpty(productType))
            {
                return productType;
            }
            return null;
        }

        public string FieldName { get; set; }
        public string ReturnType { get; set; }
    }

And create the Constants class with the IDs from the template items and field items in Sitecore:

 public static class Constants
    {
        public struct Templates
        {
            public static ID ProductTagging = new ID("{B51D60BC-0B1C-4507-8610-9FE03DF41EED}");
        }

        public struct Fields
        {
            public struct ProductType
            {
                public static ID Name = new ID("{C07F9CC6-6A80-47A5-B9FF-414F89A12978}");
            }

            public struct ProductTagging
            {
                public static ID ProductTypeTag = new ID("{36600C17-5D0C-4E92-8309-0AA1E8E44CC1}");
            }
        }
    }

 Configuring the index to use the new field

Almost there, the only thing left to do is to add a line to the configuration file for the index. Open the Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config in the includes folder. Find this element in the xml file “/configuration/sitecore/contentSearch/configuration/defaultIndexConfiguration/fields/field[@fieldName=’isbucket_text’]” and add the following line after it:

<field fieldName="producttype" storageType="yes" indexType="untokenized">MyProductAssembly.Indexing.ProductTypeValue, MyProductAssembly</field>

It should look something like this after the line is added:

          <fields hint="raw:AddComputedIndexField">
            <field fieldName="_content" storageType="no" indexType="tokenized">Sitecore.ContentSearch.ComputedFields.MediaItemContentExtractor,Sitecore.ContentSearch</field>
            <field fieldName="calculateddimension" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.CalculatedDimension,Sitecore.ContentSearch</field>
            <field fieldName="daterange_month" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.DateRangeMonthFacet,Sitecore.ContentSearch</field>
            <field fieldName="daterange_week" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.DateRangeWeekFacet,Sitecore.ContentSearch</field>
            <field fieldName="daterange_year" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.DateRangeYearFacet,Sitecore.ContentSearch</field>
            <field fieldName="istemplate" storageType="no" indexType="tokenized">Sitecore.ContentSearch.ComputedFields.IsTemplate,Sitecore.ContentSearch</field>
            <field fieldName="lock" storageType="yes" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.IsLocked,Sitecore.ContentSearch</field>
            <field fieldName="parsedcreatedby" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.ParsedCreatedBy,Sitecore.ContentSearch</field>
            <field fieldName="parsedupdatedby" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.ParsedUpdatedBy,Sitecore.ContentSearch</field>
            <field fieldName="parsedlanguage" storageType="no" indexType="tokenized">Sitecore.ContentSearch.ComputedFields.ParsedLanguage,Sitecore.ContentSearch</field>
            <field fieldName="site" storageType="yes" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.Site,Sitecore.ContentSearch</field>
            <field fieldName="sizerange" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.FileSizeGrouping,Sitecore.ContentSearch</field>
            <field fieldName="version" storageType="no" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.StoreVersionTermVector,Sitecore.ContentSearch</field>
            <field fieldName="urllink" storageType="yes" indexType="tokenized">Sitecore.ContentSearch.ComputedFields.UrlLink,Sitecore.ContentSearch</field>
            <field fieldName="isbucket_text" storageType="no" indexType="tokenized">Sitecore.ContentSearch.ComputedFields.IsBucket,Sitecore.ContentSearch</field>
            <field fieldName="producttype" storageType="yes" indexType="untokenized">MyProductAssembly.Indexing.ProductTypeValue, MyProductAssembly</field>
            <!-- Disabled for speed of indexing. Enable if you would like to query by the fields below -->
            <!--<field fieldName="_isclone">Sitecore.ContentSearch.ComputedFields.IsClone,Sitecore.ContentSearch</field>-->
            <!--<field fieldName="_links" storageType="yes" indexType="untokenized" >Sitecore.ContentSearch.ComputedFields.ItemLinks, Sitecore.ContentSearch</field>-->
            <!--<field fieldName="_templates" storageType="yes" indexType="untokenized">Sitecore.ContentSearch.ComputedFields.AllTemplates, Sitecore.ContentSearch</field>-->
            <!--<field fieldName="hasactivetest">Sitecore.ContentSearch.ComputedFields.IsBeingTested,Sitecore.ContentSearch</field>-->
            <!--<field fieldName="hasclones">Sitecore.ContentSearch.ComputedFields.HasClones,Sitecore.ContentSearch</field>-->
            <!--<field fieldName="haspublishingrestrictions">Sitecore.ContentSearch.ComputedFields.HasPublishingRestrictions,Sitecore.ContentSearch</field>-->
            <!--<field fieldName="isinworkflow">Sitecore.ContentSearch.ComputedFields.IsItemInWorkflow,Sitecore.ContentSearch</field>-->
            <!--<field fieldName="persona">Sitecore.ContentSearch.ComputedFields.PersonaMatch,Sitecore.ContentSearch</field>-->
          </fields>

All done!

Go back to Sitecore and rebuild the index for the master database. Select the product container in the content tree and select the search tab. Type “producttype:Digital camera” (or whatever your product types are called) in the search box and you should get a result that looks something like this:

ProductTypeResult

What is the use?

So after this work the items can be found from within Sitecore, that’s all fine and good but not great. The power of this comes to show when you couple this functionality with the page editor and the use of search based datasources to populate content. But more about that in a later post.

4 thoughts on “How to create a Computed index field

  1. I am trying to make checklist as computed field but i am getting error:
    Could not create instance of type: System.String. No matching constructor was found.
    Please help.

    Thanks

  2. My site is multi lingual(en,ar), I have created a computed field ,My computed field doesn’t get values for “ar” and always index for en only

Leave a Reply

Your email address will not be published. Required fields are marked *