Skip to main content
Autocomplete, InstantSearch, and Recommend in the Algolia app blocks now support version 5 of the Algolia API client. Most stores only need to switch the search client version setting to Version 5. If you use custom storefront JavaScript, follow the version 5 upgrade guide. Support for version 4 ends on August 14, 2026.
The Algolia AI Search & Discovery app can index your catalog in one of two indexing modes:
  • Variant-level. One Algolia record per Shopify variant. The default.
  • Product-level. One record per Shopify product, with its variants nested inside the record.
Both modes write to the same PREFIX_products. The mode changes the shape of the records in it, which attributes you can facet and sort on, and how your storefront renders results.

How to get access

This is a beta feature according to Algolia’s Terms of Service (“Beta Services”).
To get access to this feature, contact the Algolia support team. The Indexing mode setting stays hidden until it’s enabled, or if your store already uses product-level records.

Choose an indexing mode

Choose product-level indexing if you want: Choose variant-level indexing if:
  • Shoppers need to land on a specific variant from search results
  • You facet or sort on per-variant attributes, such as SKU or weight
  • Your shoppers filter on two or more facets at once, such as color and size. See Facet combinations across variants

Facet combinations across variants

If your shoppers filter across two or more facets, use variant-level indexing with Display one result per product. That returns one card per product while filters still apply per variant, so combinations stay accurate. Product-level indexing turns that setting off, so the two approaches are mutually exclusive. Product-level records don’t fit this case because a product record carries the option values of all its variants in one place. Algolia treats each nested attribute as multi-valued, so two refinements combine at the product level, never within a single variant. Take a product with two variants, Black/128GB and White/256GB. The record holds Black, White, 128GB, and 256GB. Filtering on Color Black and Size 256GB returns that product, even though no such variant exists. The same applies to any pair of per-variant attributes:
  • Option against option. Color and size, or color and material.
  • Option against price. If Black costs 300 and White costs 150, filtering on Color Black and a 100 to 200 price bucket returns the product.
  • Option against stock. inventory_available and variants_inventory_count describe the whole product, so Color Black plus in-stock means the product has a Black variant and has stock somewhere, not that Black is in stock.
The card also preselects the refined color and links to that variant, so a product matched on a combination it doesn’t stock looks like a correct result. Single-option catalogs aren’t affected, and selecting several values within one facet still filters correctly.

Limits to check before you switch

  • High variant counts. A product record holds every variant, so a product that fit as many small variant records can exceed Algolia’s record size limit as one product record. Algolia rejects the oversized record, and that product goes missing from search results. Selecting more optional fields makes this more likely.
  • SKU and barcode search. These don’t work until you adjust your searchable attributes. See Searchable attributes.
  • Discount and weight sorts. Neither has a product-level equivalent. See Sort orders.

Turn on product-level indexing

Changing the indexing mode triggers a full reindex and replaces every record in your products index. On a store with a live storefront, read Migrate from variant-level indexing first.
  1. In your Shopify admin, open the Algolia AI Search & Discovery app.
  2. Go to Indexing.
  3. In the Indexing section, make sure Index my Catalog is selected.
  4. Under Indexing mode, read the Review required before changing indexing mode banner. Select the checkbox to confirm you’ve reviewed it, then select Confirm. The mode options, the field checklist, and Save stay locked until you do.
  5. Select Product.
  6. Optional: to add per-variant data to your records, select the fields you want in the checklist under Product. See Product record fields.
  7. Select Save, then select Continue to confirm the reindex.
The app rewrites your facets and sort orders for the new record shape, then reindexes your catalog. Search keeps serving the old records until the new ones land.
  • The acknowledgement is stored in your browser, per store and per browser. It isn’t saved to your account. In a private window, or in a browser that blocks site data, you confirm again each time you open the app.
  • The reindex is queued, not immediate. The reindex indicator appears on the next status refresh rather than on save, so a short delay isn’t a failed save.
Changing the indexing mode and the record fields in the same save reindexes once. Changing other indexing settings in the same save, such as inventory locations or metafields, can trigger a second reindex. Some controls only update after you save. Until then, the Search options tab still lists the sort orders that product-level indexing deactivates.

Product record schema

Algolia stores your products in the following format:
JavaScript
Product-level records reuse the definitions on the index schema page for recently ordered count, named tags, metafields, and the rules that remove products from search results. If you enabled Markets, each market index gets its own product records, and every record carries a market_pricing_CURRENCY object. In product-level mode, that object holds the product-level price fields, such as variants_min_price and variants_max_price, instead of the variant-level variant_min_price and variant_max_price. Product records don’t include created_at or updated_at. The field checklist doesn’t have an entry for either, so theme code that reads them at the top level of a product record finds nothing. published_at is unaffected and always present.

Variant sub-records

Each product record holds a variants array with one entry per variant, excluding variants whose title contains [hidden]. Publication status, the algolia-ignore tag, and [hidden] in the product title all apply to the whole product rather than to single variants. Variant sub-records keep the per-variant data that a product record can’t hold at the top level: price, inventory, option values, and variant metafields. variantID isn’t an objectID. Algolia ranks, facets, and counts the product record, not its variant entries. A query that matches one variant returns the whole product. Metafields appear at both levels. Product metafields land in the record’s meta, and variant metafields in variants[].meta.

Product record fields

By default, product records stay small and carry only what search, faceting, and the product card need. Per-variant fields are opt-in. The checklist sits under the Product option in the Indexing section, and only in product-level mode. Select the fields you want, then select Save and confirm the reindex. variants[].options, variants[].price, variants[].compare_at_price, variants[].price_ratio, and variants[].inventory_available are always included. Clearing Variant options removes only the flat option1, option2, and option3 fields. Option data stays in variants[].options and option_names. If you enabled locations inventory, the app adds variants[].locations_inventory for you. Selecting more fields makes each record larger. On a catalog with many variants per product, that raises the risk of exceeding Algolia’s record size limit. See Limits to check before you switch. Older per-attribute settings for weight, requires shipping, and inventory locations combine with this checklist rather than replacing it. If your store has one turned on, that field stays in your records even when you clear the checkbox here. Product-level mode also hides those older toggles, so contact Algolia support to check or turn them off.
When the app creates your index, it marks inventory_quantity, locations_inventory, variants_inventory_count, and recently_ordered_count as unretrievable. Those are top-level paths and don’t cover variants.inventory_quantity or variants.locations_inventory. If you select Inventory quantity in product-level mode, add both nested paths to your index’s unretrievable attributes in the Algolia dashboard to keep per-variant stock levels out of public search responses.

Swatches

Product-level records index Shopify’s option value swatches. For every product option whose values carry a swatch, the app adds an entry to swatches with the option name and its values, each with a hex color, an image, or both. The app skips options without swatch data, so a product with no swatches in Shopify has no swatches attribute. The app also skips a custom option with plain text values, such as a Color option with “Red” and “Blue” and no swatches set. There’s no setting to turn swatches on or off. Swatch colors and images live on Shopify metaobjects rather than on the product, and the app’s real-time updates follow product changes. Editing a swatch’s color or image therefore doesn’t reach your records on its own, and the swatches attribute keeps its last indexed value. Other edits to the product reach your records as usual. To pick up a swatch edit, update the product or trigger a full reindex from the Indexing tab. If a product card doesn’t show a swatch row, check that the product’s option values have swatches assigned in the Shopify admin, then update the product or reindex. For how swatch chips render on search results, see Product cards.
Only the first swatches entry renders chips on product cards, and only that option drives preselection. Refining any other swatch option preselects nothing. If your store has several options with swatches, move the one you want on product cards to position 1 in Shopify. To reorder, cap, or filter the chips without changing Shopify, use the beforeInstantSearchSwatchOptionResolver hook.

Preselected swatches

A card doesn’t always show the product’s default option value. The app blocks preselect a swatch chip, swap the card image to that value’s photo, and point the card at that value’s variant when either of these happens:
  • The shopper refines a swatch option. Refining Color to “Blue” preselects Blue on every card that has it. When the shopper refines several values, the first of the card’s own swatch values wins. That order is the one beforeInstantSearchSwatchOptionResolver produces, so a hook that reorders chips changes which refined value wins.
  • The query matches a swatch name. Searching “blue” preselects the Blue chip, because the app adds swatches.values.name to your searchable attributes.
A refinement outranks a query match. The app prefers whole-value matches, but a query of “blue” can still preselect a chip named “Light Blue”. Typos don’t preselect, because the app disables typo tolerance on swatches.values.name. The app marks the chip on first render and doesn’t fire the afterInstantSearchSwatchClick hook. To override which chip is marked, or to sort matched chips first, use beforeInstantSearchTransformItems and edit _matched on the swatch option.

Product cards

Product-level cards describe a product, so they differ from variant-level cards: Selecting a swatch chip swaps the image, marks the chip selected, and updates the card link. It doesn’t navigate. Availability comes from the variants that match the chip, so sold-out values render crossed out and faded. Sold-out chips stay selectable, and the chips aren’t reachable by keyboard. Autocomplete rows show the product’s hero image, the same price range, and a plain product link. They don’t have a swatch row.
The app’s Recommend widgets don’t handle product-level records. They read attributes that only exist on variant records, so tiles render an invalid price and a placeholder image, and the tile link points at a product handle with the product ID in the variant parameter. Check your storefront for Recommend widgets before you switch.
Custom code that reads record attributes needs to know the mode. Use hit._productLevel inside template and transform hooks, because it describes the record being rendered. Use window.algoliaShopify.config.indexing_mode, which returns "product" or "variant", only when building query parameters. The two can disagree during a reindex, and the per-hit value is the reliable one.

Facets

The app rewrites the facets you configure in the Search options tab onto the product record shape, for search and for collection pages: The app always keeps these facets on: tags, option_names, variants.options, filterOnly(inventory_available), and filterOnly(variants_inventory_count). Named tags add named_tags_names and named_tags, and collection indexing adds searchable(collections) and collection_ids. Your saved configuration keeps the variant-level names, and the app translates them on each write. The Algolia dashboard therefore shows variants.options.color in your index settings while the app shows options.color. That mismatch is deliberate.
Facets on any other per-variant attribute pass through unchanged and don’t return any values, because the attribute no longer exists at the top level of the record. This affects facets on sku, barcode, variant_title, compare_at_price, price_ratio, inventory_quantity, inventory_policy, grams, weight, taxable, option1, option2, option3, and variant metafields. Re-create them with a variants. prefix, such as variants.grams.
If your store’s stock policy is deny, the app blocks filter on variants_inventory_count > 0 instead of inventory_quantity > 0. Filters in your own theme code have the same problem. If you filter on inventory_quantity > 0, as the out-of-stock products page describes, switch to inventory_available or variants_inventory_count.

Facet panels with no values

A facet panel on your storefront can render its header without returning any values. Three causes:
  • Price bucket facets. The app blocks query price_range while your index is faceted on price_ranges. Use a price range facet only if you configure the attribute by hand in the Algolia dashboard.
  • Hierarchical facets. A hierarchical facet on an option or on price sends a variant-level attribute path for each level of the hierarchy, so it doesn’t match the remapped attributes. Use a standard facet instead.
  • A stale storefront configuration. The remapping runs from the app’s configuration in your theme. If that configuration is missing or predates the mode change, the storefront queries variant-level attribute names against product records. Cards still render correctly, which makes this hard to spot. Saving in the app rewrites it.

Sort orders

Each sort order is backed by a replica index that ranks on one attribute. Product-level mode re-points the sort orders whose attribute moved, and turns off the ones with no product-level equivalent. These are the default labels. You can rename any sort order, so your list can differ. The app deactivates Highest discount first and Heaviest first on save and hides them from the Sort orders list, so you can’t activate them. Selecting Weight in the field checklist still adds weight data to your records, but the app doesn’t provide a product-level weight sort. Deactivating a sort order deletes its replica index. The search analytics attached to that replica go with it. Switching back to variant-level mode lists both sort orders again, still deactivated, and re-enabling them builds new replicas. Product-level records don’t need Display one result per product, described on the index schema page, so the app turns it off, hides the control, and stops applying distinct on search results. A stale attributeForDistinct value can remain in your index settings, where it’s visible in the Algolia dashboard but doesn’t have any effect. After any full reindex, including a mode change, recently_ordered_count restarts from nothing and rebuilds as orders come in, so Most popular ranks on an empty attribute until orders accumulate.

Searchable attributes

When you switch to product-level mode, the app adds unordered(swatches.values.name) to your searchable attributes so shoppers can search on swatch names, and disables typo tolerance on that attribute. It leaves the rest of the list alone, and the default list targets variant-level records:
  1. unordered(vendor)
  2. unordered(title)
  3. unordered(variant_title)
  4. product_type
  5. unordered(tags)
  6. unordered(handle)
  7. sku
  8. barcode
  9. unordered(body_html_safe)
The app applies that default only when it creates the index, so your live list is whatever your store has accumulated. In product-level mode, variant_title, sku, and barcode don’t exist at the top level of the record, so those three entries match nothing and shoppers can’t find products by SKU or barcode. To restore them, edit Searchable attributes in the Algolia dashboard, under Search > Configuration > Relevance Essentials > Searchable attributes, and replace those entries with their nested paths:
  • unordered(variants.variant_title)
  • variants.sku
  • variants.barcode
variants[].sku and variants[].barcode only exist if you selected SKU and Barcode in Product record fields, so select those first. Switching back to variant-level mode doesn’t remove the swatch entry. It matches nothing on variant records, so it’s safe to leave in place.
Option values aren’t searchable by default in either mode. option_names holds the option names, such as “Color”, not their values. To match a query like “red shirt” on the option value, add unordered(variants.options) to your searchable attributes. It repeats every option value once per variant and makes them matchable on every query, which can cost you relevance.

Custom ranking

The default custom ranking the app applies to a new index references recently_ordered_count, updated_at, and the flat variant option fields. On product-level records, updated_at is absent and option1, option2, and option3 exist only if you select Variant options. Review your custom ranking in the Algolia dashboard after you switch, and replace any criterion that points at an attribute your product records don’t carry.

Migrate from variant-level indexing

The app changes indexing mode in place and runs a full reindex. The app doesn’t move the work you did in the Algolia dashboard. Your rules, pins, and synonyms reference variant objectIDs and variant-level attributes, and every variant record disappears at the swap. Plan the migration in the order below.

Understand what changes in your records

Every attribute that describes a single variant moves under variants. Everything that describes the product stays where it is. These stay unchanged: title, handle, vendor, product_type, tags, named_tags_names, body_html_safe, option_names, variants_min_price, variants_max_price, variants_compare_at_price_min, variants_compare_at_price_max, variants_inventory_count, inventory_available, collections, collection_ids, category, recently_ordered_count, and _tags. The table above doesn’t capture these: variants_inventory_count and recently_ordered_count are unretrievable by default on new indices. They’re on the record and available for filtering and sorting, but your theme can’t read them unless you change that setting.

Know what you lose

  • Variant-level search results. A query for “red” returns the whole product, not its red variant. Product cards link to the product detail page, where the shopper picks the variant. Selecting a swatch chip points the card at that value’s variant, so shoppers can still reach one from search results.
  • Accurate facet combinations. See Facet combinations across variants.
  • Per-variant pricing on cards. Cards show a price range built from variants_min_price and variants_max_price, with no compare-at price and no sale badge.
  • Discount and weight sort orders. Their replica indices, and the analytics on them, go too. See Sort orders.
  • Recommend widget rendering. See the warning in Product cards.
  • Click and conversion history. Your Algolia analytics, Recommend models, Dynamic Re-Ranking, and Personalization profiles are keyed on variant objectIDs. Those IDs stop existing. Recommend results stay degraded until the models retrain against the product records. Historical analytics don’t carry over.
  • Rules, pins, and synonyms that reference variants. A pin on a variant objectID never matches a record again.

Know what the app updates for you

When you change indexing mode, the app:
  • Re-points its indexing pipeline to build product records, and reindexes the catalog.
  • Remaps the facets you configured in the app onto the product record shape, for search and for collection pages.
  • Rebuilds sort-order replicas on the product-level ranking attributes, and deactivates the two with no equivalent.
  • Turns off Display one result per product.
  • Adds swatches to your records.
  • Adds swatches.values.name to your searchable attributes.
  • Sends product objectIDs from the Shopify web pixel, so add-to-cart and purchase events attribute to your product records.
  • Keeps your saved configuration, so switching back to variant-level mode restores the facets and sort orders the app remapped.
The app doesn’t update:
  • Collection facet ordering rules. They keep their variant-level attribute names. Re-save the facets on each collection after the switch.
  • Index settings you changed by hand. The reindex preserves them rather than rewriting them for the new record shape, and saving settings in the app can overwrite them.

Update your configuration and theme

Before you change the mode:
  1. Audit your facets. Any facet on a per-variant attribute needs a variants. prefix. See the warning in Facets.
  2. Audit your searchable attributes. SKU and barcode search stops working until you re-point them. See Searchable attributes.
  3. Audit your theme code and custom hooks. Search for hit.id, hit.price, hit.sku, hit.variant_title, hit.inventory_quantity, hit.options, and hit.image. Each one moved. hit.objectID replaces hit.id.
  4. Check any code that reads dates. hit.published_at changes from an ISO-8601 string to an integer Unix timestamp, so date parsing and formatting on it needs updating. hit.created_at and hit.updated_at disappear from product records.
  5. Consolidate variant metafields you facet or filter on. Copy the value to a product metafield in the Shopify admin so it lands in the product record’s meta.
  6. Export your rules, pins, and synonyms. Record which ones reference variant objectIDs. You re-create those as product objectIDs after the switch.
  7. Select your Product record fields. Adding a field later triggers another full reindex.
  8. Check your largest products. Count the variants on the products with the most of them, and test those in a development store. See Limits to check before you switch.
After you change the mode:
  1. Re-create pins and rules against product objectIDs.
  2. Re-save the facets on each collection.
  3. Review your custom ranking. See Custom ranking.
  4. Check that each sort order returns the expected order. Replicas re-rank after the records land, so give them a few minutes on a large catalog.
  5. Expect degraded Recommend results until the models retrain on the product index.

Verify the switch

Check the search requests your storefront sends, not how the cards look. Product cards read the record they’re given, so they render correctly even when your storefront still queries variant-level attribute names. Faceting and stock filtering fail silently in that state. For each of these, confirm the request and the response rather than the page:
  • Facet panels return values, and the counts change as you refine.
  • The stock policy filter names variants_inventory_count or inventory_available.
  • Price faceting names price_ranges.
  • A search for a SKU returns the product, if you re-pointed your searchable attributes.
Incremental updates only write the attributes that changed. An attribute you cleared from the field checklist can therefore persist on existing records until the next full reindex.
Last modified on August 31, 2026