Optimizing Elastic Search results with Popularity Scores

Martijn

Martijn

Dec 30, 2024

2 min read, ~4 hours to implement

Improve your Vendure store's search by adding product popularity to your ElasticSearch setup. This combines relevance with popularity to give smarter search results and show trending products to meet customer expectations.

Install the ElasticSearch plugin

You can install the ElasticSearch plugin using this guide. You do need an Elastic Cluster running somewhere. Bonsai.io (not affiliated) has some cheaper managed ElasticSearch options for smaller stores. For larger stores, you might want to self-host ElasticSearch, or go with the hosting provided by Elastic themselves.

Install the Popularity Scores plugin

Next, install this plugin to calculate popularity scores per product.

Make ElasticSearch index the popularity score

Add each product's popularity score to the ElasticSearch index, so that we can use it during query time to boost certain results. For this, we use the custom product mappings option in the ElasticSearch plugin.

// vendure-config.ts
ElasticsearchPlugin.init({
  host: process.env.ELASTIC_SEARCH_URL!,
  port: 443,
  customProductMappings: {
    popularityScore: {
      graphQlType: 'Int!',
      valueFn: (product) =>
        (product.customFields as any)?.popularityScore || 0,
    }
  }
}

Don’t forget to reindex your products before proceeding to the next step!

Now that ElasticSearch is aware of each product's popularity, we can boost the rankings of popular products during searches. The following code will modify the original score of a search result, thus reordering the results based on popularity.

You can rewrite the query that is send to ElasticSearch with the plugin's mapQuery option.

ElasticsearchPlugin.init({
  host: process.env.ELASTIC_SEARCH_URL!,
  port: 443,
  customProductMappings: {
    popularityScore: {
      graphQlType: 'Int!',
      valueFn: (product) =>
        (product.customFields as any)?.popularityScore || 0,
    },
  },
  searchConfig: {
    mapQuery: (query, { term }) => {
      if (!term) {
        return query;
      }
      return {
        function_score: {
          field_value_factor: {
            field: 'product-popularityScore',
            factor: 1.2, // Slight impact from popularity
            modifier: 'sqrt', // 'sqrt' smoothes out large popularity values
            missing: 1,
          },
          boost_mode: "sum", // Adds popularity influence to query relevance
          // The original query that was created by Vendure
          query,
        },
      }
    }
  }
})

Tuning popularity and relevance scores

You may need to experiment with the factor, modifier, and boost_mode settings to get the rankings you want.

Available modifier options:

  • none: No transformation is applied; the raw field value is used.
  • log: Reduces the impact of large values.
  • log1p: Handles small or zero values without issues.
  • sqrt: Moderates the impact of large values.
  • reciprocal: Applies 1 / value. Prioritizes documents with smaller field values.

Available boost_mode options:

  • multiply: Multiplies the query score by the field value.
  • sum: Adds the field value to the query score.
  • replace: Replaces the query score with the field value.
  • max: Takes the higher of the query score or field value.
  • min: Takes the lower of the query score or field value.
  • avg: Uses the average of the query score and the field value.

That's it!


Need help implementing search or plugins into your website? We’re here to help!

martijn@pinelab.studio