Overview

Prerequisite

Fields have to be defined in Hawksearch in order for the data sent from Optimizely website to be saved in the Hawksearch indexes. For more details on field setup please refer to HawkSearch Field Setup.

Overall Approach

Passing data from an Optimizely solution (1 or multiple websites) to a Hawksearch engine (or more) has been designed as a 2-part process. Each part uses the Optimizely Scheduled Jobs support provided out of the box by the Optimizely framework:

  1. Full Indexing Job - process which extracts all cms (pages) & commerce (products/variants/bundles/packages) content from your Optimizely Solution, transforms each content into document objects which are then loaded into the Hawksearch Engine(s).
  2. Incremental Indexing Job - a process which uses Optimizely events raised when content is changed (published/deleted) in order to update the Hawksearch engine(s) with the latest version of data.

At the core of both indexing jobs sits a design based on a series of Handlers chained one after the other, each of them solving one particular indexing use case. This represents the first extensibility point of the indexing processes inside the connector. Additional handlers can be developed and chained as needed. For more details on how to do this, check How to Extend Handlers.

The more complex handlers (e.g. Product Indexing) are then built out of a series of Pipes which are usually extracting the content from the Optimizely databases, transforming them into Hawksearch document objects and then loading them into Hawksearch Engine(s) using the Hawksearch V4.0 API APIs. Additional pipes can be developed as needed. For more details on how to do this, check How to Extend Pipes.

Full Indexing Job

Steps (aka Handlers)

  1. Delete Previous (not set as current) Index
  2. Create Index
  3. Categories Indexing
  4. Rebuild Hierarchy
  5. Product Indexing
  6. Variants Indexing
  7. Bundles Indexing
  8. Packages Indexing
  9. CMS Content Indexing
    1. multiple pipelines can be added depending on the Optimizely page type required for indexing
  10. Rebuild All
    1. rebuilds autocomplete, percolator, learning search and related searches.
  11. Set new index as Current Index

Notes

  • steps above are repeated for each engine (language) defined in the Configuring the Connector
  • it is recommended to have this job running on a scheduled bases no more frequently that 1/day

Incremental Indexing Job

Steps (aka Handlers)

  1. Get Current Index
  2. Categories Incremental Indexing
  3. Categories Deletion
  4. Rebuild Hierarchy
  5. Cascading Events Handling
  6. Products Incremental Indexing
  7. Variants Incremental Indexing
  8. Bundles Incremental Indexing
  9. Packages Incremental Indexing
  10. Products / Variants / Bundles / Packages Deletion
  11. CMS Content Incremental Indexing
  12. CMS Content Deletion
  13. Rebuild All

Notes

  • steps above are repeated for each engine (language) defined in the Configuring the Connector
  • this job will not run if Full Indexing Job is already running
  • it is recommended to have this job running on a scheduled bases with a frequency of 1 to 10 minutes

Events

  • on publishing/expiring/deleting content the system will temporarily store information about content in a custom table
  • a full list of events and how they are treated can be found here

Indexing Events

Tables below describe the types of actions/events which will require refreshing the documents stored in Hawksearch engines. Each event is added into a custom table (named hawk.IndexingQueue) when a particular action is taken (e.g. an editor publishes a new version of product, expires a variant, etc). Then, these events are picked up and resolved by the corresponding Handler inside the Incremental Indexing Job.

Commerce entities events & actions

Category Events

Category EventCategoryProductVariantBundlePackage
publish categoryindex hierarchyNANANANA
Name in URL property changes on a category (1)NAindex all products beneath categoryindex all variants beneath categoryindex all bundles beneath categoryindex all packages beneath category
expire categoryindex hierarchy with IsActive falseNANANANA
delete categorydelete hierarchyindex all products beneath categoryindex all variants beneath categoryindex all bundles beneath categoryindex all packages beneath category
move category (change parent category) (1,2)index hierarchyindex all products beneath categoryindex all variants beneath categoryindex all bundles beneath categoryindex all packages beneath category
link category to category (1)index hierarchyindex all products beneath categoryindex all variants beneath categoryindex all bundles beneath categoryindex all packages beneath category

Product Events

Product EventCategoryProductVariantBundlePackage
publish productNAindex productindex all variantsNANA
expire productNAindex productindex all variantsNANA
delete productNAindex productindex all variantsNANA
add image to product (2)NAindex productindex all variantsNANA
delete image from product (2)NAindex productindex all variantsNANA
move product (change parent category)NAindex productindex all variantsNANA
link product to categoryNAindex productindex all variantsNANA

Variant Events

Variant EventCategoryProductVariantBundlePackage
publish variantNAindex productindex variantindex bundleindex package
expire variantNAindex productindex variantindex bundleindex package
delete variantNAindex productindex variantindex bundleindex package
add image to variant (2)NAindex productindex variantindex bundleindex package
delete image from variant (2)NAindex productindex variantindex bundleindex package
move variant (change parent category)NAindex productindex variantindex bundleindex package
link variant to categoryNAindex productindex variantindex bundleindex package
link variant to productNAindex productindex variantNANA
link variant to bundleNAindex productindex variantNANA
link variant to kit/packageNANAindex variantNAindex package

Bundle Events

Bundle EventCategoryProductVariantBundlePackage
publish bundleNANAindex all variantsindex bundleNA
expire bundleNANAindex all variantsindex bundleNA
delete bundleNANAindex all variantsdelete bundleNA
add image to bundle (2)NANAindex all variantsindex bundleNA
delete image from bundle (2)NANAindex all variantsindex bundleNA
move bundle (change parent category)NANAindex all variantsindex bundleNA
link bundle to categoryNANAindex all variantsindex bundleNA

Package Events

Package EventCategoryProductVariantBundlePackage
publish packageNANAindex all variantsNAindex package
expire packageNANAindex all variantsNAindex package
delete packageNANAindex all variantsNAindex package
add image to package (2)NANAindex all variantsNAindex package
delete image from package (2)NANAindex all variantsNAindex package
move package (change parent category)link package to categoryNANAindex all variantsNAindex package

Cms Entities Events and Actions

Optimizely EventOptimizely-Hawksearch Connector Indexing Actions
publish pageindex page and all subpages
Name in URL' property changes on a pageindex page and all subpages
expire pageindex page and all subpages
delete page (move to wastebasket) (2)index page and all subpages
move page (2)index page and all subpages
restore page (restore from wastebasket) (2)index page and all subpages

(1) introduces a cascading event and a new handler

(2) solved via Optimizely Content API - MovedContent event

Index Setup

Overview

There are 3 main indexing strategies that the connector supports:

To set up indexing inside your project, you should use the Initialization Modules capability (by implementing the IConfigurableModule interface) provided by the Optimizely platform and make your module dependent on the BasicHawksearchInitialization module which is part of the installed connector.

Configure Indexing Services

In the ConfigureContainer method, register services needed to index different types of content. Available options are:

            container.AddHawksearchCategoryIndexing<YOUR CLASS EXTENDING NodeContent>();            
            container.AddHawksearchProductIndexing<YOUR CLASS EXTENDING ProductContent>();
            container.AddHawksearchVariantIndexing<YOUR CLASS EXTENDING VariationContent>();
            container.AddHawksearchBundleIndexing<YOUR CLASS EXTENDING BundleContent>();
            container.AddHawksearchPackageIndexing<YOUR CLASS EXTENDING PackageContent>();
            container.AddHawksearchPageIndexing<YOUR CLASS EXTENDING PageData>()

AddHawksearchPageIndexing extension method can be used multiple times with different generics in order to have multiple cms page types indexed in Hawksearch.

If you don’t intend to index some of the cms or commerce entity type, there’s no need to register those services (e.g. if you don’t have bundles or packages in your project, you should not call the AddHawksearchBundleIndexing or the AddHawksearchPackageIndexing methods).

Initialize Indexing Chains

In the Initialize method, register the 2 chains of Handlers needed for the Full & Incremental Indexing jobs as follows:

        public void Initialize(InitializationEngine context)
        {
            Container.Service.Configure(config =>
            {
                config.For<IFullIndexingChain>().Add(() => FullIndexingChain(Container.Service))
                    .Transient();

                config.For<IIncrementalIndexingChain>().Add(() => IncrementalIndexingChain(Container.Service))
                    .Transient();
            });
        }

        private IndexingChain FullIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<DeletePreviousIndexHandler>()
                .AddHandler<CreateIndexHandler>()
                .AddHandler<CategoriesIndexingHandler<YOUR CLASS EXTENDING NodeContent>>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<ProductsIndexingHandler<YOUR CLASS EXTENDING ProductContent>>()
                .AddHandler<VariantsIndexingHandler<YOUR CLASS EXTENDING VariationContent>>()
                .AddHandler<BundlesIndexingHandler<YOUR CLASS EXTENDING BundleContent>>()
                .AddHandler<PackagesIndexingHandler<YOUR CLASS EXTENDING PackageContent>>()
                .AddHandler<PagesIndexingHandler<YOUR CLASS EXTENDING PageData>>()
                ... other PagesIndexingHandler registrations for different types of cms pages
                .AddHandler<RebuildIndexHandler>()
                .AddHandler<SetCurrentIndexHandler>();
        }

        private IndexingChain IncrementalIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<GetCurrentIndexHandler>()
                .AddHandler<CategoriesIncrementalIndexingHandler<YOUR CLASS EXTENDING NodeContent>>()
                .AddHandler<CategoriesDeletionHandler>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<CategoriesCascadeIndexingHandler>()
                .AddHandler<ProductsIncrementalIndexingHandler<YOUR CLASS EXTENDING ProductContent>>()
                .AddHandler<VariantsIncrementalIndexingHandler<YOUR CLASS EXTENDING VariationContent>>()
                .AddHandler<BundlesIncrementalIndexingHandler<YOUR CLASS EXTENDING BundleContent>>()
                .AddHandler<PackagesIncrementalIndexingHandler<YOUR CLASS EXTENDING PackageContent>>()
                .AddHandler<PagesIncrementalIndexingHandler<YOUR CLASS EXTENDING PageData>>()
                ... other PagesIncrementalIndexingHandler registrations for different types of cms pages
                .AddHandler<EntriesDeletionHandler>()
                .AddHandler<PagesDeletionHandler>()
                .AddHandler<RebuildIndexHandler>();
        }

If you don’t intend to index some of the cms or commerce entity type, you should not add that particular handlers to your indexing chains (e.g. if you don’t have bundles, you will skip adding the BundlesIndexingHandler and the BundlesIncrementalIndexingHandler to the indexing chains).

If you don’t intend to index cms pages, PagesIndexingHandler,PagesIncrementalIndexingHandler,PagesDeletionHandler should not be added to the indexing chains.

[IncludeInHawksearch] Attribute

[IncludeInHawksearch] is an attribute used to decorate properties which we want to index.

Examples:

  [IncludeInHawksearch]
    [BackingType(typeof(PropertyString))]
    [Display(Name = "Brand", GroupName = SystemTabNames.Content, Order = 15)]
    public virtual string Brand { get; set; }
  [IncludeInHawksearch]  
    [Display(Name = "On sale", GroupName = SystemTabNames.Content, Order = 50)]
    public virtual bool OnSale { get; set; }

This attribute is common for all 3 indexing strategies. For the Variant Attributes Roll-up Strategy, the properties of the variant decorated with this attribute will be rolled-up inside the parent product.

Out of the box, only properties with the following types can be decorated with [IncludeInHawksearch] attribute:

.NET specific

string
int
double
decimal
bool
DateTime

Optimizely specific

ContentReference
XhtmlString

Other property types will throw a NotSupportedException.

To support more types, please refer to this.

Variant Attributes Roll-up Strategy

This strategy relies on the fact that no variants will be indexed as documents inside Hawksearch. It is the default strategy of the conntector.
Variant properties, decorated with [IncludeInHawksearch] attribute, will be rolled-up inside the parent product. The product document indexed will contain information coming from both the product itself and it’s variants.

To use this indexing strategy, set variantindexingstrategy to “VariantAttributesRollUp“ in Web.config, or do not include variantindexingstrategy at all.

 <hawksearch ...
              variantindexingstrategy="VariantAttributesRollUp"
 </hawksearch>

Example

[IncludeInHawksearch]
public virtual string Color { get; set; }

The “Color” property will be rolled-up from the variant to the product. The “Color” field on a product in Hawksearch will contain a list of available colors, each one of them extracted from a variant.

Considerations

  • ariant Indexing is unavailable under this strategy. AddHawksearchVariantIndexing will throw a NotSupportedException if attempted to be used inside chain initialization. Also, VariantsIndexingHandler and VariantsIncrementalIndexingHandler pipes will be rendered useless and throw exceptions.
  • Price and inventory indexing options from Web.config will have no effect, because Variant Indexing is unavailable.

Indexing Setup Example: Optimizely Foundation

A full version of the HawksearchInitialization class used to set up indexing on the Optimizely Foundation solution can be found below:

using EPiServer.Commerce.Catalog.ContentTypes;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using Foundation.Features.CatalogContent.Bundle;
using Foundation.Features.CatalogContent.Package;
using Foundation.Features.CatalogContent.Product;
using Foundation.Features.CatalogContent.Variation;
using Foundation.Features.Hawksearch.Indexing.Pipes;
using Foundation.Features.Hawksearch.Indexing.Pipes.Pages;
using Foundation.Features.Locations.LocationItemPage;
using Optimizely.Hawksearch.Connector.Contracts;
using Optimizely.Hawksearch.Connector.Core;
using Optimizely.Hawksearch.Connector.Handlers;
using Optimizely.Hawksearch.Connector.Handlers.Category;
using Optimizely.Hawksearch.Connector.Handlers.Page;
using Optimizely.Hawksearch.Connector.Handlers.Product;
using Optimizely.Hawksearch.Connector.Initialization;
using StructureMap;

namespace Foundation.Features.Hawksearch.Indexing
{
    [InitializableModule]
    [ModuleDependency(typeof(BasicHawksearchInitialization))]
    public class HawksearchInitialization : IConfigurableModule
    {
        internal Injected<IContainer> Container;

        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            var container = context.StructureMap();
            container.AddHawksearchCategoryIndexing<NodeContent>();

            container.AddHawksearchProductIndexing<GenericProduct, GenericVariant>()
                .ExtendProductIndexing<GenericProduct, EntriesImagePipe<GenericProduct>>()
                .ExtendProductIndexing<GenericProduct, GenericProductAttributesPipe>();

            container.AddHawksearchBundleIndexing<GenericBundle, GenericVariant>();
            container.AddHawksearchPackageIndexing<GenericPackage, GenericVariant>();

            container.AddHawksearchPageIndexing<LocationItemPage>()
                .ExtendPageIndexing<LocationItemPage, FoundationPageAttributesPipe<LocationItemPage>>()
                .ExtendPageIndexing<LocationItemPage, LocationItemPageAttributesPipe>();
            container.AddHawksearchPageIndexing<StandardPage.StandardPage>()
                .ExtendPageIndexing<StandardPage.StandardPage, FoundationPageAttributesPipe<StandardPage.StandardPage>>()
                .ExtendPageIndexing<StandardPage.StandardPage, StandardPageAttributesPipe>();
        }

        public void Initialize(InitializationEngine context)
        {
            Container.Service.Configure(config =>
            {
                config.For<IFullIndexingChain>().Add(() => FullIndexingChain(Container.Service))
                    .Transient();

                config.For<IIncrementalIndexingChain>().Add(() => IncrementalIndexingChain(Container.Service))
                    .Transient();
            });
        }

        private IndexingChain FullIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<DeletePreviousIndexHandler>()
                .AddHandler<CreateIndexHandler>()
                .AddHandler<CategoriesIndexingHandler<NodeContent>>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<ProductsIndexingHandler<GenericProduct>>()
                .AddHandler<BundlesIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<RebuildIndexHandler>()
                .AddHandler<SetCurrentIndexHandler>();
        }

        private IndexingChain IncrementalIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<GetCurrentIndexHandler>()
                .AddHandler<CategoriesIncrementalIndexingHandler<NodeContent>>()
                .AddHandler<CategoriesDeletionHandler>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<CategoriesCascadeIndexingHandler>()
                .AddHandler<ProductsIncrementalIndexingHandler<GenericProduct>>()
                .AddHandler<BundlesIncrementalIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIncrementalIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIncrementalIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIncrementalIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<EntriesDeletionHandler>()
                .AddHandler<PagesDeletionHandler>()
                .AddHandler<RebuildIndexHandler>();
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }
}

Variant as Separate Document Strategy

This strategy relies on the fact that products and variants are indexed as separate documents inside Hawksearch. Their relationships are not expressed - it is not known which variants belong to which products in a parent-child fashion. The properties decorated with [IncludeInHawksearch] attribute will be the ones that are indexed for each variant.

To use this indexing strategy, set variantindexingstrategy to “SeparateDocuments“ in Web.config.

 <hawksearch ...
              variantindexingstrategy="SeparateDocuments"
 </hawksearch>

This should be the go-to strategy for variant-only catalogs.

For this strategy, it is important to add the Variant handlers and Variant Indexing extension method(s) in the initialization module:

container.AddHawksearchVariantIndexing<GenericVariant>()
    .ExtendVariantIndexing<GenericVariant, EntriesImagePipe<GenericVariant>>()
    .ExtendVariantIndexing<GenericVariant, GenericVariantAttributesPipe>()
    .ExtendVariantIndexing<GenericVariant, GenericVariantShippingPipe>();
private IndexingChain FullIndexingChain(IContainer container)
{
    return new IndexingChain(container)
        .AddHandler<DeletePreviousIndexHandler>()
        ...
        .AddHandler<VariantsIndexingHandler<GenericVariant>>()
        ...
       ;
}
private IncrementalIndexingChain FullIndexingChain(IContainer container)
{
    return new IndexingChain(container)
        .AddHandler<DeletePreviousIndexHandler>()
        ...
        .AddHandler<VariantsIncrementalIndexingHandler<GenericVariant>>()
        ...
       ;
}

Indexing Setup Example: Optimizely Foundation

A full version of the HawksearchInitialization class used to set up indexing on the Optimizely Foundation solution can be found below:

using EPiServer.Commerce.Catalog.ContentTypes;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using Foundation.Features.CatalogContent.Bundle;
using Foundation.Features.CatalogContent.Package;
using Foundation.Features.CatalogContent.Product;
using Foundation.Features.CatalogContent.Variation;
using Foundation.Features.Hawksearch.Indexing.Pipes;
using Foundation.Features.Hawksearch.Indexing.Pipes.Pages;
using Foundation.Features.Locations.LocationItemPage;
using Optimizely.Hawksearch.Connector.Contracts;
using Optimizely.Hawksearch.Connector.Core;
using Optimizely.Hawksearch.Connector.Handlers;
using Optimizely.Hawksearch.Connector.Handlers.Category;
using Optimizely.Hawksearch.Connector.Handlers.Page;
using Optimizely.Hawksearch.Connector.Handlers.Product;
using Optimizely.Hawksearch.Connector.Initialization;
using StructureMap;

namespace Foundation.Features.Hawksearch.Indexing
{
    [InitializableModule]
    [ModuleDependency(typeof(BasicHawksearchInitialization))]
    public class HawksearchInitialization : IConfigurableModule
    {
        internal Injected<IContainer> Container;

        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            var container = context.StructureMap();
            container.AddHawksearchCategoryIndexing<NodeContent>();
            
            container.AddHawksearchProductIndexing<GenericProduct, GenericVariant>()
                .ExtendProductIndexing<GenericProduct, EntriesImagePipe<GenericProduct>>()
                .ExtendProductIndexing<GenericProduct, GenericProductAttributesPipe>();

            container.AddHawksearchVariantIndexing<GenericVariant>()
                .ExtendVariantIndexing<GenericVariant, EntriesImagePipe<GenericVariant>>()
                .ExtendVariantIndexing<GenericVariant, GenericVariantAttributesPipe>()
                .ExtendVariantIndexing<GenericVariant, GenericVariantShippingPipe>();

            container.AddHawksearchBundleIndexing<GenericBundle, GenericVariant>();
            container.AddHawksearchPackageIndexing<GenericPackage, GenericVariant>();

            container.AddHawksearchPageIndexing<LocationItemPage>()
                .ExtendPageIndexing<LocationItemPage, FoundationPageAttributesPipe<LocationItemPage>>()
                .ExtendPageIndexing<LocationItemPage, LocationItemPageAttributesPipe>();
            container.AddHawksearchPageIndexing<StandardPage.StandardPage>()
                .ExtendPageIndexing<StandardPage.StandardPage, FoundationPageAttributesPipe<StandardPage.StandardPage>>()
                .ExtendPageIndexing<StandardPage.StandardPage, StandardPageAttributesPipe>();
        }

        public void Initialize(InitializationEngine context)
        {
            Container.Service.Configure(config =>
            {
                config.For<IFullIndexingChain>().Add(() => FullIndexingChain(Container.Service))
                    .Transient();

                config.For<IIncrementalIndexingChain>().Add(() => IncrementalIndexingChain(Container.Service))
                    .Transient();
            });
        }

        private IndexingChain FullIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<DeletePreviousIndexHandler>()
                .AddHandler<CreateIndexHandler>()
                .AddHandler<CategoriesIndexingHandler<NodeContent>>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<ProductsIndexingHandler<GenericProduct>>()
                .AddHandler<VariantsIndexingHandler<GenericVariant>>()
                .AddHandler<BundlesIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<RebuildIndexHandler>()
                .AddHandler<SetCurrentIndexHandler>();
        }

        private IndexingChain IncrementalIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<GetCurrentIndexHandler>()
                .AddHandler<CategoriesIncrementalIndexingHandler<NodeContent>>()
                .AddHandler<CategoriesDeletionHandler>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<CategoriesCascadeIndexingHandler>()
                .AddHandler<ProductsIncrementalIndexingHandler<GenericProduct>>()
                .AddHandler<VariantsIncrementalIndexingHandler<GenericVariant>>()
                .AddHandler<BundlesIncrementalIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIncrementalIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIncrementalIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIncrementalIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<EntriesDeletionHandler>()
                .AddHandler<PagesDeletionHandler>()
                .AddHandler<RebuildIndexHandler>();
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }
}

Variant as Child of Parent Strategy

This strategy relies on the fact that variants will be indexed as children for their parent product/bundle/package using the hawk_child_attributes JSON property.
The properties decorated with [IncludeInHawksearch] attribute will be the ones that are indexed for each variant residing below hawk_child_attributes. The product itself is, of course, indexed.

To use this indexing strategy, set variantindexingstrategy to “VariantAsChildOfParent“ in Web.config.

 <hawksearch ...
              variantindexingstrategy="VariantAsChildOfParent"
 </hawksearch>

Example

[IncludeInHawksearch]
public virtual string Color { get; set; }

The “Color” property will be included in the variant indexed under hawk_child_attributes.

Convention

Every field that will be equivalent to a variant property shall be prefixed by “Child_” in Hawksearch. Also, it will have the “Is Child Field?” flag set to “ON

Example:

  • For ->
[IncludeInHawksearch]
public virtual string Color { get; set; }

we have Child_Color

  • For ->
[IncludeInHawksearch]c
public virtual string Size { get; set; }

we have Child_Size

Default Fields

These fields are always included in indexing (can be found here)

  • Child_DisplayName
  • Child_Code
  • Child_Url

Considerations

  • Variant Indexing is unavailable under this strategy. AddHawksearchVariantIndexing will throw a NotSupportedException if attempted to be used inside chain initialization. Also, VariantsIndexingHandler and VariantsIncrementalIndexingHandler pipes will be rendered useless and throw exceptions.
  • Price and inventory indexing options from Web.config will have no effect, because Variant Indexing is unavailable.

Indexing Setup Example: Optimizely Foundation

A full version of the HawksearchInitialization class used to set up indexing on the Optimizely Foundation solution can be found below:

using EPiServer.Commerce.Catalog.ContentTypes;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
using Foundation.Features.CatalogContent.Bundle;
using Foundation.Features.CatalogContent.Package;
using Foundation.Features.CatalogContent.Product;
using Foundation.Features.CatalogContent.Variation;
using Foundation.Features.Hawksearch.Indexing.Pipes;
using Foundation.Features.Hawksearch.Indexing.Pipes.Pages;
using Foundation.Features.Locations.LocationItemPage;
using Optimizely.Hawksearch.Connector.Contracts;
using Optimizely.Hawksearch.Connector.Core;
using Optimizely.Hawksearch.Connector.Handlers;
using Optimizely.Hawksearch.Connector.Handlers.Category;
using Optimizely.Hawksearch.Connector.Handlers.Page;
using Optimizely.Hawksearch.Connector.Handlers.Product;
using Optimizely.Hawksearch.Connector.Initialization;
using StructureMap;

namespace Foundation.Features.Hawksearch.Indexing
{
    [InitializableModule]
    [ModuleDependency(typeof(BasicHawksearchInitialization))]
    public class HawksearchInitialization : IConfigurableModule
    {
        internal Injected<IContainer> Container;

        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            var container = context.StructureMap();
            container.AddHawksearchCategoryIndexing<NodeContent>();

            container.AddHawksearchProductIndexing<GenericProduct, GenericVariant>()
                .ExtendProductIndexing<GenericProduct, EntriesImagePipe<GenericProduct>>()
                .ExtendProductIndexing<GenericProduct, GenericProductAttributesPipe>();

            container.AddHawksearchBundleIndexing<GenericBundle, GenericVariant>();
            container.AddHawksearchPackageIndexing<GenericPackage, GenericVariant>();

            container.AddHawksearchPageIndexing<LocationItemPage>()
                .ExtendPageIndexing<LocationItemPage, FoundationPageAttributesPipe<LocationItemPage>>()
                .ExtendPageIndexing<LocationItemPage, LocationItemPageAttributesPipe>();
            container.AddHawksearchPageIndexing<StandardPage.StandardPage>()
                .ExtendPageIndexing<StandardPage.StandardPage, FoundationPageAttributesPipe<StandardPage.StandardPage>>()
                .ExtendPageIndexing<StandardPage.StandardPage, StandardPageAttributesPipe>();
        }

        public void Initialize(InitializationEngine context)
        {
            Container.Service.Configure(config =>
            {
                config.For<IFullIndexingChain>().Add(() => FullIndexingChain(Container.Service))
                    .Transient();

                config.For<IIncrementalIndexingChain>().Add(() => IncrementalIndexingChain(Container.Service))
                    .Transient();
            });
        }

        private IndexingChain FullIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<DeletePreviousIndexHandler>()
                .AddHandler<CreateIndexHandler>()
                .AddHandler<CategoriesIndexingHandler<NodeContent>>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<ProductsIndexingHandler<GenericProduct>>()
                .AddHandler<BundlesIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<RebuildIndexHandler>()
                .AddHandler<SetCurrentIndexHandler>();
        }

        private IndexingChain IncrementalIndexingChain(IContainer container)
        {
            return new IndexingChain(container)
                .AddHandler<GetCurrentIndexHandler>()
                .AddHandler<CategoriesIncrementalIndexingHandler<NodeContent>>()
                .AddHandler<CategoriesDeletionHandler>()
                .AddHandler<HierarchyRebuildHandler>()
                .AddHandler<CategoriesCascadeIndexingHandler>()
                .AddHandler<ProductsIncrementalIndexingHandler<GenericProduct>>()
                .AddHandler<BundlesIncrementalIndexingHandler<GenericBundle>>()
                .AddHandler<PackagesIncrementalIndexingHandler<GenericPackage>>()
                .AddHandler<PagesIncrementalIndexingHandler<LocationItemPage>>()
                .AddHandler<PagesIncrementalIndexingHandler<StandardPage.StandardPage>>()
                .AddHandler<EntriesDeletionHandler>()
                .AddHandler<PagesDeletionHandler>()
                .AddHandler<RebuildIndexHandler>();
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }
}