Sitecore Experience Commerce: Customising Commerce Content in the Sitecore Indexes for Storefront Search Results

In this article, we will look at how to control the storefront search results by customising the commerce content that is indexed in the Sitecore master and web indexes.

If you are looking for how to add new fields for faceting and other purposes, see Adding a property field to an index.

How Storefront Search Works

The SXA Search component creates a solr query based on a number of criteria, however focusing on how the search term is involved in the query, we see that the sxacontent field is our culprit for this customisation. This can be seen highlighted below in our storefront and in our search log.

From XC 9.3 onwards, the commerce portion of the Sitecore indexes were migrated over to the Commerce Engine for indexing performance improvements. There are a number of handlers utilised for populating the indexes with catalog data, however for managing the content related to the storefront search component we will look at the SxaContentFieldHandler.

See Catalog Search Field Handlers for a full list of handlers provided with XC, which are utilised when registering index fields in Plugin.Search.Solr.PolicySet-1.0.0.json.

The SxaContentFieldHandler Class

By default, the sellable item properties that are added to the sxacontent field consist of the following entries:

  • Tags
  • ProductId
  • Name
  • DisplayName
  • ItemDefinition
  • Brand

As we can see from the sitecore_web_index in Solr, a query for a specific 'Spectra' product, productid_t:6042260, shows that these properties are indexed against the sellable item.

{
  "sxacontent_txm":["39inch|4k|uhd|television|spectra",
    "6042260",
    "Habitat Spectra 39” 4K LED Ultra HD Television",
    "Habitat Spectra 39” 4K LED Ultra HD Television",
    "Product",
    "Spectra Televisions"],
  "productid_t":"6042260",
  ...
}

A common property that I have seen often requested to be included for searching is the sellable item's description, so we will use this property as well as a generic property from a custom component for demonstration purposes.

In the sample code below, we see that we inherit from the default SxaContentFieldHandler and execute the base ComposeValue method, which will populate the default fields listed above. We then simply add the properties to the sxaContent reponse object, which in this case is a list of strings.

You may have noticed that we are only adding the properties to sxacontent if a value is present. This is to minimise the bloat of empty entries and may lead to performance improvement however insignificant it may be. You may prefer to leave empty entries in the index to simplfy troubleshooting in mapping each entry back to its source property, so choose an approach that best suits you.

public class MySxaContentFieldHandler : Sitecore.Commerce.Plugin.Catalog.SxaContentFieldHandler
{
    public override object ComposeValue(object source, ConcurrentDictionary<string, object> context)
    {
        var sxaContent= base.ComposeValue(source, context) as List<string>;
        if (!(source is SellableItem) || sxaContent == null || sxaContent.Count() == 0)
        {
            return sxaContent;
        }

        var sellableItem = source as SellableItem;

        if (!string.IsNullOrWhiteSpace(sellableItem.Description))
        {
            sxaContent.Add(sellableItem.Description);
        }

        var myProperty = sellableItem.GetComponent<MyCustomComponent>().MyCustomProperty;
        if (!string.IsNullOrWhiteSpace(myProperty))
        {
            sxaContent.Add(myProperty);
        }

        return sxaContent;
    }
}

Now that we have created our modified SxaContentFieldHandler class, we register it in Plugin.Search.Solr.PolicySet-1.0.0.json for both the master and web indexes.

{
  "$type": "Sitecore.Commerce.Plugin.Search.ItemIndexablePolicy, Sitecore.Commerce.Plugin.Search",
  "IndexName": "sitecore_web_index",
  "FieldTypeMappers": [ ... ],
  "Fields": [
    {
      "$type": "Sitecore.Commerce.Plugin.Search.Solr.SolrIndexFieldConfiguration, Sitecore.Commerce.Plugin.Search.Solr",
      "Name": "sxacontent",
      "Type": "System.Collections.Generic.List`1[System.String]",
      "TypeHint": "textCollection",
      "Handler": {
        "$type": "Sitecore.Commerce.Engine.Search.MySxaContentFieldHandler, Sitecore.Commerce.Engine"
      }
    },
    ...
  ]
}

After deploying our customisation, boostrapping, and reindexing, we can see our custom entries in the sxacontent field.

{
  "sxacontent_txm":["39inch|4k|uhd|television|spectra",
    "6042260",
    "Habitat Spectra 39” 4K LED Ultra HD Television",
    "Habitat Spectra 39” 4K LED Ultra HD Television",
    "Product",
    "Spectra Televisions",
    "Enjoy incredible picture and dramatic detail with 4X the resolution of full HD. Enjoy incredible hues of color with technology that blends digital color for near-perfect representation. Experience Ultra HD picture quality with enhanced detail in every way.",
    "mycustomproperty"],
  "productid_t":"6042260",
  ...
}

And by performing a search in the storefront our search results now return our product from our custom search entries.

Development Considerations

Field Handlers are Synchronous

At this time, field handlers have been implemented synchronously and expect the information you require to be available on the provided source entity, e.g. sellable item in this case, therefore if you are looking at sourcing data externally it may be worth further consideration.

Following Helix Priniciples

Whether upgrading from a pre XC 9.3 solution or introducing existing component properties into the search index, by implementing the custom SxaContentFieldHandler you may be faced with having to break helix principles, i.e. housing your custom SxaContentFieldHandler in a feature project while referencing another feature project to retrieve the desired properties/content. As feature projects should not reference each other, I reluctantly chose to add MySxaContentFieldHandler to the Commerce Engine project in this instance.

References

Sitecore Experience Commerce: Associating Inventory from Sellable Item and Variant Pages

In this article, we introduce a small custom plugin for Sitecore Commerce Business Tools that enables the inventory association for sellable items and variants directly from the Merchandising Manager pages.

Establishing inventory information associations is an action hosted within the Inventory Manager. Prior to the introduction of inventory indexes in XC 9.3, locating an existing inventory information record for large catalogs via the Inventory Manager can be quite a tedious task with the pagination controls being the only form of search.

The custom plugin adds the Associate Sellable Item to an inventory set action to the Inventory Sets entity view for the Sellable Item and Variant merchanding pages.

A transitional step to select the Inventory Set is added to the modal view, which in the Inventory Manager is driven by the inventory set being viewed.

The final step locks the Inventory Set and Sellable Item fields, as they have already been identified, and if the sellable item already had been associated to the selected inventory set then this would behave like an edit view, keeping the UX at the forefront of this implementation.

Managing existing inventory information records can also be more achieved via the Sellable Item and Variant pages in the Inventory Sets entity view actions, however prior to XC 9.3 a business was required to create a new entity version to enable these controls, unless the EntityVersionsActionsPolicy had been updated to allow these actions to bypass the entity version (as they should). This process was also documented in Enabling Disassociate, Edit, and Transfer Inventory Actions for Published Sellable Items and Variants.

Source Code: Extended Sitecore Commerce Inventory project repository

Setting Currency Set for Catalog Product List Prices

In this article we go through the process of configuring the Currency Set that will be utilised when adding and editing the List Pricing properties of Sellable Items in the Merchandising Manager.

  1. In the Sitecore Content Editor, Go to /sitecore/Commerce/Commerce Control Panel/Shared Settings/Currency Settings/Currency Sets/.
  2. Copy the Item ID of the desired currency set. In the example below, I have created My Currency Set, which I will be using for this example.

  3. In the Commerce Authoring Environment configuration file (e.g. Habitat.CommerceAuthoring.json), locate the GlobalCurrencyPolicy and update the DefaultCurrencySet to the currency set's Item ID.
    {
        "$type": "Sitecore.Commerce.Core.GlobalCurrencyPolicy, Sitecore.Commerce.Core",
        "DefaultCurrencySet": "AC47503D-731A-4343-A156-3DBC7A5F1C8C"
    }
  4. Run the Bootstrap() command of the Commerce Engine.
  5. Confirm against any Sellable Item in the Merchandising Manager by clicking on the Add or Edit List Price actions.