Scoring elastic search results in search

When we search for a product using SearchPhrase in SearchProducts API, Can we score the results coming from elastic search as name match to be scored higher than description match?

For example when I search iPhone so I should get iPhone mobile-first then the other products where iphone is directly present in the name or title but in the properties or description.

To configure custom scoring by property names, you need:

  1. Create a Virto Commerce module and add nuget package on VirtoCommerce.CatalogModule.Data
  2. Override ProductDocumentBuilder to make properties Searchable
  3. Override ProductSearchRequestBuilder to define custom scoring.
  4. Install the module
  5. Rebuild the product search index and tests

Create a Virto Commerce module

Visual Studio Extension helps to create your new Virto Commerce extension/module promptly and with the recommended project structure. Download and Install

Then use Nuget package manager add add VirtoCommerce.CatalogModule.Data.

Update module.manifest and add dependency from VirtoCommerce.Catalog, it allows to run the module after VirtoCommerce.Catalog and override default implementation.

  ...
  <dependencies>
    <dependency id="VirtoCommerce.Catalog" version="3.0.0" />
  </dependencies>

  <title>Abc.Search.SearchScoring</title>

Override ProductDocumentBuilder to make properties Searchable

Create a new class CustomProductDocumentBuilder and extend VirtoCommerce.CatalogModule.Data.Search.Indexing.ProductDocumentBuilder.

You need to override CreateDocument method, then find and mark required fields as IsSearchable. For example: name.

 public class CustomProductDocumentBuilder : ProductDocumentBuilder
    {
        public CustomProductDocumentBuilder (ISettingsManager settingsManager, IItemService itemService, IProductSearchService productsSearchService) :
            base(settingsManager, itemService, productsSearchService)
        {

        }

        protected override IndexDocument CreateDocument(CatalogProduct product)
        {
            var result = base.CreateDocument(product);

            var field = result.Fields.FirstOrDefault(x => x.Name == "name");
            field.IsFilterable = false;
            field.IsSearchable = true;


            return result;
        }
    }

Override ProductSearchRequestBuilder to define custom scoring

Create a new class CustomProductSearchRequestBuilder and extend VirtoCommerce.CatalogModule.Data.Search.Indexing.ProductSearchRequestBuilder.

You need to override BuildRequestAsync method, then define scoring for properties by using ^. For example, the name is scoring with 2 and __content with 1 for full-text search:

    public class CustomProductSearchRequestBuilder : ProductSearchRequestBuilder
    {
        public GiiftProductSearchRequestBuilder(ISearchPhraseParser searchPhraseParser, ITermFilterBuilder termFilterBuilder, IAggregationConverter aggregationConverter) :
            base(searchPhraseParser, termFilterBuilder, aggregationConverter)
        {
        }

        public override async Task<SearchRequest> BuildRequestAsync(SearchCriteriaBase criteria)
        {
            var request = await base.BuildRequestAsync(criteria);

            request.SearchFields = new List<string> { "name^2", "__content" };

            return request;
        }
    }

4. Override default implementation with new

Add new implementation in Initialize method.

public class Module : IModule
    {
        public ManifestModuleInfo ModuleInfo { get; set; }

        public void Initialize(IServiceCollection serviceCollection)
        {

            serviceCollection.AddTransient<ProductDocumentBuilder, CustomProductDocumentBuilder>();

            serviceCollection.AddTransient<ProductSearchRequestBuilder, CustomProductSearchRequestBuilder>();
        }

        public void PostInitialize(IApplicationBuilder appBuilder)
        {
        }

        public void Uninstall()
        {
        }
    }

2. Create a Virto Commerce module

  1. Build and deploy the custom module.
  2. Go to Search Index and rebuild the Product index.
  3. Test

Now you should be able to see products where the keyword is directly present in the name and then in any other properties. You can use the same schema to boost any property.

1 Like

Hi @OlegoO
I have tried the “ProductSearchRequestBuilder, CustomProductSearchRequestBuilder” override but it’s not working for me.
Build Document is working but there is some issue with ProductSearchRequestBuilder its not even hitting the debug point I have tried the same thing.
I am using the platform 3.253.1 and catalog module 3.230.4.

@Koshalgarg155 Could you share the source code of your extension?

    protected override ISearchRequestBuilder GetRequestBuilder(ProductIndexedSearchCriteria criteria)
    {
        return _searchRequestBuilderRegistrar.GetRequestBuilderByDocumentType("CustomProductSearchRequestBuilder ");
    }

@OlegoO it’s solved now by overriding GetRequestBuilder like this. Then it could pick the extension of the CustomProductSearchRequestBuilder class and register in the module. cs.

This is how I did override the BuildRequestAsync. Other override methods were working. Only this one was not. So by overriding the above method, we are able to override this one also

    serviceCollection.AddTransient<ProductSearchRequestBuilder, CustomProductSearchRequestBuilder>();

    public class CustomProductSearchRequestBuilder: ProductSearchRequestBuilder
    {
        public CustomProductSearchRequestBuilder(ISearchPhraseParser searchPhraseParser, ITermFilterBuilder termFilterBuilder, IAggregationConverter aggregationConverter) :
            base(searchPhraseParser, termFilterBuilder, aggregationConverter)
        {
        }

        public override async Task<SearchRequest> BuildRequestAsync(SearchCriteriaBase criteria)
        {
            var request = await base.BuildRequestAsync(criteria);
            return request;
        }
    }