Sitecore Search: Custom Computed Field To Search For All Page Data Based On Inherited Templates

High Score Labs News • Nov 21, 2018
Let’s imagine that you have a site with a lot of page items and they have a very complicated template inheritance structure; however, you want to have a search that can find pages with specific content. Then it would make sense to target inherited page templates, starting from the base page template and then, use the search index to find items which are inherited from this base template.
Out of the box, Sitecore provides an option to search only for items with a specific templates; however, this does not include inherited templates (Sitecore.ContentSearch.SearchTypes.SearchResultItem.TemplateId property) . So, lets create our own custom calculated field to search for items based on their inherited templates.
First, let’s create a handy extension method to grab all the item templates:
using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Data.Managers;
using Sitecore.Data.Templates;
namespace Sitecore.Foundation.SitecoreExtensions.Extensions
{
public static class ItemExtensions
{
public static List<Template> GetBaseTemplates(this Item item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
var template = TemplateManager.GetTemplate(item);
if (template != null)
{
var templateList = template.GetBaseTemplates();
if (templateList != null)
{
var list = templateList.ToList();
if (!list.Any(x => x.ID == template.ID))
{
list.Add(template);
}
return list;
}
}
return new List<Template>();
}
}
}
Next, let’s create our custom calculated field:
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Utilities;
using Sitecore.Data.Items;
using Sitecore.Foundation.SitecoreExtensions.Extensions;
using System.Linq;
namespace Sitecore.Foundation.Search.ComputedFields
{
//ContentSearch.ComputedFields.AllTemplates returns item template joined with item.Template.BaseTemplates
//so, actually it’s getting only current item template and immediate ones, so let’s fix that!
public class AllTemplates : Sitecore.ContentSearch.ComputedFields.AllTemplates
{
public override object ComputeFieldValue(IIndexable indexable)
{
Item obj = (indexable as SitecoreIndexableItem);
if (obj == null)
{
return null;
}
//get current and all the way inherited templates
var stringList = obj.GetBaseTemplates().Select(t => IdHelper.NormalizeGuid(t.ID));
return stringList;
}
}
}
Next, add this field to the index configuration and lets use it together with the “deep content” field from the previous article: Part One Article
<documentOptions type=”Sitecore.ContentSearch.LuceneProvider.LuceneDocumentBuilderOptions, Sitecore.ContentSearch.LuceneProvider”>
<indexAllFields>true</indexAllFields>
<include hint=”list:AddIncludedTemplate”>
</include>
<include hint=”list:AddIncludedField”>
<fieldId>_uniqueid</fieldId>
</include>
<exclude hint=”list:AddExcludedTemplate”>
</exclude>
<exclude hint=”list:AddExcludedField”>
</exclude>
<fields hint=”raw:AddComputedIndexField”>
<field fieldName=”deepcontent”>Sitecore.Foundation.Search.ComputedFields.DeepContent, Sitecore.Foundation.Search</field>
<field fieldName=”_basetemplates” storageType=”yes” indexType=”untokenized”>Sitecore.Foundation.Search.ComputedFields.AllTemplates, Sitecore.Foundation.Search</field>
</fields>
</documentOptions>
<fieldMap type=”Sitecore.ContentSearch.FieldMap, Sitecore.ContentSearch”>
<fieldNames hint=”raw:AddFieldByFieldName”>
<field fieldName=”deepcontent” storageType=”YES” indexType=”TOKENIZED” vectorType=”WITH_POSITIONS_OFFSETS” boost=”1f” emptyString=”_EMPTY_” nullValue=”_NULL_” type=”System.String” settingType=”Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider”>
<Analyzer type=”Sitecore.ContentSearch.LuceneProvider.Analyzers.LowerCaseKeywordAnalyzer, Sitecore.ContentSearch.LuceneProvider”/>
</field>
</fieldNames>
</fieldMap>
Next, create our index model:
using Sitecore.ContentSearch;
using Sitecore.ContentSearch.Converters;
using Sitecore.ContentSearch.SearchTypes;
using Sitecore.Data;
using System.Collections.Generic;
using System.ComponentModel;
namespace Sitecore.Foundation.Search.Models.Index
{
public class BasePageSearchResultItem : SearchResultItem
{
//name from config file
[IndexField(“_basetemplates”)] [TypeConverter(typeof(IndexFieldEnumerableConverter))]
public IEnumerable<ID> ItemBaseTemplates { get; set; }
//name from config file
[IndexField(“deepcontent”)]
public string DeepContent { get; set; }
}
}
And finally, our search example:
using Sitecore.Foundation.SitecoreExtensions.Extensions;
using System.Linq;
using Sitecore.Foundation.Search.Models.Index;
using Sitecore.ContentSearch.Linq.Utilities;
using Sitecore.Data;
using Sitecore.ContentSearch;
using System.Collections.Generic;
namespace Sitecore.Feature.Search
{
public class SearchService
{
// Base Page Template ID
public ID basePageTemplateID = ID.Parse(“base Page Template ID”);
public List<BasePageSearchResultItem> Search(string searchPhrase)
{
//create a search context
var context = ContentSearchManager.GetIndex(“index_name”).CreateSearchContext();
//get querable for a current context language
var querable = context.GetQueryable<BasePageSearchResultItem>(new CultureExecutionContext(Context.Language.CultureInfo));
//Use a predicate builder for the search expression
var exprAnd = PredicateBuilder.True<BasePageSearchResultItem>();
//search only for items which are inherited from the base page template
exprAnd = exprAnd.And(q => q.ItemBaseTemplates.Contains(basePageTemplateID));
//and search for a search phrase in the item’s deep content
exprAnd = exprAnd.And(q => q.DeepContent.Contains(searchPhrase));
//execute the query
var results = querable.Where(exprAnd).ToList();
foreach (var result in results)
{
//you can easily get the founded sitecore item
var item = result.GetItem();
}
//or, you can use index model in your search results page
return results;
}
}
}
The end result of all this work is that we can now search for items based on their inherited templates and not just the out of the box solution that Sitecore provides.
See part one of this article here: Part One Article