We created an SXA SOLR based search component that displayed News based on a users selected locations. They are able to 'include' themselves in additional locations (cities, regions, etc) by selecting more from a drop down list component.
Any of the selected locations data was pulled into the search component on page load via browser cookies. Then all of this information was passed to a Scriban template extension which returned a list of content search results.
We have multiple of these search components on our page (with different template options, etc.) and need a way to cache the SOLR search components results. It will need to update whenever the user selected values from the drop down list change. Every time you select a value, the page is refreshed and the search based component should update its results. There are no Sitecore cache options to cover this scenario, unfortunately. Nothing is being updated in the index or on the data item when this happens. So, we had to look into creating some custom cache functionality.
The Idea
We have the Sitecore CustomCache at our disposal for exactly that: custom caching of our data using custom defined logic. Using the CustomCache means we are implementing an abstract class that follows a common implementation pattern defined by Sitecore. It also means we are able to utilize clearing capabilities on the cache.aspx Sitecore admin page.
We already have a search result object defined at the Helix Foundation level used for returning results. This is ultimately what we want to cache.
We will need a key for the cache that is unique to each combination of locations. We can achieve this by encoding the request object and using that string as the cache key. I have detailed how to do this in Part 2: Creating a Hash Key from a C# Object.
Now we have our basic idea, let us go about how to implement this.
Note, this solution was implemented in Sitecore 10.1.0
The Caching
Lets first create the object that we would like to cache. Below, we are using a custom Helix Foundation level object that returns other information about the content search results like page size and facets.
The SearchResultItem is a result object populated by SOLR index fields.
In order to cache this object, our object implements Sitecore.Caching.ICacheable and implements its interface.
public class SearchResults : ICacheable
{
// SearchResults is a Foundation level object containing other information like page size, facets, etc, but this could be anything in your case.
public SearchResults SearchResultItems { get; set; }
public bool Cacheable { get; set; }
public bool Immutable
{
get { return true; }
}
public event DataLengthChangedDelegate DataLengthChanged;
public long GetDataLength()
{
return Sitecore.Reflection.TypeUtil.SizeOfObject();
}
}
Our object implements Sitecore.Caching.Generics.CustomCache. The <string> parameter indicates that our cache key is a string.
We have defined a static key and pass in a max size for the cache in the constructor.
public class SearchResultsCache : CustomCache<string>
{
public SearchResultsCache(long maxSize)
: base("SearchResultsCache.SearchResults", maxSize)
{
}
public SearchResults Get(string cacheKey)
{
return (SearchResults)base.GetObject(cacheKey);
}
}
The SearchResultsCacheManager allows us to interact with our newly created custom cache via the GetCache and SetCache methods.
class SearchResultsCacheManager
{
private static readonly SearchResultsCache Cache;
static SearchResultsCacheManager()
{
Cache = new SearchResultsCache(StringUtil.ParseSizeString(Settings.GetSetting("SearchResultsCaching.SearchResultsCacheMaxSize", "50MB")));
Cache.InnerCache.Scavengable = true;
}
public static SearchResults GetCache(string key)
{
return Cache.Get(key);
}
public static void SetCache(string key, SearchResults value, DateTime absExpirationDateTime)
{
Cache.InnerCache.Add(key, value, absExpirationDateTime);
}
public static void SetCache(string key, SearchResults value, TimeSpan slidingExpiration)
{
Cache.InnerCache.Add(key, value, slidingExpiration);
}
public static void SetCache(string key, SearchResults value)
{
Cache.InnerCache.Add(key, value);
}
}
Further Considerations
With this configuration, when new items are added to the index, the cache will not be refreshed to reflect the appearance of these news items.
In Part 3: How to Clear the Sitecore CustomCache, I have touched on how we can clear our custom caches by listening for the publish pipeline and running a custom function to do so.