In ecommerce, you often have a notion of base products and variants (or “variations”). For example, a certain type of t-shirt (the base product) might be available in different colors and sizes (the variants), or a certain phone (the base product) comes in different memory capacity configurations (the variants).

You can store this data in Algolia in two ways:

  • Variant-level records: one Algolia record for each product variation.
  • Product-level records: one Algolia record for each base product. All variations are contained in this record.

Record structure examples

Suppose you have a t-shirt available in two colors, each color being available in two sizes.

With the variant-level records model, you index each variant in a separate record:

json
[
  {
    "name": "V-neck t-shirt",
    "category": "Sport",
    "color": "White",
    "size": "M",
    "image": "v-neck-t-shirt-white.jpg",
    "price": 19.99
  },
  {
    "name": "V-neck t-shirt",
    "category": "Sport",
    "color": "White",
    "size": "L",
    "image": "v-neck-t-shirt-white.jpg",
    "price": 19.99
  },
  {
    "name": "V-neck t-shirt",
    "category": "Sport",
    "color": "Blue",
    "size": "M",
    "image": "v-neck-t-shirt-blue.jpg",
    "price": 22.99
  },
  {
    "name": "V-neck t-shirt",
    "category": "Sport",
    "color": "Blue",
    "size": "L",
    "image": "v-neck-t-shirt-blue.jpg",
    "price": 22.99
  }
]

With the product-level records model, all variants associated with a given base product are in a single record. The common attributes are at the top level while the variation attributes (in this example color and size, but also corresponding images and prices) are nested in an array, with an array element corresponding to each unique variant:

json
{
  "name": "V-neck t-shirt",
  "category": "Sport",
  "variants": [
    {
      "color": "White",
      "size": "M",
      "image": "v-neck-t-shirt-white.jpg",
      "price": 19.99
    },
    {
      "color": "White",
      "size": "L",
      "image": "v-neck-t-shirt-white.jpg",
      "price": 19.99
    }
    {
      "color": "Blue",
      "size": "M",
      "image": "v-neck-t-shirt-blue.jpg",
      "price": 22.99
    },
    {
      "color": "Blue",
      "size": "L",
      "image": "v-neck-t-shirt-blue.jpg",
      "price": 22.99
    }
  ]
}

Choose your record model

To choose between the two models, one of the main decision factors is the use of Algolia AI and merchandising features.

If you use Algolia’s AI or merchandising features, the product-level records model might have the following advantages if this also reflects your product and merchandising dynamics:

  • Click and conversion events generated by each variants are grouped by base product. This means that AI can re-rank or personalize results accordingly at the product level and not only for specific variants.
  • It might also be preferable to manage Rules and merchandising on the product-level, especially when dealing with a large number of variants that should be treated similarly. For example, you can promote or hide specific items on a per-product, rather than per-individual variant, basis.
  • Search analytics results are grouped by product, making them easier to analyze.

If you don’t use AI or merchandising or prefer having these features operate on a per-variant basis, using variant-level records has the following advantages:

  • For each query, the returned variant record is the most textually relevant. For example typing “red shoes” directly returns variants that have those words in their searchable attributes or facets. In the product-level model, as the returned record contains all variations, displaying the correct image requires frontend work.
  • Better support for faceting on several attributes. For example, if a user filters on color: green AND size:40, Algolia returns only records of variants that have both characteristics. In the Product-level model, a record is part of the results if one of the variants matches color:green and another variant matches size:40.
  • You can individually update the price, availability, or any other attribute of a single variant, while the product-level model requires you to re-send the complete array of variants (Algolia doesn’t support partial updates of arrays).

Capabilities by record model

The following table summarizes (in a simplified view) which model is best suited depending on your needs.

Variant-level recordsProduct-level records
AI FeaturesPer variantPer product
Rules and MerchandisingPer variantPer product
Search analyticsPer variantPer product
Textual relevanceOptimizedSupported
Faceting supportOptimizedRequires frontend work (see below)
Variant powered PLPOptimizedRequires frontend work (see below)
Granular variant updateSupported-
Average record size*Optimized
Number of recordsHighOptimized

*: the average record size for the Variant-level model highly depends if you want to display color swatches or images carousels in your results. Indeed, to display those, each of your variant records must contain information or images of their siblings, which has an impact on the average record size.

Results display

Group records per product

Depending on the desired consumer end user experience as well as catalog size, displaying only one result tile per product, instead of showing each variant of the same product as separate results, may be preferable.

With the product-level record model, you get one result per product by default.

With the variant-level record model, you need to use Algolia’s distinct feature to achieve this experience. A good way is to add the ID of the base product to the record and apply the distinct feature on it:

jsonc
{
  "name": "V-neck t-shirt",
  "baseProductID": "v-neck-t-shirt-001",
  ...
}

Facets

An important search feature is faceting, which enables users to refine their results.

In the variant-level record model, users can directly narrow down the results to individual variants without modifying the UI code. This is because Algolia returns only the records of the variants matching the selected facets.

With the product-level record model, each match returns the full record, including all variants. To only show the variants that match the selected facets, you must post-process the search results in your InstantSearch code. Here is what the transformItems function could look like:

js
hits({
  // ...
  transformItems(items, { results }) {
    return items.map((item) => {
      const colorFacets =
        results._state.disjunctiveFacetsRefinements["variants.color"] || [];

      let selectedVariant;
      if (colorFacets.length > 0) {
        selectedVariant = item.variants.find((variant) => {
          return colorFacets.includes(variant.color);
        });
      } else {
        selectedVariant = item.variants[0];
      }

      item.image = selectedVariant.image;
      item.price = selectedVariant.price;
      item.url = selectedVariant.url;

      return item;
    });
  },
});