Sometimes, when we work on indexes in Sitecore, it is not enough to use just out of the box fields that are stored in indexes. When we want to reduce or avoid DB calls during running a search query, we need to store more information in index. In such situations, it is a common thing to use computed fields. It helps us to add information to index in easily way. When we extend data stored in computed fields, it is more than sure that we’ll use some information from another Sitecore items. It is nothing wrong, because we want to speed up our search as much as it is possible, but in such situations we should be aware of potential issues.

What kind of danger can we face?

When computed field stores information from another Sitecore items, we need to be careful to keep data up to date in our indexes. Once we change something in the Sitecore item, based on index update strategy, index document for this one will be updated as well. In such situations we should also remember about updating index documents that are using changed item in computed fields. Otherwise we’ll keep depracated data in our index what can cause inconsistent results presented for end user.

Example of issue

SampleItemsHierarchy

Tree structure presented above shows the pottential issue. We have particular types of items:

  • Car store - location where the store is. It contains vary car brands inside.
  • Car brand - item repsponsible for keeping data about brand.
  • Car model - item what contains information about model and price

Let’s imagine that we want to find all stores that offering cars within specified price range. Based on structure from example, price field exists only in car models items. In this situation we could go through all cars in index, choose those which contain proper price for our range and take the related car stores to results. It sounds good, but in case of large amount of items, it can take some time.

To improve it, we can prepare a simple computed field to keep price range for each car store item. Our custom computed field will check all cars within car store and select min and max values. Then our search will go only through store items instead of all cars that we have. For this purpose we’re creating computed field to keeping price range and we’re rebuiling index. For now it looks good, because our index was rebuilt recently. Please notice that once we update single car price in Sitecore, index document for this item will be updated with a new value, but store will still keep the old price range in its index document. To avoid this issue we can create custom indexing strategy, but it would be pretty big piece of code. Let’s see some simple tricks that you will be able to use in similar situation.

Updating Index Item

To make things simpler it will be fine to handle some sitecore events like item:saved or publish:end and update indexes only for particular items.

Inside an event handler, we should refresh index documents related to changed one. After finding out all required items, let’s update them by using one of the proposed snippets of code:

public static void Update(string indexName, ICollection<Item> items)
{
    var index = ContentSearchManager.GetIndex(indexName);
    foreach (var item in items)
    {
        var uniqueId = new SitecoreItemUniqueId(item.Uri);
        IndexCustodian.UpdateItem(index, uniqueId);
    }
}

Code presented above will update index documents for items passed in parameters. It will work in asynchronous way, because IndexCustodian creates updating process as Sitecore job and puts it in the job queue. I would recommend this way instead of synchronous way. Because if we would use it in Sitecore event handlers, we have to be aware that they work in synchronous way too, so in case a large amount of items, it can freeze our instance for a while.

But if you still want to do it in synchronous way, example presented below shows how to achieve it without IndexCustodian:

public static void Update(string indexName, ICollection<Item> items)
{
    var index = ContentSearchManager.GetIndex(indexName);
    foreach (var item in items)
    {
        var uniqueId = new SitecoreItemUniqueId(item.Uri);
        index.Update(uniqueId);
    }
}

Refreshing Index Item

Beyond index updating, we are able also to refresh index item. Main difference is that refreshing process updates indexes for pointed item and its descendants. Please take a look at the code below:

public static void Refresh(string indexName, Item item)
{
    var index = ContentSearchManager.GetIndex(indexName);
    var indexableItem = (SitecoreIndexableItem) item;
    IndexCustodian.Refresh(index, indexableItem);
}

In the code presented above we used IndexCustodian like in updating example, so this process will be handled asynchronously.

If we want do it in synchronous way, just take a look at the following example:

public static void Refresh(string indexName, Item item)
{
    var index = ContentSearchManager.GetIndex(indexName);
    var indexableItem = (SitecoreIndexableItem)item;
    index.Refresh(indexableItem);
}

Conclusion

Computed fields are really helpful in scope of indexes customising. They bring us possibilities to store additional information to speed up search and other features like faceting, pagination and so on. It is important to use them wisely by remembering about all relations to other items and keeping index documents up to date. For this purpose we could rebuild index, but it will cost too much time and effort in case of single item changes. The better solution is updating or refreshing single index items, but we need to be aware of differences between those two processes and use them in proper situations.

Thank you for reading this post, I hope it’ll help some of you.

Updated:

Leave a comment