Customizing the content of search indexes
The system provides a way to customize the content that smart search indexes store for pages or objects. You can add external data into indexes, or parse and otherwise modify the text that the smart search adds to indexes by default.
Deploying customizations to the live site
You need to deploy the code (assembly) containing your search customizations to the separate live site (MVC) application. Otherwise the customized search indexing will not work for content changes that occur through the live site.
Adding custom fields for page indexes
The Xperience API allows developers to add custom fields for locally stored page search indexes. For example, you can use custom index fields to set up search result filtering or faceting scenarios based on external data not actually stored within the searched pages.
To add custom page index fields, implement event handlers for BOTH of the following events:
- DocumentsEvents.CreateSearchFields.Execute – occurs while (re)building a locally stored search index of the Pages type when the system prepares the collection of search fields.
- DocumentsEvents.CreateSearchFieldsForPage.Execute – occurs when the system indexes or updates a page for a locally stored search index of the Pages type. The event triggers separately for each indexed page.
You need to assign handlers for the events at the beginning of the application’s life cycle – create a custom module class and override the module’s OnInit method.
Register all custom fields into your indexes by handling the CreateSearchFields event.
- Add fields via the Fields property of the handler’s CreateSearchFieldsEventArgs parameter.
Add custom search fields to indexes and set their value for individual pages.
- Set field values using the Value property of added ISearchField objects.
- Add fields via the Fields property of the handler’s CreateSearchFieldsForPageEventArgs parameter.
using System;
using CMS;
using CMS.DataEngine;
using CMS.DocumentEngine;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomSmartSearchModule))]
public class CustomSmartSearchModule : Module
{
// Module class constructor, the system registers the module under the name "CustomSmartSearch"
public CustomSmartSearchModule()
: base("CustomSmartSearch")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns handlers to the CreateSearchFields events
DocumentEvents.CreateSearchFields.Execute += OnCreateSearchFields;
DocumentEvents.CreateSearchFieldsForPage.Execute += OnCreateSearchFieldsForPage;
}
private void OnCreateSearchFields(object sender, CreateSearchFieldsEventArgs e)
{
// Only adds the custom field for the index with the 'PageIndex' code name
if (e.IndexInfo.IndexCodeName.Equals("PageIndex", StringComparison.InvariantCultureIgnoreCase))
{
// Creates a custom search index field storing string values
ISearchField field = SearchFieldFactory.Instance.Create("customField", typeof(string), CreateSearchFieldOption.SearchableAndRetrievableWithTokenizer);
// Registers the field to the collection of search fields within the index
e.Fields.Add(field);
}
}
private void OnCreateSearchFieldsForPage(object sender, CreateSearchFieldsForPageEventArgs e)
{
// Only adds the custom field for the index with the 'PageIndex' code name,
// and only for pages under the '/Products' section of the content tree
if (e.IndexInfo.IndexCodeName.Equals("PageIndex", StringComparison.InvariantCultureIgnoreCase) &&
e.Page.NodeAliasPath.StartsWith("/Products/"))
{
// Creates a custom search index field storing string values
ISearchField field = SearchFieldFactory.Instance.Create("customField", typeof(string), CreateSearchFieldOption.SearchableAndRetrievableWithTokenizer);
// Sets the value of the custom index field for the indexed page
field.Value = "Custom value";
// Adds the field to the index
e.Fields.Add(field);
}
}
}
Adding custom fields for Azure indexes
If you wish to add custom fields for an Azure Cognitive Search index, use the options described on the Customizing Azure Search page.
Customizing the search content
The system allows you to extend and change the content that the smart search uses to find matching results. Within search indexes, the content used for the primary search is stored in a system field that combines values from a large number of other fields (all fields that have the Content flag enabled in the search settings of Xperience object fields). The name of the field within indexes depends on the type of the index:
- Locally stored indexes: _content
- Azure Search indexes: sys_content
To customize the search content of indexes, implement event handlers for the following events:
- DocumentsEvents.GetContent.Execute – occurs before the system writes a page’s data into smart search indexes.
- ObjectEvents.GetContent.Execute – occurs before the system writes the data of objects into search indexes. You can also use <name>Info.TYPEINFO.Events instead of ObjectEvents to handle the GetContent events for specific object types.
You need to assign handlers for the events at the beginning of the application’s life cycle – create a custom module class and override the module’s OnInit method.
The system triggers GetContent events when a page or object is updated and when rebuilding search indexes.
Inside the GetContent event handler methods, you can access the search index content via the Content property of the handler’s DocumentSearchEventArgs or ObjectEventArgs parameter (string type).
Index rebuilding requirements
If you modify the external data that your GetContent event handlers add to the index, the system does not automatically update the content of the corresponding indexes. To ensure that your indexes are up to date after making changes to the external data, you need to manually rebuild the indexes (or set up automatic updating of the search indexes via additional custom code).
For example, if you use GetContent handlers to add user data into page indexes:
- Saving a page covered by the index updates the content of the index, including the external user data.
- Saving user objects directly does NOT update the index – a rebuild is required.
Example - Adding data from a user field to page indexes (DocumentEvents)
Pages in Xperience can have a user assigned as the owner. The following example shows how to add the Description value from the user settings of a page’s owner into the search index content of each page.
Open your Xperience administration project in Visual Studio (using the WebApp.sln file).
Create a custom module class. For example, name the class CustomSmartSearchModule.cs.
- Add the class into a custom Class Library project within the Xperience solution.
Override the module’s OnInit method and assign a handler to the DocumentEvents.GetContent.Execute event.
using CMS; using CMS.DataEngine; using CMS.DocumentEngine; using CMS.Membership; // Registers the custom module into the system [assembly: RegisterModule(typeof(CustomSmartSearchModule))] public class CustomSmartSearchModule : Module { // Module class constructor, the system registers the module under the name "CustomSmartSearch" public CustomSmartSearchModule() : base("CustomSmartSearch") { } // Contains initialization code that is executed when the application starts protected override void OnInit() { base.OnInit(); // Assigns a handler to the GetContent event for pages DocumentEvents.GetContent.Execute += OnGetPageContent; } private void OnGetPageContent(object sender, DocumentSearchEventArgs e) { // Gets an object representing the page that is being indexed TreeNode indexedPage = e.Node; // Checks that the page exists if (indexedPage != null) { // Gets the user object of the page owner UserInfo pageOwner = UserInfo.Provider.Get(indexedPage.NodeOwner); if (pageOwner != null) { // Adds the value of the "Description" field from the owner's user settings into the indexed content // Spaces added as separators to ensure that typical search index analyzers can correctly tokenize the index content e.Content += " " + pageOwner.UserDescription + " "; } } } }
Save the CustomSmartSearchModule.cs file.
Sign in to the Xperience administration interface.
Open the Smart search application and Rebuild your page search indexes.
The search now returns results for pages if the owner’s description field matches the search text.
Example - Indexing personal category names for users (ObjectEvents)
Users in Xperience can create personal categories for organizing pages. The following example demonstrates how to customize the search so that the display names of personal categories are included in the content of user indexes.
Open your Xperience administration project in Visual Studio (using the WebApp.sln file).
Create a custom module class. For example, name the class CustomSmartSearchModule.cs.
- Add the class into a custom Class Library project within the Xperience solution.
Override the module’s OnInit method and assign a handler to the UserInfo.TYPEINFO.Events.GetContent.Execute event.
using CMS; using CMS.DataEngine; using CMS.Membership; using CMS.Taxonomy; // Registers the custom module into the system [assembly: RegisterModule(typeof(CustomSmartSearchModule))] public class CustomSmartSearchModule : Module { // Module class constructor, the system registers the module under the name "CustomSmartSearch" public CustomSmartSearchModule() : base("CustomSmartSearch") { } // Contains initialization code that is executed when the application starts protected override void OnInit() { base.OnInit(); // Assigns a handler to the GetContent event for user objects UserInfo.TYPEINFO.Events.GetContent.Execute += OnGetUserContent; } private void OnGetUserContent(object sender, ObjectEventArgs e) { // Gets the indexed user object UserInfo indexedUser = (UserInfo)e.Object; // Checks that the object exists if (indexedUser != null) { // Gets the personal page categories of the indexed user ObjectQuery<CategoryInfo> personalCategories = CategoryInfo.Provider.Get().WhereEquals("CategoryUserID", indexedUser.UserID); // Loops through the categories foreach (CategoryInfo category in personalCategories) { // Adds the display name of the category to the user search index // Spaces added as separators to ensure that typical search index analyzers can correctly tokenize the index content e.Content += " " + category.CategoryDisplayName + " "; } } } }
Save the CustomSmartSearchModule.cs file.
Sign in to the Xperience administration interface.
Open the Smart search application and Rebuild your user indexes.
The search results now include users if the name of at least one of the user’s personal page categories matches the search text.
Example - Indexing the names of assigned product options for product pages (DocumentEvents)
The following example shows how to add the names of e-commerce product options into the indexed content for the relevant product pages. Only affects product options in categories of the Products type.
Open your Xperience administration project in Visual Studio (using the WebApp.sln file).
Create a custom module class. For example, name the class CustomSmartSearchModule.cs.
- Add the class into a custom Class Library project within the Xperience solution.
Override the module’s OnInit method and assign a handler to the DocumentEvents.GetContent.Execute event.
using CMS; using CMS.DataEngine; using CMS.DocumentEngine; using CMS.Ecommerce; // Registers the custom module into the system [assembly: RegisterModule(typeof(CustomSmartSearchModule))] public class CustomSmartSearchModule : Module { // Module class constructor, the system registers the module under the name "CustomSmartSearch" public CustomSmartSearchModule() : base("CustomSmartSearch") { } // Contains initialization code that is executed when the application starts protected override void OnInit() { base.OnInit(); // Assigns a handler to the GetContent event for pages DocumentEvents.GetContent.Execute += OnGetProductPageContent; } private void OnGetProductPageContent(object sender, DocumentSearchEventArgs e) { // Gets an object representing the page that is being indexed TreeNode indexedPage = e.Node; // Checks that the page exists and represents a product (SKU) if (indexedPage != null && indexedPage.HasSKU) { // Gets the ID of the SKU int skuId = indexedPage.NodeSKUID; // Checks that the SKU has at least one enabled product option if (SKUInfoProvider.HasSKUEnabledOptions(skuId)) { // Gets the SKU's enabled product option categories of the "Products" type ObjectQuery<OptionCategoryInfo> categories = OptionCategoryInfoProvider.GetProductOptionCategories(skuId, true, OptionCategoryTypeEnum.Products); // Loops through the product option categories foreach (OptionCategoryInfo category in categories) { // Gets a list of enabled options in the product option category ObjectQuery<SKUInfo> options = SKUInfoProvider.GetSKUOptionsForProduct(skuId, category.CategoryID, true); // Loops through the product options foreach (SKUInfo option in options) { // Adds the name of the product option into the indexed content for the product page // Spaces added as separators to ensure that typical search index analyzers can correctly tokenize the index content e.Content += " " + option.SKUName + " "; } } } } } }
Save the CustomSmartSearchModule.cs file.
Sign in to the Xperience administration interface.
Open the Smart search application and Rebuild your page search indexes.
The search now returns product pages if the search text matches the name of one of the page’s product options.