Skip to main content

Commerce

Optimizely Configured Commerce – Customizing Elasticsearch v7 Index

The Power Of Search Engine. Transforming Industries And Customer Service. A Look Into The Future Of Ai Search. Yellow Loupe Icon Processing Data On Smart Phone. Modern 3d Render

The Optimizely configured commerce introduces Elasticsearch v7 for a better search experience. In the dynamic landscape of the Commerce world, there is always room for extended code customization. Optimizely offers detailed instructions on customizing Elasticsearch v7 indexes.

There are a lot of advantages to using Elasticsearch v7. some are

  • Improved Performance
  • Security Enhancements
  • Elasticsearch SQL
  • GeoJSON Support
  • Usability and Developer-Friendly Features

In this post, we will go through how we will add the custom column in the Elasticsearch v7 index step by step.

Setting Elasticsearch v7 as a Default Provider from the Admin

The very first step is to set a default provider in admin. Below are the steps to set the default provider:

  1. Login into Admin
  2. Navigate to the Settings
  3. Search for “Search Provider Name”
  4. Set Elasticsearch v7 into “Search Provider Name” and “Search Indexer Name” (See Screenshot)
  5. Click on Save.

 

Creating Custom Field

After configuring the default provider in the admin section, the site will use Elasticsearch v7, conducting searches on indexes newly established by Elasticsearch v7.

If we want to add a new custom field to these indexes, Optimizely provides some pipelines to add the new custom field.

Add a Class into the Solution to Extend the ElasticsearchProduct Class and Create a New Field

In this class, we have created a property named StockedInWharehouses which is the type of list of strings.

namespace Extensions.Search.ElasticsearchV7.DocumentTypes.Product
{
 using Insite.Search.ElasticsearchV7.DocumentTypes.Product;
 using Nest7;

 [ElasticsearchType(RelationName = "product")]
 public class ElasticsearchProductCustom : ElasticsearchProduct
 {
     public ElasticsearchProductCustom(ElasticsearchProduct source)
         : base(source) // This constructor copies all base code properties.
     {
     }

     [Keyword(Index = true, Name = "stockedInWarehouses")]
     public List<string> StockedInWarehouses { get; set; }
 }
}

Override Pipeline to Insert Data into Custom Property

To add the data into custom property, use a PrepareToRetrieveIndexableProducts class extension. Handle data retrieval within custom code by composing a LINQ query to fetch the required data. The best performance achive by returing Dictonary like.ToDictionary(record => record.ProductId). Here is an example code snippet

namespace Extensions.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Pipes.PrepareToRetrieveIndexableProducts
{
 using Insite.Core.Interfaces.Data;
 using Insite.Core.Plugins.Pipelines;
 using Insite.Data.Entities;
 using Insite.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Parameters;
 using Insite.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Results;
 using System.Linq;

 public sealed class PrepareToRetrieveIndexableProducts : IPipe<PrepareToRetrieveIndexableProductsParameter, PrepareToRetrieveIndexableProductsResult>
 {
     public int Order => 0; // This pipeline has no base code so Order can be anything.

     public PrepareToRetrieveIndexableProductsResult Execute(IUnitOfWork unitOfWork, PrepareToRetrieveIndexableProductsParameter parameter, PrepareToRetrieveIndexableProductsResult result)
     {
         result.RetrieveIndexableProductsPreparation = unitOfWork.GetRepository<ProductWarehouse>().GetTableAsNoTracking()
               .Join(unitOfWork.GetRepository<Product>().GetTableAsNoTracking(), x => x.ProductId, y => y.Id, (x, y) => new { prodWarehouse = x })
               .Join(unitOfWork.GetRepository<Warehouse>().GetTableAsNoTracking(), x => x.prodWarehouse.WarehouseId, y => y.Id, (x, y) => new { Name = y.Name, productId = x.prodWarehouse.ProductId })
               .GroupBy(z => z.productId).ToList()
               .Select(p => new {
                   productId = p.Key.ToString(),
                   warehouses = string.Join(",", p.Select(i => i.Name))
               })
               .ToDictionary(z => z.productId, x => x.warehouses);

         return result;
     }
 }
}

Assign Data to Custom Property in the Elasticsearch7

After retrieving data into the “RetrieveIndexableProductsPreparation” result property, set the data into a custom property for indexable products. To achieve this crate a class “ExtendElasticsearchProduct” and extend with IPipe<CreateElasticsearchProductParameter, CreateElasticsearchProductResult>

Here in the execute method, the parameter contains the RetrieveIndexableProductsPreparation property and this contains our data. Fetch this data using the TryGetValue method.

Avoid having the logic to fetch data return in the CreateElasticsearchProductResult extension class. Writing the data retrieval logic in this class will impact the performance of creating the product indexes.

Here you’ll find an illustrative code snippet:

namespace Extensions.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Pipes.CreateElasticsearchProduct
{
 using System;
 using System.Collections.Generic;
 using Insite.Core.Interfaces.Data;
 using Insite.Core.Plugins.Pipelines;
 using Insite.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Parameters;
 using Insite.Search.ElasticsearchV7.DocumentTypes.Product.Index.Pipelines.Results;

 public sealed class ExtendElasticsearchProduct : IPipe<CreateElasticsearchProductParameter, CreateElasticsearchProductResult>
 {
     public int Order => 150;

     public CreateElasticsearchProductResult Execute(IUnitOfWork unitOfWork, CreateElasticsearchProductParameter parameter, CreateElasticsearchProductResult result)
     {
         var elasticsearchProductCustom = new ElasticsearchProductCustom(result.ElasticsearchProduct);

         if (((Dictionary<Guid, int>)parameter.RetrieveIndexableProductsPreparation).TryGetValue(elasticsearchProductCustom.ProductId.ToString(), out var stockedInWarehouses))
         {
            elasticsearchProductCustom.StockedInWarehouses = ExtractList(stockedInWarehouses);
         }

         result.ElasticsearchProduct = elasticsearchProductCustom;

         return result;
     }

     private static List<string> ExtractList(string content)
     {
       if (string.IsNullOrWhiteSpace(content))
         return new List<string>();
       return content
        .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
        .ToList();
     }
 }
}

After rebuilding the full product index, it displays the newly created ‘StockedInWarehouses’ column in product indexes. The below screeshot showing index with value

Term Query to filter StockedInWarehouses

Now you can easily use the StockedInWarehouses field in term query to filter out search results.

var currentWarehouseId = SiteContext.Current.PickUpWarehouseDto == null
   ? SiteContext.Current.WarehouseDto.Name
   : SiteContext.Current.PickUpWarehouseDto.Name;  
 
result.StockedItemsOnlyQuery = result
      .SearchQueryBuilder
      .MakeTermQuery("stockedInWarehouses.keyword", currentWarehouseId);

Reference Link : https://docs.developers.optimizely.com/configured-commerce/docs/customize-the-search-rebuild-process

 

Thoughts on “Optimizely Configured Commerce – Customizing Elasticsearch v7 Index”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Prasad Joshi

I am a technical expert who provides leadership in developing technology-enabled applications and services within the Optimizely Commerce framework and ZNode eCommerce framework.

More from this Author

Follow Us