BizFx – Add new field in search criteria in Customers, Orders, Catalogs

Hi Folks,

While working in Sitecore Commerce 9.3, we got a requirement to add some more field to search in Customer business tool dashboard. So we decided to look into how it was implemented. So we came to know that we have related blocks in 2 pipelines –

  1. IFullIndexMinionPipeline
  2. IIncrementalIndexMinionPipeline

If we see in NodeConfiguration it looks like below –

Sitecore.Commerce.Plugin.Search
IFullIndexMinionPipeline (Sitecore.Commerce.Plugin.Search.SearchIndexMinionArgument => System.Collections.Generic.IEnumerable1[[Sitecore.Commerce.Plugin.Search.IndexingResult, Sitecore.Commerce.Plugin.Search, Version=5.0.0.0, Culture=neutral, PublicKeyToken=null]]) ------------------------------------------------------------ Plugin.Search.InitializeIndexingViewBlock (Sitecore.Commerce.Plugin.Search.SearchIndexMinionArgument => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.FaultInjection.MinionFaultBlock (System.Object => System.Object) ------------------------------------------------------------ Plugin.Customers.InitializeCustomersIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Orders.InitializeOrdersIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Catalog.InitializeCategoryIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Catalog.InitializeCatalogIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Catalog.InitializeSellableItemIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Inventory.InitializeSellableItemIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Promotions.InitializePromotionBookIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Promotions.InitializePromotionsIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Pricing.InitializePriceBooksIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Pricing.InitializePriceCardsIndexingViewBlock (Sitecore.Commerce.EntityViews.EntityView => Sitecore.Commerce.EntityViews.EntityView) ------------------------------------------------------------ Plugin.Search.ProcessItemsToAddOrUpdateBlock (Sitecore.Commerce.EntityViews.EntityView => System.Collections.Generic.IEnumerable1[[Sitecore.Commerce.Plugin.Search.IndexingResult, Sitecore.Commerce.Plugin.Search, Version=5.0.0.0, Culture=neutral, PublicKeyToken=null]])

We were working for Customer dashboard, so found InitializeCustomersIndexingViewBlock , similarly for order dashboard we can see InitializeOrdersIndexingViewBlock and for catalog as well we can see similar blocks.

Now we had 2 way to update new fields, either we can override this existing block or add a new block after InitializeCustomersIndexingViewBlock. But we overrode existing block and created new block for this with name CustomInitializeCustomersIndexingViewBlock.

In this block we added code as is and added our additional code along with this.

using Sitecore.Commerce.Core;
using Sitecore.Commerce.EntityViews;
using Sitecore.Commerce.Plugin.Customers;
using Sitecore.Commerce.Plugin.Search;
using Sitecore.Framework.Conditions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ProcessDocumentSearchResultBlock = Sitecore.Commerce.Plugin.Search.ProcessDocumentSearchResultBlock;
using Sitecore.Framework.Pipelines;

namespace Plugin.xyz.EntityViews.EntityViews
{
    [PipelineDisplayName("Customers.block.CustomInitializeCustomersIndexingViewBlock")]
    public class CustomInitializeCustomersIndexingViewBlock : PipelineBlock<EntityView, EntityView, CommercePipelineExecutionContext>
    {
        /// <summary>
        /// Runs the specified argument.
        /// </summary>
        /// <param name="arg">The argument.</param>
        /// <param name="context">The context.</param>
        /// <returns>An <see cref="EntityView"/></returns>
        public override Task<EntityView> Run(EntityView arg,
      CommercePipelineExecutionContext context)
        {
            Condition.Requires(arg).IsNotNull($"{this.Name}: argument cannot be null.");
            var argument = context.CommerceContext.GetObjects<SearchIndexMinionArgument>().FirstOrDefault();
            if (string.IsNullOrEmpty(argument?.Policy?.Name))
            {
                return Task.FromResult(arg);
            }

            var customers = argument.Entities?.OfType<Sitecore.Commerce.Plugin.Customers.Customer>().ToList();
            if (customers == null || !customers.Any())
            {
                return Task.FromResult(arg);
            }

            var searchViewNames = context.GetPolicy<KnownSearchViewsPolicy>();

            customers.ForEach(
                customer =>
                {
                    var documentView = arg.ChildViews.Cast<EntityView>()
                        .FirstOrDefault(v => v.EntityId.Equals(customer.Id, StringComparison.OrdinalIgnoreCase) && v.Name.Equals(searchViewNames.Document, StringComparison.OrdinalIgnoreCase));
                    if (documentView == null)
                    {
                        documentView = new EntityView
                        {
                            Name = context.GetPolicy<KnownSearchViewsPolicy>().Document,
                            EntityId = customer.Id
                        };
                        arg.ChildViews.Add(documentView);
                    }

                    documentView.Properties.Add(new ViewProperty { Name = "EntityUniqueId", RawValue = customer.UniqueId });
                    documentView.Properties.Add(new ViewProperty { Name = "EntityId", RawValue = customer.Id });
                    documentView.Properties.Add(new ViewProperty { Name = "UserName", RawValue = customer.UserName });
                    documentView.Properties.Add(new ViewProperty { Name = "Email", RawValue = customer.Email });
                    documentView.Properties.Add(new ViewProperty { Name = "Status", RawValue = customer.AccountStatus });
                    documentView.Properties.Add(new ViewProperty { Name = "FirstName", RawValue = customer.FirstName });
                    documentView.Properties.Add(new ViewProperty { Name = "LastName", RawValue = customer.LastName });
                    documentView.Properties.Add(new ViewProperty { Name = "DateCreated", RawValue = customer.DateCreated });
                    documentView.Properties.Add(new ViewProperty { Name = "DateUpdated", RawValue = customer.DateUpdated });
                    documentView.Properties.Add(new ViewProperty { Name = "ArtifactStoreId", RawValue = context.CommerceContext.Environment.ArtifactStoreId });                    
                    documentView.Properties.Add(new ViewProperty { Name = "CustomField1", RawValue = "CustomField1Value" });

                });
            return Task.FromResult(arg);
        }

        public CustomInitializeCustomersIndexingViewBlock()
      : base((string)null)
        {
        }        
    }
}

After this we added this block in configureSitecore.cs like below –

.ConfigurePipeline<IFullIndexMinionPipeline>(
                        configure =>
                        {
                            configure.Replace<Sitecore.Commerce.Plugin.Customers.InitializeCustomersIndexingViewBlock, Plugin.xyz.EntityViews.EntityViews.CustomInitializeCustomersIndexingViewBlock>();
                        })
                    .ConfigurePipeline<IIncrementalIndexMinionPipeline>(
                        configure =>
                        {
                            configure.Replace<Sitecore.Commerce.Plugin.Customers.InitializeCustomersIndexingViewBlock, Plugin.xyz.EntityViews.EntityViews.CustomInitializeCustomersIndexingViewBlock>();
                        })

After these changes we added these custom fields in PlugIn.Search.PolicySet-1.0.0.json like below into CustomerScope section and did Bootstrap via Postman

"$type": "Sitecore.Commerce.Plugin.Search.IndexablePolicy, Sitecore.Commerce.Plugin.Search",
        "SearchScopeName": "CustomersScope",
        "Properties": {...
...

"CustomField1": {
            "TypeName": "System.String",
            "IsKey": false,
            "IsSearchable": true,
            "IsFilterable": false,
            "IsSortable": false,
            "IsFacetable": false,
            "IsRetrievable": true
          }

Then we tried to rebuild customer index from postman (Run FullIndex Minion – Customers), and logged in into Business Tool and navigate to Customer Dashboard and tried to find for our custom field values but we didn’t get anything in the search. So, what we were missing here. Then we came to know that we need to do entry in solr index in managed-schema file. Thanks to Andrei Paliakou, who helped me in this. We need to do these entry in 2 solr cores.

  1. CustomersScope (C:\Solr\sc93-solr-8.1.1\server\solr\CustomersScope\conf\managed-schema)
  2. CustomersScope-Rebuild (C:\Solr\sc93-solr-8.1.1\server\solr\CustomersScope-Rebuild\conf\managed-schema)

Open managed-schema file and tried to find

<!-- CommerceEngine Customer -->
<field name="username" type="string" indexed="true" stored="true" />

Add custom field here after artifactstoreid like below –

<field name="artifactstoreid" type="string" indexed="true" stored="true" />
<field name="customField1" type="string" indexed="true" stored="true" />

Also in the same file try to find below and add custom field here as well-

<!-- This can be enabled, in case the client does not know what fields may be searched. It isn't enabled by default
         because it's very expensive to index everything twice. -->
<copyField source="username" dest="_text_"/> ...
...
<copyField source="lastName" dest="_text_"/>
<copyField source="customField1" dest="_text_"/>

And now Run the customer rebuild index from postman and try to search our customField value and you will see the result this time.

That’s it.

Happy Coding 🙂

One thought on “BizFx – Add new field in search criteria in Customers, Orders, Catalogs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s