---
title: Index variants in a product document
slug: commerce-connect-14/docs/indexing-variants-in-a-product-document
docTags: 
createdAt: 2026-07-23T10:25:32.641Z
---

## Index the entire variant inside the product

By default, variants are indexed as `ContentReferences` inside the product document sent to the Optimizely Search & Navigation index. This allows all variant references for a specific product from the index and gets the product for a specific variant.

Depending on how your site works, it might be better to index the entire variant inside of the product, as shown in the following examples.

### Override CatalogContentClientConventions

**Commerce Connect version 14**

Override the `CatalogContentClientConventions` class, and register it in the `ConfigureServices` method of startup.cs.

```text
//Startup class
    public void ConfigureServices(IServiceCollection services)
    {
            services.AddTransient<CatalogContentClientConventions, SiteCatalogContentClientConventions>();
    }
```

**Commerce Connect versions 10-13**

Override the `CatalogContentClientConventions` class, and register it in an initialization module to override the default conventions.

```csharp
[InitializableModule]
    [ModuleDependency(typeof(FindCommerceInitializationModule))]
    public class InitializationModule : IConfigurableModule
      {
        public void Initialize(InitializationEngine context)
          {
          }
        public void Uninitialize(InitializationEngine context)
          {
          }
        public void ConfigureContainer(ServiceConfigurationContext context)
          {
            context.Services.AddTransient<CatalogContentClientConventions, SiteCatalogContentClientConventions>();
          }
      }
    public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
      }
```

### Exclude variant reference indexing for product content

To exclude variant references, override the `ApplyProductContentConventions`, and exclude the field using conventions.

```csharp
public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
        protected override void ApplyProductContentConventions(TypeConventionBuilder<ProductContent> conventionBuilder)
          {
            base.ApplyProductContentConventions(conventionBuilder);
            conventionBuilder
              .ExcludeField(x => x.Variations());
          }
      }
```

### Include related variant content items in product content

Create an extension method for product content to index variants for it.

```csharp
public static class ProductContentExtensions
      {
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent)
          {
            return VariationContents(productContent, ServiceLocator.Current.GetInstance<IContentLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent, IContentLoader contentLoader, IRelationRepository relationRepository)
          {
            return contentLoader.GetItems(productContent.GetVariants(relationRepository), productContent.Language).OfType<VariationContent>();
          }
      }
    
    public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
        protected override void ApplyProductContentConventions(TypeConventionBuilder<ProductContent> conventionBuilder)
          {
            base.ApplyProductContentConventions(conventionBuilder);
            conventionBuilder
              .ExcludeField(x => x.Variations())
              .IncludeField(x => x.VariationContents());
          }
      }
```

### Add default price and prices in product content

The following example shows how to

- index the highest default price for product variants
- index prices from variants in the `Prices` extension method

```csharp
public static class ProductContentExtensions
      {
        public static Price DefaultPrice(this ProductContent productContent)
          {
            return DefaultPrice(productContent, ServiceLocator.Current.GetInstance<ReadOnlyPricingLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static Price DefaultPrice(this ProductContent productContent, ReadOnlyPricingLoader pricingLoader, IRelationRepository relationRepository)
          {
            var maxPrice = new Price();
            var variationLinks = productContent.GetVariants(relationRepository);
            foreach (var variationLink in variationLinks)
              {
                var defaultPrice = pricingLoader.GetDefaultPrice(variationLink);
                if (defaultPrice.UnitPrice.Amount > maxPrice.UnitPrice.Amount)
                  {
                    maxPrice = defaultPrice;
                  }
              }
            return maxPrice;
          }
    
        public static IEnumerable<Price> Prices(this ProductContent productContent)
          {
            return Prices(productContent, ServiceLocator.Current.GetInstance<ReadOnlyPricingLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static IEnumerable<Price> Prices(this ProductContent productContent, ReadOnlyPricingLoader pricingLoader, IRelationRepository relationRepository)
          {
            var variationLinks = productContent.GetVariants(relationRepository);
            return variationLinks.SelectMany(variationLink => pricingLoader.GetPrices(variationLink, null, Enumerable.Empty<CustomerPricing>()));
          }
    
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent)
          {
            return VariationContents(productContent, ServiceLocator.Current.GetInstance<IContentLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent, IContentLoader contentLoader, IRelationRepository relationRepository)
          {
            return contentLoader.GetItems(productContent.GetVariants(relationRepository), productContent.Language).OfType<VariationContent>();
          }
      }
    
    public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
        protected override void ApplyProductContentConventions(TypeConventionBuilder<ProductContent> conventionBuilder)
          {
            base.ApplyProductContentConventions(conventionBuilder);
            conventionBuilder
              .ExcludeField(x => x.Variations())
              .IncludeField(x => x.VariationContents())
              .IncludeField(x => x.DefaultPrice())
              .IncludeField(x => x.Prices());
          }
      }
```

### Add inventory in product content

The following example shows how to index the inventories from variants.

```csharp
public static class ProductContentExtensions
      {
        public static IEnumerable Inventories(this ProductContent productContent)
          {
            return Inventories(
              productContent,
              ServiceLocator.Current.GetInstance<IContentLoader>(),
              ServiceLocator.Current.GetInstance<InventoryLoader>(),
              ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static IEnumerable<Inventory> Inventories(this ProductContent productContent, IContentLoader contentLoader, InventoryLoader inventoryLoader, IRelationRepository relationRepository)
          {
            var variations = contentLoader.GetItems(productContent.GetVariants(relationRepository), productContent.Language).OfType<VariationContent>();
            return variations.SelectMany(x => x.GetStockPlacement(inventoryLoader));
          }
    
        public static Price DefaultPrice(this ProductContent productContent)
          {
            return DefaultPrice(productContent, ServiceLocator.Current.GetInstance<ReadOnlyPricingLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static Price DefaultPrice(this ProductContent productContent, ReadOnlyPricingLoader pricingLoader, IRelationRepository relationRepository)
          {
            var maxPrice = new Price();
            var variationLinks = productContent.GetVariants(relationRepository);
            foreach (var variationLink in variationLinks)
              {
                var defaultPrice = pricingLoader.GetDefaultPrice(variationLink);
                if (defaultPrice.UnitPrice.Amount > maxPrice.UnitPrice.Amount)
                  {
                    maxPrice = defaultPrice;
                  }
              }
            return maxPrice;
          }
    
        public static IEnumerable<Price> Prices(this ProductContent productContent)
          {
            return Prices(productContent, ServiceLocator.Current.GetInstance<ReadOnlyPricingLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static IEnumerable<Price> Prices(this ProductContent productContent, ReadOnlyPricingLoader pricingLoader, IRelationRepository relationRepository)
          {
            var variationLinks = productContent.GetVariants(relationRepository);
            return variationLinks.SelectMany(variationLink => pricingLoader.GetPrices(variationLink, null, Enumerable.Empty<CustomerPricing>()));
          }
    
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent)
          {
            return VariationContents(productContent, ServiceLocator.Current.GetInstance<IContentLoader>(), ServiceLocator.Current.GetInstance<IRelationRepository>());
          }
    
        public static IEnumerable<VariationContent> VariationContents(this ProductContent productContent, IContentLoader contentLoader, IRelationRepository relationRepository)
          {
            return contentLoader.GetItems(productContent.GetVariants(relationRepository), productContent.Language).OfType<VariationContent>();
          }
      }
    
    public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
        protected override void ApplyProductContentConventions(TypeConventionBuilder<ProductContent> conventionBuilder)
          {
            base.ApplyProductContentConventions(conventionBuilder);
            conventionBuilder
              .ExcludeField(x => x.Variations())
              .IncludeField(x => x.VariationContents())
              .IncludeField(x => x.DefaultPrice())
              .IncludeField(x => x.Prices())
              .IncludeField(x => x.Inventories());
          }
      }
```

### Exclude indexing of variant content

Exclude the variant content from being indexed by calling `ShouldIndex` for the type, using the conventions API.

```csharp
public class SiteCatalogContentClientConventions : CatalogContentClientConventions
      {
        protected override void ApplyProductContentConventions(ClientConventions.TypeConventionBuilder<ProductContent> conventionBuilder)
          {
            base.ApplyProductContentConventions(conventionBuilder);
            conventionBuilder
              .ExcludeField(x => x.Variations())
              .IncludeField(x => x.VariationContents())
              .IncludeField(x => x.DefaultPrice())
              .IncludeField(x => x.Prices())
              .IncludeField(x => x.Inventories());
          }
    
       public override void ApplyConventions(IClientConventions clientConventions)
          {
            base.ApplyConventions(clientConventions);
            // Uncomment line below if we don't index VariationContent
            // ContentIndexer.Instance.Conventions.ForInstancesOf<VariationContent>().ShouldIndex(x => false);
            SearchClient.Instance.Conventions.NestedConventions.ForInstancesOf<ProductContent>().Add(x => x.VariationContents());
          }
      }
```

### Listen for price and inventory changes

The `CatalogContentEventListener` class listens for price changes for classes that implement `IPricing`, and inventories for classes that implement `IStockPlacement`. `VariationContent` implements both of them, but `ProductContent` does not implement them. Therefore, override the default implementation and make some changes.

```csharp
[InitializableModule]
    [ModuleDependency(typeof(FindCommerceInitializationModule))]
    public class InitializationModule : IConfigurableModule
      {
        public void Initialize(InitializationEngine context)
          {
            context.Locate.Advanced.GetInstance<PriceIndexing>().IsIndexingIIndexedPrices = true;
          }
        public void Uninitialize(InitializationEngine context)
          {
          }
        public void ConfigureContainer(ServiceConfigurationContext context)
          {
            context.Services.AddSingleton<CatalogContentClientConventions, 
              SiteCatalogContentClientConventions>();
          }
      }
    
    public class SiteCatalogContentEventListener : CatalogContentEventListener
      {
        private ReferenceConverter _referenceConverter;
        private readonly IContentRepository _contentRepository;
        private readonly IRelationRepository _relationRepository;
    
        public SiteCatalogContentEventListener(
          ReferenceConverter referenceConverter,
          IContentRepository contentRepository,
          IClient client,
          CatalogEventIndexer indexer,
          CatalogContentClientConventions catalogContentClientConventions,
          PriceIndexing priceIndexing, 
          IRelationRepository relationRepository)
          : base(
                  referenceConverter,
                  contentRepository,
                  client,
                  indexer, 
                  catalogContentClientConventions,
                  priceIndexing)
          {
            _referenceConverter = referenceConverter;
            _contentRepository = contentRepository;
            _relationRepository = relationRepository;
          }
        protected override void IndexContentsIfNeeded(IEnumerable<ContentRefenrence> contentLinks, IDictionary<Type, bool> cachedReindexContentOnEventForType, Func<bool> isReindexingContentOnUpdates)
          {
            // Update parent contents
            var contents = _contentRepository.GetItems(contentLinks, CultureInfo.InvariantCulture).ToList();
            var parentContentLinks = new List<ContentReference>();
            foreach (var parents in contents.OfType().Select(content => _contentRepository.GetItems(content.GetParentProducts(_relationRepository), CultureInfo.InvariantCulture).Select(c => c.ContentLink).ToList()))
              {
                parentContentLinks.AddRange(parents);
              }
              // If index variants still needed, keep the line below
              // IndexContentsIfNeeded(contentLinks, GetIndexContentAction(), cachedReindexContentOnEventForType, isReindexingContentOnUpdates);
    
              IndexContentsIfNeeded(parentContentLinks, GetIndexContentAction(), cachedReindexContentOnEventForType, isReindexingContentOnUpdates);
          }
      }
```
