Walkthrough: Creating a field handler for custom Sitecore Commerce template properties

Abstract

How to create a custom field handler class to index an extended Sitecore Commerce template property to enable search based on the new field.

When you add new Catalog entity properties to the Commerce template, and you want to provide the ability to search based on those new properties, you must:

  • Create a custom Commerce template field handler.

  • Add the new Catalog entity properties to the Commerce Engine search policy set.

  • Add the new field to the CE Connect search provider configuration.

Create the custom external settings field handler class

When you extend the Sitecore Commerce template with new Catalog properties, you create a custom index field handler class that adds your custom Catalog entity fields in the Catalog External Settings component, so that the Commerce Engine knows where to find them.

To create a custom handler for new Catalog properties defined as External Settings:

  1. Create a class that inherits from the Sitecore.Commerce.Plugin.Search.AbstractIndexFieldHandler class:

    public class ExternalSettingsFieldHandler : AbstractIndexFieldHandler
  2. Add a public property to contain the name of the ExternalSettingsPropertyName:

    public class ExternalSettingsFieldHandler : AbstractIndexFieldHandler
     {
      public string ExternalSettingsPropertyName { get; set; } = string.Empty;
  3. Add an override for the ComposeValue method, and check for valid field handler information:

    public override object ComposeValue(object source, ConcurrentDictionary<string, object> context)
    {
     // check for valid field handler information
     if (context == null || source == null || !(source is CatalogItemBase entity) || string.IsNullOrEmpty(ExternalSettingsPropertyName))
      {
       return null;
      }
  4. In the body of ComposeValue, check for the External Settings component, and match the settings to the deterministic ID of the property to index:

    • If there is a match, look for the property name to index first in the specified language.

    • If a match does not exist in the specified language, look in Shared Settings.

    • If a match is not found in Shared Settings, there is no match, and therefore nothing to index.

      // check for required external settings information
      if (!entity.HasComponent<ExternalSettingsComponent>() ||
        !context.TryGetValue("LanguageCode", out object languageCode) ||
        !context.TryGetValue("DeterministicId", out var tempDeterministicId) ||
        !(tempDeterministicId is Guid deterministicId))
        {
         return null;
        }
        string lang = languageCode.ToString().ToLowerInvariant();
        var externalSettingsComponent = entity.GetComponent<ExternalSettingsComponent>();
        var settingsCollection = JsonConvert.DeserializeObject<Dictionary<Guid, Dictionary<string, Dictionary<string, string>>>>(externalSettingsComponent.Settings);
        if (!settingsCollection.ContainsKey(deterministicId))
        {
         // this id in not in the external settings, return null
         return null;
        }
        var externalSettings = settingsCollection[deterministicId];
        if (externalSettings == null)
        {
         // bad external settings return null
         return null;
        }
         // look for language first
         if (externalSettings.ContainsKey(lang))
         {
          // we found a language, try to find the property
          var languageSettings = externalSettings[lang];
          if (languageSettings.ContainsKey(ExternalSettingsPropertyName))
           {
            // found it, return the value
            return languageSettings[ExternalSettingsPropertyName];
           }
         }
        // no language property found, check shared settings
        var sharedSettings = externalSettings["shared"];
        if (sharedSettings.ContainsKey(ExternalSettingsPropertyName))
        {
         // found in shared settings, return the value
         return sharedSettings[ExternalSettingsPropertyName];
        }
        // Not found, just return null
        return null;
        }
     }

Add the custom field handler and new properties to index in the Search policy set file

To register the custom field handler and the new custom fields with the search index, you must add them to the Solr search policy set on the Commerce Engine.

To add the custom field handler and new properties to the Search policy set file:

  1. Open the C:\inetpub\wwwroot\<CommerceEngine>\wwwroot\data\Environments\PlugIn.Search.Solr.PolicySet-1.0.0.json file.

  2. Locate the section that contains your index configuration. For example, the following example shows the Sitecore Master index configuration that defines a new handler class ("$type": "Myplugin.Search.Solr.ExternalSettingsFieldHandler, MyPlugin.Search.Solr"), and a custom Catalog entity property to index ("ExternalSettingsPropertyName": "TestingText"):

    {
      "$type": "Sitecore.Commerce.Plugin.Search.ItemIndexablePolicy, Sitecore.Commerce.Plugin.Search",
      "IndexName": "sitecore_master_index",
      "FieldTypeMappers": ...
      "Fields": [ ...
        {
        "$type": "MyPlugin.Search.Solr.SolrIndexFieldConfiguration, MyPlugin.Search.Solr",
        "Name": "ExampleExternalSettingProperty",
        "Type": "System.String",
        "Handler": {
           "$type": "Myplugin.Search.Solr.ExternalSettingsFieldHandler, MyPlugin.Search.Solr",
           "ExternalSettingsPropertyName": "TestingText"
           },
        "Localizable": true
    }
  3. In the index configuration, make the following changes:

    • Update the "Name" property to match the name of the new custom field from the template in Sitecore.

    • Update the "Handler": class with the name of your new index field handler class.

      Note

      If the new custom field is not a shared setting, set the "Localizable": true

  4. After you make the configuration changes, bootstrap the Commerce Engine.

  5. In Microsoft IIS Manager, restart the Commerce Engine minions service (for example, CommerceMinions_Sc).

  6. Manually rebuild Sitecore XP indexes. After rebuilding the indexes, in the Solr Admin query page, the new search handlers are visible in the search index. In the following example, the new indexed field is "exampleexternalsettingproperty_t":"Testing Text"

    {
     "responseHeader":{
       "status":0,
       "QTime":2,
       "params":{
         "q":"*:*",
         "fl":"_displayname, productid_t, commercesearchitemtype_t,catalogentityid_t, exampleexternalsettingproperty_t, exampleexternalsettingproperty_t_en",
         "fq":["catalogentityid_t:\"Entity-SellableItem-7042112\"",
           "_language:en",
           "_latestversion:true",
           "_parent:e9f2e3a705750632b67e5e65092230c3"],
         "rows":"11",
         "_":"1611154590220"}},
     "response":{"numFound":1,"start":0,"docs":[
         {
           "productid_t":"7042112",
           "_displayname":"Optix 9.2MP Flash Memory Camcorder—30X 320X Zoom",
           "commercesearchitemtype_t":"SellableItem",
           "catalogentityid_t":"Entity-SellableItem-7042112",
           "exampleexternalsettingproperty_t":"Testing Text",
           "exampleexternalsettingproperty_t_en":"Testing Text"}]
         }
    }

Add a new field to CE Connect search provider configuration

In order to make the new field searchable in Sitecore, you must add it to the search provider configuration in CE Connect.

To add the new field to the search provider configuration in CE Connect:

  1. On your Sitecore service instance, in the \App_Config\Include\Y.Commerce.Engine folder, open the search provider config file. For example, for Sorl, open the Sitecore.Commerce.Engine.Connectors.Index.Solr.config file.

    Note

    If you use Azure as your search provider, open the Sitecore.Commerce.Engine.Connectors.Index.Azure.config file.

  2. In the FieldMap section, add the new fieldName definition. In the following example, we add a new field named "exampleexternalsettingproperty".

    <field fieldName="exampleexternalsettingproperty" storageType="YES" indexType="UN_TOKENIZED" vectorType="NO" boost="1f" returnType="string" settingType="Sitecore.ContentSearch.SolrProvider.SolrSearchFieldConfiguration, Sitecore.ContentSearch.SolrProvider"/>