Sitecore search pages with all rendering fields

Image

Sitecore uses indexing to search for all the content and fields. The search indexing will be done on the Sitecore field level rather than the page level. Sitecore provides different search APIs to search for different content based on various fields in Sitecore (like rich area text field, Single line text field, multi-line text field, word documents docuements, check box field etc.). Each field will be indexed as search indexing documents in the corresponding indexing locations (For example, if we use Solr search, indexes will be stored in C:\Program Files\Solr-5.0.0\solr-5.1.0\solr-5.1.0\server\solr\sitecore_master_index).Implementation of generic search results where we need to search a text in all the Sitecore fields of an item and its rendering fields will become a difficult job where we need to add each field to predicate in the search API (for example - predicate = predicate. And(p => p.Page_title.Contains(searchText));) and get the results. Also, when searching the fields of page items in Sitecore, we often will miss the content from its renderings which are being rendered in the page. So, we need to search the rendering fields along with the page fields.In this blog, we want to achieve the solution to the following problems.

  1. Search Only pages (which can be linked as URL and shown in search results page).
  2. Search Sitecore page item fields and its presentation rendering fields.
  3. Exclude some templates from search results page (like 404 error page, search results page itself)
  4. Get control over which fields needs to be searched.

 SolutionNote - This code has been tested in Sitecore 8.2 version.Other versions may have different API code.We need to extend the search indexing to achieve rendering search with flexibility to exclude or include different templates. Instead of going through each field at a time, here we are associating all the fields of the item and its rendering fields into a single component. Sitecore provided ability to override its default feature of indexing single field values called computed field and provides a class called AbstractComputedIndexField which is in  Sitecore.ContentSearch dll.We need to follow the below procedure to create a custom computed field and configure it in sitecore.

  1. In your class library project create a new c# class and override the methods called ComputeFieldValue from AbstractComputedIndexField class.

The sample code is shown belownamespace Horizontal.Services.Search{public class ContentField : AbstractComputedIndexField{//this content item id is base page item id.(only sitecore item which has presentation values will be added as base page item.having this item in inheritance will be detected as Page item which can be linked or shown in search results page)private const string ContentBaseItemId = "{05CCD5DD-29EB-4F46-8477-5818449F2728}";private const string SearchResults = "{CAC6FE6A-D624-45A3-A680-DF199772624D}";public override object ComputeFieldValue(IIndexable indexable){Item item = indexable as SitecoreIndexableItem;if (item == null) return null;if (!IsValidItem(item)) return null;StringBuilder sbData = new StringBuilder();var renderings = GetRenderingReferences(item, "default");if (renderings == null)return null;if (item.Versions.IsLatestVersion()){//the fields of the current item will be added to indexforeach (Field field in item.Fields){if (field.ShouldBeIndexed() && !string.IsNullOrWhiteSpace(field.Value)){sbData.AppendFormat("{0} ", StripHtml(field.Value));}}foreach (var rendering in renderings){if (string.IsNullOrEmpty(rendering.Settings.DataSource)) continue;var dataSourceItem = item.Database.GetItem(rendering.Settings.DataSource);//the fields of the rendering of the current item will be added to indexif (dataSourceItem != null){foreach (Field field in dataSourceItem.Fields){if (field.ShouldBeIndexed() && !string.IsNullOrWhiteSpace(field.Value)){sbData.AppendFormat("{0} ", StripHtml(field.Value));}}}}}return sbData.ToString().Trim();}private Sitecore.Layouts.RenderingReference[] GetRenderingReferences(Item item, string deviceName){LayoutField layoutField = item.Fields["__final renderings"];if (layoutField == null)return null;Sitecore.Layouts.RenderingReference[] renderings = null;if (item.Database != null){renderings = layoutField.GetReferences(GetDeviceItem(item.Database, deviceName));}else{renderings = layoutField.GetReferences(GetDeviceItem(Sitecore.Context.Database, deviceName));}return renderings;}private bool IsValidItem(Item item){Item searchPage = Sitecore.Configuration.Factory.GetDatabase("web").GetItem(new ID(SearchResults));bool isValid = true;MultilistField multilistField = searchPage.Fields["Searchable Page Templates"];if(multilistField != null)isValid = multilistField.TargetIDs.Contains(item.TemplateID) ? true : false;if (!item.IsDerived(new ID(ContentBaseItemId)) || !item.Paths.FullPath.ToLowerInvariant().Contains("/sitecore/content/")) return false;return isValid;}private DeviceItem GetDeviceItem(Sitecore.Data.Database db, string deviceName){return db.Resources.Devices.GetAll().Where(d => d.Name.ToLower() == deviceName.ToLower()).First();}public static string StripHtml(string source){var htmlRegex = new Regex("<.*?>", RegexOptions.Compiled);var removedTags = htmlRegex.Replace(source, string.Empty);return HttpUtility.HtmlDecode(removedTags);}}}

  1. Create a configuration file to include it in app_config/Include folder.The configuration file code should look like the following
 <?xml version="1.0"?><configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"><sitecore><contentSearch><indexConfigurations><defaultSolrIndexConfiguration type="Sitecore.ContentSearch.SolrProvider.SolrIndexConfiguration, Sitecore.ContentSearch.SolrProvider"><documentOptions type="Sitecore.ContentSearch.SolrProvider.SolrDocumentBuilderOptions, Sitecore.ContentSearch.SolrProvider"><fields hint="raw:AddComputedIndexField"><field fieldName="customcontent" returnType="text"> Horizontal.Services.Search.ContentField, Horizontal.Services</field></fields></documentOptions></defaultSolrIndexConfiguration></indexConfigurations></contentSearch></sitecore></configuration>
  1. Rebuild the indexes from Sitecore.

     Follow the procedure below to do that

  • Open Sitecore desktop
  • Open Indexing manager and click on Sitecore >control Panel >Indexing >Indexing Manager.
  • Check the indexes that needs to be rebuilt and click Rebuild.
  1. Once the indexing is completed, Go to the Solr admin url(http://localhost:8983/solr) , navigate to the sitecore_web_index core and check for the newly added field customcontent_t and execute the query from the panel and look for the results. Below is screenshot of Solr console if you are not sure how to check it.
  1. Add the following code to get the page results that searched both page fields and renderings fields.
               var predicate = PredicateBuilder.True<SearchResultModel>();predicate = predicate.And(p => p.customContent.Contains(searchText));var query = context.GetQueryable<SearchResultModel>().Where(predicate);totalCount = query.Count();List<SearchResultModel> results = query.Skip((page - 1) *   count).Take(count).ToList();
  1. Above code will fetch the page results of the search text user has searched by searching in both page and its renderings.