<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Jonas Bergqvist</title> <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Optimizely Graph Cache: The Power of &quot;Item&quot;</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2025/2/optimizely-graph-cache-the-power-of-item/</link>            <description>&lt;p&gt;In the world of content delivery, speed is king. A high cache hit ratio is the secret weapon for delivering snappy performance and happy users. But how do you achieve that? One powerful trick lies in understanding the difference between &quot;items&quot; and &quot;item&quot; in your GraphQL queries.&lt;/p&gt;
&lt;h2&gt;The Cache Challenge: Lists vs. Specifics&lt;/h2&gt;
&lt;p&gt;GraphQL&#39;s flexibility often leads to queries fetching lists (&quot;items&quot;). While necessary for many scenarios, this can hinder caching. Why? Because even a list with one item requires the cache to consider potential changes to &lt;em&gt;any&lt;/em&gt; content, leading to frequent cache invalidations.&lt;/p&gt;
&lt;h2&gt;The &quot;Item&quot; Advantage: Precision Caching&lt;/h2&gt;
&lt;p&gt;Enter the &quot;item&quot; query. When you request a &lt;em&gt;single&lt;/em&gt; item by its unique identifier, you unlock granular, efficient caching.&lt;/p&gt;
&lt;p&gt;How to Leverage &quot;Item&quot; for Optimal Caching:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Improve Cache Hit Ratio:&lt;/strong&gt; Swap &quot;items&quot; for &quot;item&quot; when fetching a single entity. It&#39;s a small change with big results.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unique Identifier-Driven Queries:&lt;/strong&gt; Only use &quot;item&quot; when targeting a specific item via its unique identifier (relative paths, URLs, codes, etc.).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/link/c0dc3b485a35446b96d0db661dc7baf9.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cache hit example for query with &quot;item&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e0195cec5b4a4874b198420033e67f1f.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cache hit example for query with &quot;items&quot;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Practical Example&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;Instead of&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/p&gt;
&lt;div class=&quot;code-block ng-tns-c2164703490-17 ng-trigger ng-trigger-codeBlockRevealAnimation&quot;&gt;&lt;!----&gt;
&lt;div class=&quot;formatted-code-block-internal-container ng-tns-c2164703490-17&quot;&gt;
&lt;div class=&quot;animated-opacity ng-tns-c2164703490-17&quot;&gt;
&lt;pre class=&quot;ng-tns-c2164703490-17&quot;&gt;&lt;code class=&quot;code-container formatted ng-tns-c2164703490-17&quot;&gt;query GetItem($relativePath: String) {
  Content(&lt;strong&gt;limit:1&amp;nbsp;&lt;/strong&gt;where: { RelativePath: { eq: $relativePath } }) {
    &lt;strong&gt;items &lt;/strong&gt;{ Name }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/p&gt;
&lt;div class=&quot;code-block ng-tns-c2164703490-18 ng-trigger ng-trigger-codeBlockRevealAnimation&quot;&gt;&lt;!----&gt;
&lt;div class=&quot;formatted-code-block-internal-container ng-tns-c2164703490-18&quot;&gt;
&lt;div class=&quot;animated-opacity ng-tns-c2164703490-18&quot;&gt;
&lt;pre class=&quot;ng-tns-c2164703490-18&quot;&gt;&lt;code class=&quot;code-container formatted ng-tns-c2164703490-18&quot;&gt;query GetItem($relativePath: String) {
  Content(where: { RelativePath: { eq: $relativePath } }) {
    &lt;strong&gt;item &lt;/strong&gt;{ Name }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Using &quot;item&quot; for single entity retrieval based on unique identifiers dramatically improves cache hit ratios, enhancing performance and user experience. It&#39;s a simple, powerful technique to unlock the full potential of your GraphQL cache.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2025/2/optimizely-graph-cache-the-power-of-item/</guid>            <pubDate>Fri, 28 Feb 2025 14:56:08 GMT</pubDate>           <category>Blog post</category></item><item> <title>Boosting Graph Query Performance with Cached Templates</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2025/2/boosting-graph-query-performance-with-stored-templates/</link>            <description>&lt;p&gt;To optimize query performance, Cached Templates pre-process and caches translated queries as templates. The initial execution of a query generates a template with placeholders for variable values. This eliminates the need for repeated translation when the same query structure is used with different variables, resulting in substantial performance improvements.&lt;/p&gt;
&lt;p&gt;Cached Templates can be activated by adding the &lt;code&gt;stored=true&lt;/code&gt; query string parameter and the &lt;code&gt;cg-stored-query: template&lt;/code&gt; header to your requests. The service will handle template generation and caching automatically, improving the efficiency of subsequent requests. This feature will be enabled for all requests automatically later this year.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5f4d547377ef4cde9c0961df611268dd.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The system supports the following variable types for cached templates: Boolean, Date, DateTime, String, Int, Float, Locales, and [Locales]. For Locales and [Locales] types, the variable name is required to be &#39;locale&#39;. Any other variable types will result in a fallback to &#39;Cached Queries&#39;.&lt;/p&gt;
&lt;p&gt;Cached Queries, similar to Cached Templates, improve performance by caching fully resolved queries. They act as a backup for queries incompatible with Cached Templates.&lt;/p&gt;
&lt;p&gt;These features are a major step towards optimized Graph query performance, ensuring faster applications and a better user experience.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/c52762bf878d4003825b9b5719fea8e5.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/298e0d3dafa740f4b06cfa38c94f9e94.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2025/2/boosting-graph-query-performance-with-stored-templates/</guid>            <pubDate>Thu, 27 Feb 2025 15:41:52 GMT</pubDate>           <category>Blog post</category></item><item> <title>Required fields support in Optimizely Graph</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2024/9/optimizely-graph-release-3-11-0/</link>            <description>&lt;p&gt;It&#39;s been possible to have &quot;required&quot; properties (value must be entered) in the CMS for a long time. The required metadata haven&#39;t been reflected in Graph - until now. Its now possible to get the GraphQL schema updated, to get information about which fields that always has a value.&lt;/p&gt;
&lt;h3&gt;Configure CMS&lt;/h3&gt;
&lt;p&gt;The CMS must be configured to send metadata about required (value must be entered) properties to the GraphQL schema. We had to add this configuration setting, to not create a breaking change for the 3.11.0 release. The default value is &#39;false&#39;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/881bb7003e6549bfbfcc4f637c78fca2.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Set property as required&lt;/h3&gt;
&lt;p&gt;Here is an example of the property &quot;Heading&quot; in the &quot;TeaserBlock&quot; content type. This property is set to be &quot;required&quot; (value must be entered).&lt;/p&gt;
&lt;h2&gt;&lt;img src=&quot;/link/da3e1bce7c2947cab186bfd9c1c2609a.aspx&quot; /&gt;&lt;/h2&gt;
&lt;h3&gt;GraphQL query with required support&lt;/h3&gt;
&lt;p&gt;The GraphQL query is now configured to know that the field always has a value&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/884c765f6c7a4d02a62fda19ea28a45a.aspx&quot; /&gt;&lt;/p&gt;
</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2024/9/optimizely-graph-release-3-11-0/</guid>            <pubDate>Wed, 25 Sep 2024 15:25:27 GMT</pubDate>           <category>Blog post</category></item><item> <title>Product Listing Page - using Graph</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2024/7/product-listing-page---using-graph/</link>            <description>&lt;p&gt;Optimizely Graph makes it possible to query your data in an advanced way, by using GraphQL. Querying data, using facets and search phrases, is very valuable when building &quot;Product Listing Pages&quot; for Commerce sites. I will describe how you can create your product listing page for Customizable Commerce, using NextJs.&lt;/p&gt;
&lt;p&gt;We are going to use the Foundation example site in this tutorial. For more information how to setup Foundation, see &lt;a href=&quot;https://github.com/episerver/Foundation&quot;&gt;https://github.com/episerver/Foundation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We will create a NextJs app where you can filter products by brand, size, color, and price. We will also be able to search using &quot;semantic search&quot; and normal relevance search.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/5480dbb8f90c40fbac74a93b2f3f2720.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: The templating and styling of the &quot;product listing page&quot; in this example has been made by a person (me) that has very poor knowledge of styling. Its just an example of how you can create a template.&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;Setup and start the Foundation site&lt;/h1&gt;
&lt;p&gt;Start with setting up the Foundation site, and make sure the site is running as it should. Also make sure you can login to edit/admin mode. For more information about the Foundation site, see &lt;a href=&quot;https://github.com/episerver/Foundation&quot;&gt;https://github.com/episerver/Foundation&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Enable Graph for the Foundation site&lt;/h1&gt;
&lt;p&gt;You need to have a Graph account to be able to send content from the Foundation site to Graph. But don&#39;t worry if you don&#39;t have any Graph account. We have prepared a Graph account with Foundation content, so you can test to create an application using our account.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: You can skip this part (&quot;Enable Graph for the Foundation Site&quot;) if you don&#39;t have a Graph account. You can instead jump to the next section &quot;Create GraphQL queries&quot; and use the following singleKey: 5m4F2pBpXPWehc3QGqFfvohgNtgYxHQOxfmKsnqhRYDpZTBU&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Install package to push content to Graph&lt;/h2&gt;
&lt;p&gt;Install the following nuget package (3.9.0 or later): Optimizely.ContentGraph.Cms&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;Optimizely.ContentGraph.Cms&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: The current pre-package &quot;Optimizely.ContentGraph.Commerce&quot; is currently not maintained. A proper Commerce integration will come later. Don&#39;t use the pre-package Optimizely.ContentGraph.Commerce. Add classes similar to how I&#39;m doing it in this blog post instead.&amp;nbsp;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Configure your Graph account&lt;/h2&gt;
&lt;p&gt;Add the following section to appSettings.json:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;  &quot;Optimizely&quot;: {
    &quot;ContentGraph&quot;: {
      &quot;GatewayAddress&quot;: &quot;https://cg.optimizely.com&quot;,
      &quot;AppKey&quot;: &quot;{your-app-key}&quot;,
      &quot;Secret&quot;: &quot;{your-secret}&quot;,
      &quot;SingleKey&quot;: &quot;{your-single-key}&quot;,
      &quot;AllowSendingLog&quot;: &quot;true&quot;
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And add the following to the &quot;ConfigureServices&quot; method in Startup.cs:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;            services.AddContentGraph(x =&amp;gt;
            {
                x.IncludeInheritanceInContentType = true;
                x.PreventFieldCollision = true;
            });&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Add a reference to &quot;GraphCommerceIntegration&quot; project&lt;/h2&gt;
&lt;p&gt;1. Clone the repository GraphCommerceIntegration&lt;/p&gt;
&lt;pre class=&quot;language-markup&quot;&gt;&lt;code&gt;https://github.com/jonasbergqvist/GraphCommerceIntegration.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2. Add the project &quot;&lt;a href=&quot;https://github.com/jonasbergqvist/GraphCommerceIntegration/blob/main/GraphCommerceIntegration/GraphCommerceIntegration.csproj&quot;&gt;GraphCommerceIntegration.csproj&lt;/a&gt;&quot;, which you find under the &quot;&lt;span&gt;GraphCommerceIntegration&quot; folder, to your Foundation solution.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;3. Add Project reference in the Foundation site to &quot;GraphCommerceIntegration&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;4. Compile and start the Foundation site&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Note: The repository contains&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;A class that makes scheduled job include catalog content (event synchronisation of catalog content works without referencing the project)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;A class that includes all languages that has been configured on catalog content.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;A class that triggers event synchronisation when prices have been changed.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Price Base class, which makes it easy to include price information on products and variations when they are pushed to Graph&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Asset Base class, which makes it easy to add assets information on products and variations when they are pushed to Graph&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Aggregation Base class, which makes it easy to aggregate data from variations to its related products.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note: For more information, see the README at &lt;a href=&quot;https://github.com/jonasbergqvist/GraphCommerceIntegration/tree/main&quot;&gt;https://github.com/jonasbergqvist/GraphCommerceIntegration/tree/main&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Add default price to Products and Variations&lt;/h2&gt;
&lt;p&gt;Add the following class to your Foundation site, to include default price on default market for products and variations:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [ServiceConfiguration(typeof(IContentApiModelProperty), Lifecycle = ServiceInstanceScope.Singleton)]
    public class DefaultPriceContentApiModel : ContentApiModelPriceBase
    {
        private readonly ICurrentMarket _currentMarketService;

        public DefaultPriceContentApiModel(
            ContentTypeModelRepository contentTypeModelRepository,
            IContentLoader contentLoader,
            IPriceService priceService,
            ICurrentMarket currentMarketService)
            : base(contentTypeModelRepository, contentLoader, priceService)
        {
            _currentMarketService = currentMarketService;
        }

        public override string Name =&amp;gt; &quot;DefaultMarketPrice&quot;;

        protected override IMarket GetMarket()
        {
            return _currentMarketService.GetCurrentMarket();
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: Products will get the lowest price from its related variations.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Aggregate data from Variation content to Product content&lt;/h2&gt;
&lt;p&gt;Add the following two classes, to include colors and sizes, when content is getting pushed to Graph:&lt;/p&gt;
&lt;h3&gt;Colors&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [ServiceConfiguration(typeof(IContentApiModelProperty), Lifecycle = ServiceInstanceScope.Singleton)]
    public class ColorContentApiModel : ProductAggregationContentApiModelBase&amp;lt;string, GenericProduct, GenericVariant&amp;gt;
    {
        public ColorContentApiModel(ContentTypeModelRepository contentTypeModelRepository, IContentLoader contentLoader)
            : base(contentTypeModelRepository, contentLoader)
        {
        }

        public override string Name =&amp;gt; &quot;Colors&quot;;

        protected override Expression&amp;lt;Func&amp;lt;GenericVariant, string&amp;gt;&amp;gt; VariationProperty =&amp;gt; (x) =&amp;gt; x.Color;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Sizes&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [ServiceConfiguration(typeof(IContentApiModelProperty), Lifecycle = ServiceInstanceScope.Singleton)]
    public class SizeContentApiModel : ProductAggregationContentApiModelBase&amp;lt;string, GenericProduct, GenericVariant&amp;gt;
    {
        public SizeContentApiModel(ContentTypeModelRepository contentTypeModelRepository, IContentLoader contentLoader)
            : base(contentTypeModelRepository, contentLoader)
        {
        }

        public override string Name =&amp;gt; &quot;Sizes&quot;;

        protected override Expression&amp;lt;Func&amp;lt;GenericVariant, string&amp;gt;&amp;gt; VariationProperty =&amp;gt; (x) =&amp;gt; x.Size;

        protected override string ModifyValue(string value) =&amp;gt; value.ToUpper();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Push default product asset to Graph&lt;/h2&gt;
&lt;p&gt;Add the following class, to include the url of default product asset to products, when they are pushed to Graph&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;    [ServiceConfiguration(typeof(IContentApiModelProperty), Lifecycle = ServiceInstanceScope.Singleton)]
    public class DefaultImageUrlContentApiModel : CommerceAssetApiModelBase&amp;lt;string&amp;gt;
    {
        public DefaultImageUrlContentApiModel(ContentTypeModelRepository contentTypeModelRepository, IContentLoader contentLoader, IUrlResolver urlResolver)
            : base(contentTypeModelRepository, contentLoader, urlResolver)
        {
        }

        public override string Name =&amp;gt; &quot;DefaultImageUrl&quot;;

        public override string NoValue =&amp;gt; string.Empty;

        protected override string GetAssets(IEnumerable&amp;lt;CommerceMedia&amp;gt; commerceMediaItems)
        {
            foreach(CommerceMedia media in commerceMediaItems.OrderBy(x =&amp;gt; x.SortOrder))
            {
                if (ContentLoader.TryGet&amp;lt;IContentImage&amp;gt;(media.AssetLink, out var contentMedia))
                {
                    return GetUrl(media);
                }
            }

            return NoValue;
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Create GraphQL queries&lt;/h1&gt;
&lt;p&gt;We will create a couple of GraphQL queries using the data from the Foundation site. We can use our online IDE using url &lt;a href=&quot;https://cg.optimizely.com/app/graphiql?auth={your-single-key}&quot;&gt;https://cg.optimizely.com/app/graphiql?auth={your-single-key}&lt;/a&gt;. You can use our example account if you don&#39;t have any Graph account of your own: &lt;a href=&quot;https://cg.optimizely.com/app/graphiql?auth=5m4F2pBpXPWehc3QGqFfvohgNtgYxHQOxfmKsnqhRYDpZTBU&quot;&gt;https://cg.optimizely.com/app/graphiql?auth=5m4F2pBpXPWehc3QGqFfvohgNtgYxHQOxfmKsnqhRYDpZTBU&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Create ProductListing page&lt;/h2&gt;
&lt;p&gt;Create the following query (including a fragment) to handle the product listing page:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;fragment GenericProductTeaser on GenericProduct {
  Name
  Code
  DefaultMarketPrice
  Brand
  DefaultImageUrl
}

query ProductListing(
  $languages: [Locales] = en
  $searchText: String,
  $brands: [String!],
  $sizes: [String!],
  $colors: [String!],
  $minPrice: Float,
  $maxPrice: Float,
  $skip: Int = 0,
  $limit: Int = 10,
  $order: GenericProductOrderByInput = { 
    _ranking: SEMANTIC,
  }) {
    GenericProduct(
      locale: $languages
      where:{
        _or:[
        {
          _fulltext: {
              match: $searchText
          }
        },
        {
          Name: {
            match: $searchText
            boost: 5
          }
        }
        ]
        DefaultMarketPrice: {
            gte: $minPrice
            lte: $maxPrice
        }
      }
      skip: $skip,
      limit: $limit
      orderBy: $order
  	) {
      total
      items {
          ...GenericProductTeaser
      }
      facets {
          Brand(filters: $brands) {
              name
              count
          }
          Sizes(filters:$sizes) {
              name
              count
          }
          Colors(filters:$colors) {
              name
              count
          }
          DefaultMarketPrice(
            ranges: [
              { to: 50 },
              { from: 51, to: 100 },
              { from: 101, to: 150 },
              { from: 151, to: 200 },
              { from: 201, to: 250 },
              { from: 251, to: 300 },
              { from: 301, to: 350 },
              { from: 351, to: 400 },
              { from: 401, to: 450 },
              { from: 451, to: 500 },
              { from: 501 },
            ]) {
              name
              count
           }
      	}
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lets go through the query peice by peice&lt;/p&gt;
&lt;h3&gt;GenericProductTeaser&lt;/h3&gt;
&lt;p&gt;A fragment is a reusable partual query. You can think about them as blocks. We are creating a fragment that we call &quot;GenericProductTeaser&quot; (it can be called what ever we like) that is handling the &quot;GenericProduct&quot; type. &quot;GenericProduct&quot; is a content-type in the Foundation site, which has been pushed to Graph. We can now choose which fields (properties) to return from &quot;GenericProduct&quot; type. You will get intellisese by holding ctrl and clicking space (ctrl + space).&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;fragment GenericProductTeaser on GenericProduct {
  Name
  Code
  DefaultMarketPrice
  Brand
  DefaultImageUrl
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Query name and Variables&lt;/h3&gt;
&lt;p&gt;We have named the query &quot;ProductListing&quot;, but it could have been named anything. We have also added several variables to the query, which makes it possible to execute the query with many different options. You can think about a GraphQL query as a method in your favorite programming language. You name a query and add the variables to it that you like. You can then use the variables inside the query.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$languages: The languages that you want to get content for. This can be multiple languages. The values in &quot;Locales&quot; is the languages that you have configured in your commerce site.&lt;/li&gt;
&lt;li&gt;$searchText: A search phrase to perform normal or semantic search&lt;/li&gt;
&lt;li&gt;$brands: Selected brands&lt;/li&gt;
&lt;li&gt;$sizes: Selected sizes&lt;/li&gt;
&lt;li&gt;$minPrice: Lowest default price to get products for&lt;/li&gt;
&lt;li&gt;$hightPrice: Highest default price to get products for&lt;/li&gt;
&lt;li&gt;$skip: How many items from the top of the result to skip&lt;/li&gt;
&lt;li&gt;$limit: The number of result items to get&lt;/li&gt;
&lt;li&gt;$order: Which order to receive content.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;query ProductListing(
  $languages: [Locales] = en
  $searchText: String,
  $brands: [String!],
  $sizes: [String!],
  $colors: [String!],
  $minPrice: Float,
  $maxPrice: Float,
  $skip: Int = 0,
  $limit: Int = 10,
  $order: GenericProductOrderByInput = { 
    _ranking: SEMANTIC,
  })&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GenericProduct&lt;/h3&gt;
&lt;p&gt;We will query for all &quot;GenericProduct&quot; items using the content-type &quot;GenericProduct&quot;. This will query all content that is of type &quot;GenericProduct&quot; or inherits from &quot;GenericProduct&quot;.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;GenericProduct(&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Locale&lt;/h3&gt;
&lt;p&gt;The languages to query, were we use the variable $languages. Default value for $languages has been set to &quot;en&quot; in the query.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;locale: $languages&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Where&lt;/h3&gt;
&lt;p&gt;Filtering of content. Filtering will only happen for variables that has a value.&lt;/p&gt;
&lt;p&gt;We are using an &quot;_or&quot; statement to match the variable &quot;$searchText&quot; against all searchable properties (_fullText) and &quot;Name&quot;. We will boost result that matches &quot;Name&quot; by 5.&lt;/p&gt;
&lt;p&gt;We will also filter the &quot;default market price&quot;, based on the $minPrice and $maxPrice variables.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;      where:{
        _or:[
        {
          _fulltext: {
              match: $searchText
          }
        },
        {
          Name: {
            match: $searchText
            boost: 5
          }
        }
        ]
        DefaultMarketPrice: {
            gte: $minPrice
            lte: $maxPrice
        }
      }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Skip &amp;amp; Limit&lt;/h3&gt;
&lt;p&gt;Skip $skip items from the top and include $limit number of items in the result&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;      skip: $skip,
      limit: $limit&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;OrderBy&lt;/h3&gt;
&lt;p&gt;Order the result based on the incoming $order variable. It will by default order the result based on &quot;semantic search&quot; ranking (default variable value in the query)&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;orderBy: $order&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Total&lt;/h3&gt;
&lt;p&gt;The total number of result for the query&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;total&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Items&lt;/h3&gt;
&lt;p&gt;Items are being used to get selected fields (properties). We are referencing the fragment &quot;GenericProductTeaser&quot; to get the fields (properties) specified in the fragment&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;items {
          ...GenericProductTeaser
      }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Facets&lt;/h3&gt;
&lt;p&gt;Facets are aggregating data for a specific field (property) and gives you the unique values together with &quot;count&quot; for the unique value.&lt;/p&gt;
&lt;p&gt;We are creating facets for &quot;Brand&quot;, &quot;Sizes&quot;, &quot;Colors&quot;, and &quot;DefaultMarketPrice&quot;. The first three facets are simple facets, where we use &quot;filters&quot; parameter for each facet to specify which values the user has selected. The last facet is a range facet, to give the the count for different price intervals.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;      facets {
          Brand(filters: $brands) {
              name
              count
          }
          Sizes(filters:$sizes) {
              name
              count
          }
          Colors(filters:$colors) {
              name
              count
          }
          DefaultMarketPrice(
            ranges: [
              { to: 50 },
              { from: 51, to: 100 },
              { from: 101, to: 150 },
              { from: 151, to: 200 },
              { from: 201, to: 250 },
              { from: 251, to: 300 },
              { from: 301, to: 350 },
              { from: 351, to: 400 },
              { from: 401, to: 450 },
              { from: 451, to: 500 },
              { from: 501 },
            ]) {
              name
              count
          	}
      	}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create Product Details page&lt;/h2&gt;
&lt;p&gt;We will also create a product detail page, which will get a product using &quot;code&quot;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;query ProductDetail(
  $locale: Locales = en
  $code: String!
) {
  GenericProduct(
    locale: [$locale]
    where:{
      Code: { eq: $code }
    }
    limit:1
  ) {
    items {
      Name
      Code
      DefaultImageUrl
      DefaultMarketPrice
      Brand
      LongDescription
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Create NextJs application with Foundation data&lt;/h1&gt;
&lt;p&gt;We will create a NextJs app from scratch. If you want to see how the final result can be, than check out the site here: &lt;a href=&quot;https://github.com/jonasbergqvist/GraphCommerceIntegration/tree/main/graph-commerce-example-app&quot;&gt;https://github.com/jonasbergqvist/GraphCommerceIntegration/tree/main/graph-commerce-example-app&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Create new NextJs app&lt;/h2&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;TypeSript: Yes&lt;/li&gt;
&lt;li&gt;ESLint: Yes&lt;/li&gt;
&lt;li&gt;Tailwind CSS: Yes&lt;/li&gt;
&lt;li&gt;src/ directory: Yes&lt;/li&gt;
&lt;li&gt;App Router: No&lt;/li&gt;
&lt;li&gt;Customize the defaultimport alias: No&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Add dependencies&lt;/h2&gt;
&lt;p&gt;Open package.json and add the following&lt;/p&gt;
&lt;h3&gt;In devDependencies&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;    &quot;@graphql-codegen/cli&quot;: &quot;^5.0.2&quot;,
    &quot;@graphql-codegen/client-preset&quot;: &quot;^4.2.6&quot;,
    &quot;@parcel/watcher&quot;: &quot;^2.4.1&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;In dependencies&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;    &quot;@apollo/client&quot;: &quot;^3.10.4&quot;,
    &quot;graphql&quot;: &quot;^16.8.1&quot;,
    &quot;html-react-parser&quot;: &quot;^5.1.10&quot;,
    &quot;next-range-slider&quot;: &quot;^1.0.5&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;In scripts&lt;/h3&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&quot;codegen&quot;: &quot;graphql-codegen --watch&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Install all dependencies&lt;/h3&gt;
&lt;p&gt;Run the following command to install all the dependencies&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configure GraphQL Codegen&lt;/h2&gt;
&lt;p&gt;GraphQL codegen is a greate tool to give strongly typed queries and query result. We need to configure Codegen to use our account in Graph for the application&lt;/p&gt;
&lt;h3&gt;Create codegen.ts&lt;/h3&gt;
&lt;p&gt;Create a new file under the root folder of the application with name &quot;codegen.ts&quot; and add the following&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { CodegenConfig  } from &#39;@graphql-codegen/cli&#39;

const config : CodegenConfig = {
    schema: &quot;https://cg.optimizely.com/content/v2?auth={your-single-key}&quot;,
    documents: [&quot;src/**/*.{ts,tsx}&quot;],
    ignoreNoDocuments: true,
    generates: {
        &#39;./src/graphql/&#39;: {
            preset: &#39;client&#39;,
            plugins: [],
        }
    }
}

export default config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change {your-single-key} to your single-key. If you don&#39;t have any Graph account, then use&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;5m4F2pBpXPWehc3QGqFfvohgNtgYxHQOxfmKsnqhRYDpZTBU&lt;/span&gt;&lt;/div&gt;
&lt;h3&gt;&lt;span&gt;Start codegen watcher&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;&lt;span&gt;Run the following command to let GraphQL codegen continuesly check your project for GraphQL queries&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;yarn codegen&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Use Apollo Client with one-page edit support&lt;/h2&gt;
&lt;p&gt;Apollo Client is one of many clients that has build-in GraphQL support. We will use Apollo Client in this example, but you can use any client of choose in your own projects.&lt;/p&gt;
&lt;h3&gt;Add apolloClient.tsx&lt;/h3&gt;
&lt;p&gt;Add a new file with name &quot;apolloClient.tsx&quot; under &quot;src&quot; folder&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { ApolloClient, createHttpLink, InMemoryCache } from &#39;@apollo/client&#39;;
import { setContext } from &#39;@apollo/client/link/context&#39;;

let client: ApolloClient&amp;lt;any&amp;gt; | undefined = undefined;

if (typeof window !== &quot;undefined&quot; &amp;amp;&amp;amp; window.location !== undefined) {
    const queryString = window?.location?.search;
    const urlParams = new URLSearchParams(queryString);
    const preview_token = urlParams.get(&#39;preview_token&#39;) ?? undefined;

    if (preview_token) {
        const httpLink = createHttpLink({
            uri: &#39;https://cg.optimizely.com/content/v2&#39;,
        });

        const authLink = setContext((_, { headers }) =&amp;gt; {
            return {
                headers: {
                    ...headers,
                    authorization: `Bearer ${preview_token}`
                }
            };
        });

        client = new ApolloClient({
            link: authLink.concat(httpLink),
            cache: new InMemoryCache()
        });

        const communicationScript = document.createElement(&#39;script&#39;);
        communicationScript.src = `{url-to-your-foundation-site}/Util/javascript/communicationinjector.js`;
        communicationScript.setAttribute(&#39;data-nscript&#39;, &#39;afterInteractive&#39;)
        document.body.appendChild(communicationScript);
    }
}

if (client === undefined) {
    const httpLink = createHttpLink({
        uri: &#39;https://cg.optimizely.com/content/v2?auth={your-single-key}&#39;,
    });

    const authLink = setContext((_, { headers }) =&amp;gt; {
        return {
            headers: {
                ...headers
            }
        };
    });

    client = new ApolloClient({
        link: authLink.concat(httpLink),
        cache: new InMemoryCache()
    });
}

export default client;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your need to change two things in this file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change {url-to-your-foundation-site} to the url where you run your Foundation site, for example &lt;a href=&quot;https://localhost:44397/&quot;&gt;https://localhost:44397&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;{your-single-key} to your single-key of your Graph account. Use 5m4F2pBpXPWehc3QGqFfvohgNtgYxHQOxfmKsnqhRYDpZTBU if you don&#39;t have any Graph account&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;h3&gt;&lt;span&gt;Update _app.tsx to use your apollo cloent&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Update_app.tsx under src/pages to be the following&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import &quot;@/styles/globals.css&quot;;
import { ApolloProvider } from &quot;@apollo/client&quot;;
import type { AppProps } from &quot;next/app&quot;;
import client from &#39;../apolloClient&#39;;

export default function App({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;ApolloProvider client={client!}&amp;gt;
      &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/ApolloProvider&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2&gt;Create GenericProductTeaserComponent.tsx&lt;/h2&gt;
&lt;p&gt;Add a folder with name &quot;components&quot; under &quot;src&quot; and add a file with name &quot;GenericProductTeaserComponent.tsx&quot; in the &quot;components&quot; folder.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { FragmentType, graphql, useFragment } from &quot;@/graphql&quot;
import { Dispatch, FC, SetStateAction, useState } from &quot;react&quot;; 

export const GenericProductTeaserFragment = graphql(/* GraphQL */ `
    fragment GenericProductTeaser on GenericProduct {
        Name
        Code
        DefaultMarketPrice
        Brand
        DefaultImageUrl
    }
`)

interface GenericProductTeaserProps {
    GenericProductTeaser: FragmentType&amp;lt;typeof GenericProductTeaserFragment&amp;gt;
    setSelectedCode: Dispatch&amp;lt;SetStateAction&amp;lt;string&amp;gt;&amp;gt;;
    setShowModal: Dispatch&amp;lt;SetStateAction&amp;lt;boolean&amp;gt;&amp;gt;;
}
 
const GenericProductTeaserComponent: FC&amp;lt;GenericProductTeaserProps&amp;gt; = ({ GenericProductTeaser, setSelectedCode, setShowModal}) =&amp;gt; {
    const setSelected = (event: any) =&amp;gt; {
        if(event?.target?.id) {
            setSelectedCode(event?.target?.id)
            setShowModal(true)
        }
      }

    const item = useFragment(GenericProductTeaserFragment, GenericProductTeaser)
    const imageUrl = &#39;https://localhost:44397&#39; + item.DefaultImageUrl
        return (
            &amp;lt;div className=&quot;group relative&quot;&amp;gt;
                &amp;lt;div className=&quot;aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-80&quot;&amp;gt;
                    &amp;lt;img src={imageUrl} alt={item.Name ?? &#39;&#39;} id={item.Code ?? &#39;&#39;} onClick={setSelected} className=&quot;h-full w-full object-cover object-center lg:h-full lg:w-full&quot;/&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div className=&quot;mt-4 flex justify-between&quot;&amp;gt;
                &amp;lt;div&amp;gt;
                    &amp;lt;h3 className=&quot;text-sm text-gray-700&quot;&amp;gt;
                        &amp;lt;button data-modal-target=&quot;defaultModal&quot; data-modal-toggle=&quot;defaultModal&quot; id={item.Code ?? &#39;&#39;} onClick={setSelected} className=&quot;inline-flex items-center text-blue-400&quot;&amp;gt;
                            {item.Name}
                        &amp;lt;/button&amp;gt;
                    &amp;lt;/h3&amp;gt;
                    &amp;lt;p className=&quot;mt-1 text-sm text-gray-500&quot;&amp;gt;{item.Brand}&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;p className=&quot;text-sm font-medium text-gray-900&quot;&amp;gt;${item.DefaultMarketPrice}&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        )
}
 
export default GenericProductTeaserComponent&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The GraphQL fragment GenericProductTeaser is added in the beginning of the file. This query is then used in the &quot;GenericProductTeaserComponent&quot;. You can try to add fields (properties) that exist in GenericProduct in the fragment. You will have the fields/properties available in the GenericProductTeaserComponent a couple of seconds after you have added them and saved the file.&lt;/p&gt;
&lt;p&gt;Test to add _score and save the file&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;    fragment GenericProductTeaser on GenericProduct {
        Name
        Code
        DefaultMarketPrice
        Brand
        DefaultImageUrl
        _score
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;_score will be available in &quot;item&quot; inside GenericProductTeaserComponent a couple of seconds after you have saved the file.&lt;/p&gt;
&lt;p&gt;Try to build the site&lt;/p&gt;
&lt;p&gt;Check that everything is working by running the following command&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create ProductListingComponent..tsx&lt;/h2&gt;
&lt;p&gt;Create ProductListingComponent.tsx under the &quot;components&quot; with the product listing page GraphQL query&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 

        } 
    })

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;

                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;

                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update index.tsx&lt;/h2&gt;
&lt;p&gt;Update index.tsx in src/pages folder to the following&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import ProductListingComponent from &quot;@/components/ProductListingComponent&quot;;

export default function Home() {
  return (
      &amp;lt;ProductListingComponent /&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Start the Foundation website to make images work&lt;/h2&gt;
&lt;p&gt;Start the Foundation website if its not running already. It needs to run to make images in the NextJs app work. The reason for this is that Graph only stores the link to the actual images, which lives inside the Commerce system. Everything except the images (you will get broken images) will work in case you don&#39;t start the Foundation site.&lt;/p&gt;
&lt;h2&gt;Start the NextJs app&lt;/h2&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should now see some products when browsing to&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;http://localhost:3000/&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create ProductDetailComponent.tsx&lt;/h2&gt;
&lt;p&gt;Create a file with name ProductDetailComponent.tsx under &quot;components&quot;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { Dispatch, FC, SetStateAction, useEffect, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;
import { graphql } from &#39;@/graphql&#39;
import parse from &#39;html-react-parser&#39;;


export const ProductDetail = graphql(/* GraphQL */ `
    query ProductDetail(
    $locale: Locales = en
    $code: String!
    ) {
    GenericProduct(
        locale: [$locale]
        where:{
        Code: { eq: $code }
        }
        limit:1
    ) {
        items {
        Name
        Code
        DefaultImageUrl
        DefaultMarketPrice
        Brand
        LongDescription
        }
    }
    }
`)


interface ProductDetailProps {
    code: string
    setOpen: Dispatch&amp;lt;SetStateAction&amp;lt;boolean&amp;gt;&amp;gt;;
}
 
const ProductDetailComponent: FC&amp;lt;ProductDetailProps&amp;gt; = ({code, setOpen}) =&amp;gt; {


    const { data } = useQuery(ProductDetail, {
        variables: {
            code
        }
    })


    const item = data?.GenericProduct?.items![0]
    const imageUrl = &#39;{url-to-your-foundation-site}&#39; + item?.DefaultImageUrl
    return (
        &amp;lt;div className=&quot;justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none&quot;&amp;gt;
            &amp;lt;div className=&quot;relative w-auto my-6 mx-auto max-w-3xl&quot;&amp;gt;
            {/*content*/}
            &amp;lt;div className=&quot;border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none&quot;&amp;gt;
                {/*header*/}
                &amp;lt;div className=&quot;flex items-start justify-between p-5 border-b border-solid border-slate-200 rounded-t&quot;&amp;gt;
                &amp;lt;h3 className=&quot;text-3xl font-semibold&quot;&amp;gt;
                    { item?.Name }
                &amp;lt;/h3&amp;gt;
                    &amp;lt;button
                        className=&quot;text-red-500 background-transparent font-bold uppercase px-6 py-2 text-sm outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150&quot;
                        type=&quot;button&quot;
                        onClick={() =&amp;gt; setOpen(false)}
                    &amp;gt;
                        Close
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
                {/*body*/}
                &amp;lt;div className=&quot;relative p-6 flex-auto&quot;&amp;gt;
                &amp;lt;div className=&quot;aspect-h-1 aspect-w-1 w-full overflow-hidden rounded-md bg-gray-200 lg:aspect-none group-hover:opacity-75 lg:h-80&quot;&amp;gt;
                    &amp;lt;img src={imageUrl} alt={item?.Name ?? &#39;&#39;} className=&quot;h-full w-full object-cover object-center lg:h-full lg:w-full&quot;/&amp;gt;
                &amp;lt;/div&amp;gt;
                    &amp;lt;p className=&quot;my-4 text-slate-500 text-lg leading-relaxed&quot;&amp;gt;
                        { parse(item?.LongDescription ?? &#39;&#39;)}
                    &amp;lt;/p&amp;gt;
                    &amp;lt;p className=&quot;my-4 text-slate-800 text-lg leading-relaxed&quot;&amp;gt;
                        From: {item?.Brand}
                    &amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
                {/*footer*/}
            &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}
 
export default ProductDetailComponent&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;Change {url-to-your-foundation-site} to the url where you run your Foundation site, for example&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://localhost:44397/&quot;&gt;https://localhost:44397&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Update ProductListingComponent to load product details when needed&lt;/h2&gt;
&lt;p&gt;Update ProductListingComponent.tsx to open product details component in a modal when clicking on the image or name&lt;/p&gt;
&lt;p&gt;Add the following before &amp;lt;/main&amp;gt;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and import the ProductDetailComponent&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import ProductDetailComponent from &#39;./ProductDetailComponent&#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ProductListingComponent should now look like this&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
import ProductDetailComponent from &#39;./ProductDetailComponent&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 

        } 
    })

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;

                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;

                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Test the app&lt;/h3&gt;
&lt;p&gt;Try to click on an image to load details about the product&lt;/p&gt;
&lt;p&gt;Create TermFacetComponent.tsx&lt;/p&gt;
&lt;p&gt;Create TermFacetComponent.tsx in &quot;components&quot; folder with following code&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { StringFacet } from &quot;@/graphql/graphql&quot;
import { Dispatch, FC, SetStateAction } from &quot;react&quot;

interface TermFacetProps {
    headingText: string
    values: string[]
    facet: StringFacet[] | null
    setValues: Dispatch&amp;lt;SetStateAction&amp;lt;string[]&amp;gt;&amp;gt;;
}

const TermFacetComponent: FC&amp;lt;TermFacetProps&amp;gt; = ({ headingText, values, facet, setValues }) =&amp;gt; {

    const handleSelection = (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        let localValues = Array.from(values)
        if(event.target.checked) {
            localValues.push(event.target.id);
        }
        else {
            localValues = localValues.filter(x =&amp;gt; x !== event.target.id);
        }
        setValues(localValues);
    };

    return (
      &amp;lt;div className=&quot;border-b border-gray-200 py-6&quot;&amp;gt;
        &amp;lt;h3 className=&quot;-my-3 flow-root&quot;&amp;gt;{ headingText }&amp;lt;/h3&amp;gt;
        &amp;lt;div className=&quot;pt-6&quot; id=&quot;filter-section-0&quot;&amp;gt;
          &amp;lt;div className=&quot;space-y-4&quot;&amp;gt;
            
              { facet?.map((item, idx) =&amp;gt; {
                  return (
                    &amp;lt;div className=&quot;flex items-center&quot; key={idx}&amp;gt;
                      &amp;lt;input id={item?.name ?? &#39;&#39;} name=&quot;color[]&quot; value={item?.name ?? &#39;&#39;} checked={values.indexOf(item?.name ?? &#39;&#39;) &amp;gt; -1 } onChange={handleSelection} type=&quot;checkbox&quot; className=&quot;h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500&quot;/&amp;gt;
                      &amp;lt;label htmlFor=&quot;filter-color-0&quot; className=&quot;ml-3 text-sm text-gray-600&quot;&amp;gt;{ item?.name } ({ item?.count })&amp;lt;/label&amp;gt;
                    &amp;lt;/div&amp;gt;
                  )
              }) }
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

export default TermFacetComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update ProductListingComponent.tsx to use terms facets&lt;/h2&gt;
&lt;p&gt;Update ProductListingComponent.tsx to look like this&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useEffect, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
import ProductDetailComponent from &#39;./ProductDetailComponent&#39;
import TermFacetComponent from &#39;./TermFacetComponent&#39;
import { StringFacet } from &#39;@/graphql/graphql&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [brands, setBrands] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [brandFacet, setBrandFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [colors, setColors] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [colorFacet, setColorFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [sizes, setSizes] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [sizeFacet, setSizeFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 
            brands, 
            colors, 
            sizes,
        } 
    })
    
    function facetOptionChanged(fasetQueryResult: StringFacet[], faset: StringFacet[]): boolean {
        if(fasetQueryResult.length != faset.length) {
            return true
        }

        for (let i = 0; i &amp;lt; fasetQueryResult.length; i++) {
            if(fasetQueryResult[i]?.name !== faset[i]?.name) {
                return true
            }

            if(fasetQueryResult[i]?.count !== faset[i]?.count) {
                return true
            }
        }

        return false
    }

    useEffect(() =&amp;gt; {
        if(data?.GenericProduct?.facets?.Brand != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Brand) {
          if(facetOptionChanged(data?.GenericProduct?.facets?.Brand as StringFacet[], brandFacet)) {
            setBrandFacet(data.GenericProduct.facets?.Brand as StringFacet[])
          }
        }

        if(data?.GenericProduct?.facets?.Colors != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Colors) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Colors as StringFacet[], colorFacet)) {
              setColorFacet(data.GenericProduct.facets?.Colors as StringFacet[])
            }
        }

        if(data?.GenericProduct?.facets?.Sizes != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Sizes) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Sizes as StringFacet[], sizeFacet)) {
              setSizeFacet(data.GenericProduct.facets?.Sizes as StringFacet[])
            }
        }
      }, [brandFacet, colorFacet, sizeFacet, data?.GenericProduct?.facets]);

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;

                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Brands&#39;
                                facet={brandFacet}
                                values={brands}
                                setValues={setBrands}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Colors&#39;
                                facet={colorFacet}
                                values={colors}
                                setValues={setColors}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Sizes&#39;
                                facet={sizeFacet}
                                values={sizes}
                                setValues={setSizes}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;

                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;

                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Test facets in the app&lt;/h3&gt;
&lt;p&gt;Test to click on different facet values, and validate that correct products are shown based on your selections&lt;/p&gt;
&lt;h2&gt;Create RangeFacetComponent.tsx&lt;/h2&gt;
&lt;p&gt;Create RangeFacetComponent.tsx under &quot;Components&quot; with following code&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { NumberFacet } from &quot;@/graphql/graphql&quot;
import React, { Dispatch, FC, SetStateAction, useEffect, useState } from &quot;react&quot;;
import { RangeSlider } from &#39;next-range-slider&#39;;
import &#39;next-range-slider/dist/main.css&#39;;

interface RangeFacetProps {
    headingText: string
    minValue: number
    maxValue: number
    currentLowValue: number
    currentHighValue: number
    facet: NumberFacet[] | null
    setLowValue: Dispatch&amp;lt;SetStateAction&amp;lt;number&amp;gt;&amp;gt;;
    setHighValue: Dispatch&amp;lt;SetStateAction&amp;lt;number&amp;gt;&amp;gt;;
}

const RangeFacetComponent: FC&amp;lt;RangeFacetProps&amp;gt; = ({ headingText, minValue, maxValue, currentLowValue, currentHighValue, facet, setLowValue, setHighValue }) =&amp;gt; {

    const [localLowValue, setLocalLowValue] = useState(() =&amp;gt; minValue);
    const [localHighValue, setHighLocalValue] = useState(() =&amp;gt; maxValue)

    useEffect(() =&amp;gt; {
        const delayDebounceFn = setTimeout(() =&amp;gt; {
          setLowValue(localLowValue)
          setHighValue(localHighValue)
        }, 500)
    
        return () =&amp;gt; clearTimeout(delayDebounceFn)
      }, [localLowValue, localHighValue, setLowValue, setHighValue])

      const facetValues = Array.from(facet?.values() ?? []).map((x) =&amp;gt; x.count!)
      const highestFacetCount = Math.max(...facetValues)
    return (
        &amp;lt;div className=&quot;border-b border-gray-200 py-6&quot;&amp;gt;
            &amp;lt;div className=&quot;flex flex-col items-center w-full max-w-screen-md p-6 pb-6 rounded-lg shadow-xl sm:p-1&quot;&amp;gt;
		        &amp;lt;div className=&quot;flex items-end flex-grow w-full mt-2 space-x-2 sm:space-x-3 h-16&quot;&amp;gt;
                {
                    facet?.map((x, index) =&amp;gt; {
                        let hValue = Math.round((x.count! / highestFacetCount) * 12)
                        if(Math.abs(hValue % 2) == 1) {
                            hValue = hValue - 1
                        }
                        const className = &quot;bg-indigo-200 relative flex justify-center w-full h-&quot; + hValue
                        return (
                            &amp;lt;div className=&quot;relative flex flex-col items-center flex-grow pb-5 group&quot; key={index}&amp;gt;
                                &amp;lt;span className=&quot;absolute top-0 hidden -mt-6 text-xs font-bold group-hover:block&quot;&amp;gt;{x?.count ?? &#39;0&#39;}&amp;lt;/span&amp;gt;
                                &amp;lt;div className={className}&amp;gt;&amp;lt;/div&amp;gt;
                            &amp;lt;/div&amp;gt;
                        )
                    })
                }
		        &amp;lt;/div&amp;gt;
	        &amp;lt;/div&amp;gt;
            &amp;lt;div&amp;gt;
                &amp;lt;RangeSlider
                    min={minValue}
                    max={maxValue}
                    step={10}
                    options={{
                        leftInputProps: {
                            value: currentLowValue,
                            onChange: (e) =&amp;gt; setLocalLowValue(Number(e.target.value)),
                        },
                        rightInputProps: {
                            value: currentHighValue,
                            onChange: (e) =&amp;gt; setHighLocalValue(Number(e.target.value)),
                        },
                        }
                    }
                /&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div className=&quot;text-center&quot;&amp;gt;{ headingText }: {currentLowValue} - {currentHighValue}&amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
  );
}

export default RangeFacetComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update ProductListingComponent to add price facet&lt;/h2&gt;
&lt;p&gt;Add RangeFacetComponent and two &quot;useStates&quot;. The variables send in the GraphQL query will also have to pass the minPrice and maxPrice&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;&amp;lt;RangeFacetComponent
                                headingText=&#39;Price range&#39;
                                currentHighValue={highPrice}
                                setHighValue={setHighPrice}
                                setLowValue={setLowPrice}
                                currentLowValue={lowPrice}
                                minValue={0}
                                maxValue={600}
                                facet={data?.GenericProduct?.facets?.DefaultMarketPrice as NumberFacet[]}
                            /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;    const [lowPrice, setLowPrice] = useState(() =&amp;gt; 0);
    const [highPrice, setHighPrice] = useState(() =&amp;gt; 600)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;    const { data } = useQuery(ProductListing, { 
        variables: { 
            brands, 
            colors, 
            sizes,
            minPrice: lowPrice,
            maxPrice: highPrice,
        } 
    })&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ProductListingComponent should have the following code after adding RangeFacetComponent&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useEffect, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
import ProductDetailComponent from &#39;./ProductDetailComponent&#39;
import TermFacetComponent from &#39;./TermFacetComponent&#39;
import { NumberFacet, StringFacet } from &#39;@/graphql/graphql&#39;
import RangeFacetComponent from &#39;./RangeFacetComponent&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [brands, setBrands] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [brandFacet, setBrandFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [colors, setColors] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [colorFacet, setColorFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [sizes, setSizes] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [sizeFacet, setSizeFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [lowPrice, setLowPrice] = useState(() =&amp;gt; 0);
    const [highPrice, setHighPrice] = useState(() =&amp;gt; 600)

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 
            brands, 
            colors, 
            sizes,
        } 
    })
    
    function facetOptionChanged(fasetQueryResult: StringFacet[], faset: StringFacet[]): boolean {
        if(fasetQueryResult.length != faset.length) {
            return true
        }

        for (let i = 0; i &amp;lt; fasetQueryResult.length; i++) {
            if(fasetQueryResult[i]?.name !== faset[i]?.name) {
                return true
            }

            if(fasetQueryResult[i]?.count !== faset[i]?.count) {
                return true
            }
        }

        return false
    }

    useEffect(() =&amp;gt; {
        if(data?.GenericProduct?.facets?.Brand != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Brand) {
          if(facetOptionChanged(data?.GenericProduct?.facets?.Brand as StringFacet[], brandFacet)) {
            setBrandFacet(data.GenericProduct.facets?.Brand as StringFacet[])
          }
        }

        if(data?.GenericProduct?.facets?.Colors != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Colors) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Colors as StringFacet[], colorFacet)) {
              setColorFacet(data.GenericProduct.facets?.Colors as StringFacet[])
            }
        }

        if(data?.GenericProduct?.facets?.Sizes != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Sizes) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Sizes as StringFacet[], sizeFacet)) {
              setSizeFacet(data.GenericProduct.facets?.Sizes as StringFacet[])
            }
        }
      }, [brandFacet, colorFacet, sizeFacet, data?.GenericProduct?.facets]);

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;RangeFacetComponent
                                headingText=&#39;Price range&#39;
                                currentHighValue={highPrice}
                                setHighValue={setHighPrice}
                                setLowValue={setLowPrice}
                                currentLowValue={lowPrice}
                                minValue={0}
                                maxValue={600}
                                facet={data?.GenericProduct?.facets?.DefaultMarketPrice as NumberFacet[]}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Brands&#39;
                                facet={brandFacet}
                                values={brands}
                                setValues={setBrands}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Colors&#39;
                                facet={colorFacet}
                                values={colors}
                                setValues={setColors}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Sizes&#39;
                                facet={sizeFacet}
                                values={sizes}
                                setValues={setSizes}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;

                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;

                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add OrderByComponent.tsx&lt;/p&gt;
&lt;p&gt;Add OrderByComponent.tsx under &quot;components&quot; with the following code&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { OrderBy } from &quot;@/graphql/graphql&quot;;
import { Dispatch, FC, SetStateAction, useState } from &quot;react&quot;

interface OrderByProps {
    orderBy: string
    setorderBy: Dispatch&amp;lt;SetStateAction&amp;lt;string&amp;gt;&amp;gt;;
    orderByDirection: OrderBy
    setorderByDirection: Dispatch&amp;lt;SetStateAction&amp;lt;OrderBy&amp;gt;&amp;gt;;
}

const OrderByComponent: FC&amp;lt;OrderByProps&amp;gt; = ({ orderBy, setorderBy, orderByDirection, setorderByDirection }) =&amp;gt; {
    const [isOrderByInputOpen, setOrderByInputIsOpen] = useState(false);
    const [isOrderByDirectionOpen, setOrderByDirectionIsOpen] = useState(false);

    const toggleOrderByDirectionDropdown = () =&amp;gt; {
        setOrderByDirectionIsOpen(!isOrderByDirectionOpen);
    };

    const toggleOrderByInputDropdown = () =&amp;gt; {
        setOrderByInputIsOpen(!isOrderByInputOpen);
    };

    const orderBySemantic = () =&amp;gt; {
        setorderBy(&#39;Semantic&#39;)
        setOrderByInputIsOpen(false);
    };

    const orderByName = () =&amp;gt; {
        setorderBy(&#39;Name&#39;)
        setOrderByInputIsOpen(false);
        setorderByDirection(OrderBy.Desc)
    };

    const orderByPrice = () =&amp;gt; {
        setorderBy(&#39;DefaultMarketPrice&#39;)
        setOrderByInputIsOpen(false);
    };

    const orderByBrand = () =&amp;gt; {
        setorderBy(&#39;Brand&#39;)
        setOrderByInputIsOpen(false);
    };

    const orderAsc = () =&amp;gt; {
        setorderByDirection(OrderBy.Asc)
        setOrderByDirectionIsOpen(false);
    };

    const orderDesc = () =&amp;gt; {
        setorderByDirection(OrderBy.Desc)
        setOrderByDirectionIsOpen(false);
    };

    return (
        &amp;lt;div className=&#39;w-full py-6 pb-8&#39;&amp;gt;
            &amp;lt;div className=&quot;ml-2 relative inline-block&quot;&amp;gt;
                &amp;lt;button
                    type=&quot;button&quot;
                    className=&quot;px-4 py-2 text-black bg-slate-400 hover:bg-slate-300 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm inline-flex items-center&quot;
                    onClick={toggleOrderByInputDropdown}
                &amp;gt;
                    Order By: {orderBy}&amp;lt;svg className=&quot;w-2.5 h-2.5 ml-2.5&quot; aria-hidden=&quot;true&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 10 6&quot;&amp;gt;
                        &amp;lt;path stroke=&quot;currentColor&quot; d=&quot;m1 1 4 4 4-4&quot; /&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;

                {isOrderByInputOpen &amp;amp;&amp;amp; (
                    &amp;lt;div className=&quot;origin-top-right absolute right-0 mt-2 w-44 rounded-lg shadow-lg bg-white ring-1 ring-black ring-opacity-5&quot;&amp;gt;
                        &amp;lt;ul role=&quot;menu&quot; aria-orientation=&quot;vertical&quot; aria-labelledby=&quot;options-menu&quot;&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderBySemantic}
                                &amp;gt;
                                    Semantic
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderByName}
                                &amp;gt;
                                    Name
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderByPrice}
                                &amp;gt;
                                    DefaultMarketPrice
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderByBrand}
                                &amp;gt;
                                    Brand
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/div&amp;gt;
                )}
            &amp;lt;/div&amp;gt;

            &amp;lt;div className=&quot;ml-2 relative inline-block&quot;&amp;gt;
                &amp;lt;button
                    type=&quot;button&quot;
                    className=&quot;px-4 py-2 text-black bg-slate-400 hover:bg-slate-300 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm inline-flex items-center&quot;
                    onClick={toggleOrderByDirectionDropdown}
                &amp;gt;
                    Direction: {orderByDirection} &amp;lt;svg className=&quot;w-2.5 h-2.5 ml-2.5&quot; aria-hidden=&quot;true&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 10 6&quot;&amp;gt;
                        &amp;lt;path stroke=&quot;currentColor&quot; d=&quot;m1 1 4 4 4-4&quot; /&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;

                {isOrderByDirectionOpen &amp;amp;&amp;amp; (
                    &amp;lt;div className=&quot;origin-top-right absolute right-0 mt-2 w-44 rounded-lg shadow-lg bg-white ring-1 ring-black ring-opacity-5&quot;&amp;gt;
                        &amp;lt;ul role=&quot;menu&quot; aria-orientation=&quot;vertical&quot; aria-labelledby=&quot;options-menu&quot;&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderAsc}
                                &amp;gt;
                                    Asc
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                            &amp;lt;li&amp;gt;
                                &amp;lt;a
                                    href=&quot;#&quot;
                                    className=&quot;block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100&quot;
                                    onClick={orderDesc}
                                &amp;gt;
                                    Desc
                                &amp;lt;/a&amp;gt;
                            &amp;lt;/li&amp;gt;
                        &amp;lt;/ul&amp;gt;
                    &amp;lt;/div&amp;gt;
                )}
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    )
}

export default OrderByComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update ProductListingComponent to include ordering&lt;/h2&gt;
&lt;p&gt;ProductListingComponent should now look like this&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useEffect, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
import ProductDetailComponent from &#39;./ProductDetailComponent&#39;
import TermFacetComponent from &#39;./TermFacetComponent&#39;
import { GenericProductOrderByInput, NumberFacet, OrderBy, Ranking, StringFacet } from &#39;@/graphql/graphql&#39;
import RangeFacetComponent from &#39;./RangeFacetComponent&#39;
import OrderByComponent from &#39;./OrderByCompontent&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [orderByInput, setOrderByInput] = useState(() =&amp;gt; &#39;DefaultMarketPrice&#39;);
    const [orderByDirection, setOrderByDirection] = useState(() =&amp;gt; OrderBy.Asc);

    const [brands, setBrands] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [brandFacet, setBrandFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [colors, setColors] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [colorFacet, setColorFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [sizes, setSizes] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [sizeFacet, setSizeFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [lowPrice, setLowPrice] = useState(() =&amp;gt; 0);
    const [highPrice, setHighPrice] = useState(() =&amp;gt; 600)

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 
            brands, 
            colors, 
            sizes,
            minPrice: lowPrice,
            maxPrice: highPrice,
            order: getOrder()
        } 
    })

    function getOrder(): GenericProductOrderByInput {
        if(orderByInput === &quot;Name&quot;) {
            return { Name: orderByDirection }
        } else if (orderByInput === &quot;Brand&quot;) {
            return { Brand: orderByDirection }
        } else if(orderByInput === &quot;DefaultMarketPrice&quot;) {
            return { DefaultMarketPrice: orderByDirection }
        } else {
            return { _ranking: Ranking.Semantic }
        }
    }
    
    function facetOptionChanged(fasetQueryResult: StringFacet[], faset: StringFacet[]): boolean {
        if(fasetQueryResult.length != faset.length) {
            return true
        }

        for (let i = 0; i &amp;lt; fasetQueryResult.length; i++) {
            if(fasetQueryResult[i]?.name !== faset[i]?.name) {
                return true
            }

            if(fasetQueryResult[i]?.count !== faset[i]?.count) {
                return true
            }
        }

        return false
    }

    useEffect(() =&amp;gt; {
        if(data?.GenericProduct?.facets?.Brand != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Brand) {
          if(facetOptionChanged(data?.GenericProduct?.facets?.Brand as StringFacet[], brandFacet)) {
            setBrandFacet(data.GenericProduct.facets?.Brand as StringFacet[])
          }
        }

        if(data?.GenericProduct?.facets?.Colors != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Colors) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Colors as StringFacet[], colorFacet)) {
              setColorFacet(data.GenericProduct.facets?.Colors as StringFacet[])
            }
        }

        if(data?.GenericProduct?.facets?.Sizes != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Sizes) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Sizes as StringFacet[], sizeFacet)) {
              setSizeFacet(data.GenericProduct.facets?.Sizes as StringFacet[])
            }
        }
      }, [brandFacet, colorFacet, sizeFacet, data?.GenericProduct?.facets]);

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;RangeFacetComponent
                                headingText=&#39;Price range&#39;
                                currentHighValue={highPrice}
                                setHighValue={setHighPrice}
                                setLowValue={setLowPrice}
                                currentLowValue={lowPrice}
                                minValue={0}
                                maxValue={600}
                                facet={data?.GenericProduct?.facets?.DefaultMarketPrice as NumberFacet[]}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Brands&#39;
                                facet={brandFacet}
                                values={brands}
                                setValues={setBrands}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Colors&#39;
                                facet={colorFacet}
                                values={colors}
                                setValues={setColors}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                        &amp;lt;TermFacetComponent
                                headingText=&#39;Sizes&#39;
                                facet={sizeFacet}
                                values={sizes}
                                setValues={setSizes}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;

                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;
                                &amp;lt;OrderByComponent 
                                    orderBy={orderByInput} 
                                    setorderBy={setOrderByInput}
                                    orderByDirection={orderByDirection}
                                    setorderByDirection={setOrderByDirection}
                                /&amp;gt;
                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create SearchTextComponent.tsx&lt;/p&gt;
&lt;p&gt;Create SearchTextComponent.tsx under &quot;components&quot; with the following code&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { Dispatch, FC, SetStateAction, useState } from &quot;react&quot;

interface SearchTextProps {
    searchText: string
    setSearchText: Dispatch&amp;lt;SetStateAction&amp;lt;string&amp;gt;&amp;gt;;
}

const SearchTextComponent: FC&amp;lt;SearchTextProps&amp;gt; = ({ searchText, setSearchText }) =&amp;gt; {

    const [internalSearchText, setInternalSearchText] = useState(() =&amp;gt; searchText);

    const handleSearchClick = (event: any) =&amp;gt; {
        setSearchText(internalSearchText)
    };
  
    const handleSearchInput = (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
        setInternalSearchText(event.target.value);
    };
  
    const handleSearchboxKeyDown = (event: React.KeyboardEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
      if (event.key === &quot;Enter&quot;) {
        setSearchText(internalSearchText)  
      }
    };

    return (
      &amp;lt;div className=&quot;relative flex items-center w-full h-full lg:w-96 group&quot;&amp;gt;
          &amp;lt;input type=&quot;text&quot; value={internalSearchText} onChange={handleSearchInput} onKeyDown={handleSearchboxKeyDown} className=&quot;block w-full py-1.5 pl-10 pr-4 leading-normal rounded-2xl focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500 ring-opacity-90 bg-gray-100 dark:bg-gray-800 text-gray-400 aa-input&quot; placeholder=&quot;Search&quot;/&amp;gt;
          &amp;lt;button type=&quot;submit&quot; onClick={handleSearchClick} className=&quot;absolute top-0 right-0 p-2.5 text-sm font-medium h-full text-blue-500 focus:outline-none&quot;&amp;gt;
            &amp;lt;svg className=&quot;w-4 h-4&quot; aria-hidden=&quot;true&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; fill=&quot;none&quot; viewBox=&quot;0 0 20 20&quot;&amp;gt;
              &amp;lt;path stroke=&quot;currentColor&quot; strokeLinecap=&quot;round&quot; strokeLinejoin=&quot;round&quot; strokeWidth=&quot;2&quot; d=&quot;m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z&quot;/&amp;gt;
            &amp;lt;/svg&amp;gt;
            &amp;lt;span className=&quot;sr-only&quot;&amp;gt;Search&amp;lt;/span&amp;gt;
          &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    )
}

export default SearchTextComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update ProductListingComponent to include searchbox&lt;/h2&gt;
&lt;p&gt;Update ProductListingComponent.tsx to look like this&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { FC, useEffect, useState } from &#39;react&#39;
import { useQuery } from &#39;@apollo/client&#39;

import { graphql } from &#39;@/graphql&#39;
import { GenericProductOrderByInput, NumberFacet, OrderBy, Ranking, StringFacet } from &#39;@/graphql/graphql&#39;
import TermFacetComponent from &#39;./TermFacetComponent&#39;
import SearchTextComponent from &#39;./SearchTextComponent&#39;
import GenericProductTeaserComponent from &#39;./GenericProductTeaserComponent&#39;
import OrderByComponent from &#39;./OrderByCompontent&#39;
import RangeFacetComponent from &#39;./RangeFacetComponent&#39;
import ProductDetailComponent from &#39;./ProductDetailComponent&#39;
 
export const ProductListing = graphql(/* GraphQL */ `
    query ProductListing(
        $languages: [Locales] = en
        $searchText: String,
        $brands: [String!],
        $sizes: [String!],
        $colors: [String!],
        $minPrice: Float,
        $maxPrice: Float,
        $skip: Int = 0,
        $limit: Int = 10,
        $order: GenericProductOrderByInput = {       
            _ranking: SEMANTIC,
            DefaultMarketPrice: ASC 
        }
        )
        {
        GenericProduct(
            locale: $languages
            where:{
                _or:[
                    {
					    _fulltext: {
                            match: $searchText
                        }
                    },
                    {
						Name: {
                            match: $searchText
                            boost: 20
                        }
                    }
                ]
                DefaultMarketPrice: {
                    gte: $minPrice
                    lte: $maxPrice
                }
            }
            skip: $skip,
            limit: $limit
            orderBy: $order
        ) {
            total
            items {
                ...GenericProductTeaser
            }
            facets {
                Brand(filters: $brands) {
                    name
                    count
                }
                Sizes(filters:$sizes) {
                    name
                    count
                }
                Colors(filters:$colors) {
                    name
                    count
                }
                DefaultMarketPrice(ranges: [
                    { to: 50 },
                    { from: 51, to: 100 },
                    { from: 101, to: 150 },
                    { from: 151, to: 200 },
                    { from: 201, to: 250 },
                    { from: 251, to: 300 },
                    { from: 301, to: 350 },
                    { from: 351, to: 400 },
                    { from: 401, to: 450 },
                    { from: 451, to: 500 },
                    { from: 501 },
                ]) {
                    name
                    count
                }
            }
        }
    }
`)
 
const ProductListingComponent: FC = () =&amp;gt; {

    const [searchText, setSearchText] = useState(() =&amp;gt; &#39;&#39;);
    const [orderByInput, setOrderByInput] = useState(() =&amp;gt; &#39;DefaultMarketPrice&#39;);
    const [orderByDirection, setOrderByDirection] = useState(() =&amp;gt; OrderBy.Asc);

    const [brands, setBrands] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [brandFacet, setBrandFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [colors, setColors] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [colorFacet, setColorFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [sizes, setSizes] = useState(() =&amp;gt; new Array&amp;lt;string&amp;gt;());
    const [sizeFacet, setSizeFacet] = useState(() =&amp;gt; new Array&amp;lt;StringFacet&amp;gt;())

    const [lowPrice, setLowPrice] = useState(() =&amp;gt; 0);
    const [highPrice, setHighPrice] = useState(() =&amp;gt; 600)

    const [showModal, setShowModal] = useState(false);
    const [selectedCode, setSelectedCode] = useState(() =&amp;gt; &#39;&#39;);

    const { data } = useQuery(ProductListing, { 
        variables: { 
            searchText, 
            brands, 
            colors, 
            sizes,
            minPrice: lowPrice,
            maxPrice: highPrice,
            order: getOrder()
        } 
    })

    function getOrder(): GenericProductOrderByInput {
        if(orderByInput === &quot;Name&quot;) {
            return { Name: orderByDirection }
        } else if (orderByInput === &quot;Brand&quot;) {
            return { Brand: orderByDirection }
        } else if(orderByInput === &quot;DefaultMarketPrice&quot;) {
            return { DefaultMarketPrice: orderByDirection }
        } else {
            return { _ranking: Ranking.Semantic }
        }
    }

    function facetOptionChanged(fasetQueryResult: StringFacet[], faset: StringFacet[]): boolean {
        if(fasetQueryResult.length != faset.length) {
            return true
        }

        for (let i = 0; i &amp;lt; fasetQueryResult.length; i++) {
            if(fasetQueryResult[i]?.name !== faset[i]?.name) {
                return true
            }

            if(fasetQueryResult[i]?.count !== faset[i]?.count) {
                return true
            }
        }

        return false
    }

    useEffect(() =&amp;gt; {
        if(data?.GenericProduct?.facets?.Brand != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Brand) {
          if(facetOptionChanged(data?.GenericProduct?.facets?.Brand as StringFacet[], brandFacet)) {
            setBrandFacet(data.GenericProduct.facets?.Brand as StringFacet[])
          }
        }

        if(data?.GenericProduct?.facets?.Colors != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Colors) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Colors as StringFacet[], colorFacet)) {
              setColorFacet(data.GenericProduct.facets?.Colors as StringFacet[])
            }
        }

        if(data?.GenericProduct?.facets?.Sizes != undefined &amp;amp;&amp;amp; data?.GenericProduct?.facets?.Sizes) {
            if(facetOptionChanged(data?.GenericProduct?.facets?.Sizes as StringFacet[], sizeFacet)) {
              setSizeFacet(data.GenericProduct.facets?.Sizes as StringFacet[])
            }
        }
      }, [brandFacet, colorFacet, sizeFacet, data?.GenericProduct?.facets]);

    return (
        &amp;lt;main&amp;gt;
            &amp;lt;div className=&quot;flex&quot;&amp;gt;
            &amp;lt;div className=&quot;relative hidden lg:block w-80&quot;&amp;gt;
                &amp;lt;div className=&quot;h-full rounded-2xl ml-4 bg-slate-50&quot;&amp;gt;
                    
                    &amp;lt;nav className=&quot;mt-2 ml-4 mr-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;RangeFacetComponent
                                headingText=&#39;Price range&#39;
                                currentHighValue={highPrice}
                                setHighValue={setHighPrice}
                                setLowValue={setLowPrice}
                                currentLowValue={lowPrice}
                                minValue={0}
                                maxValue={600}
                                facet={data?.GenericProduct?.facets?.DefaultMarketPrice as NumberFacet[]}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;TermFacetComponent
                                headingText=&#39;Brands&#39;
                                facet={brandFacet}
                                values={brands}
                                setValues={setBrands}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;TermFacetComponent
                                headingText=&#39;Colors&#39;
                                facet={colorFacet}
                                values={colors}
                                setValues={setColors}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;

                    &amp;lt;nav className=&quot;mt-2 ml-4&quot;&amp;gt;
                        &amp;lt;div&amp;gt;
                            &amp;lt;TermFacetComponent
                                headingText=&#39;Sizes&#39;
                                facet={sizeFacet}
                                values={sizes}
                                setValues={setSizes}
                            /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/nav&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            
            &amp;lt;div className=&quot;w-full pl-0 md:p-2&quot;&amp;gt;
                &amp;lt;header className=&quot;z-40 items-center w-full h-16 shadow-lg rounded-2xl bg-slate-50&quot;&amp;gt;
                    &amp;lt;div className=&quot;relative z-20 flex flex-col justify-center h-full px-3 mx-auto flex-center&quot;  style={{ background: &quot;radial-gradient(141.61% 141.61% at 29.14% -11.49%, rgba(203, 213, 225, 0.15) 0%, rgba(203, 213, 225, 0) 57.72%)&quot;}}&amp;gt;
                        &amp;lt;div className=&quot;relative flex items-center w-full pl-1 lg:max-w-68 sm:pr-2 sm:ml-0 &quot;&amp;gt;
                            &amp;lt;SearchTextComponent 
                                searchText={searchText}
                                setSearchText={setSearchText}
                             /&amp;gt;
                            &amp;lt;div className=&#39;mt-2 ml-4&#39;&amp;gt;
                                &amp;lt;OrderByComponent 
                                    orderBy={orderByInput} 
                                    setorderBy={setOrderByInput}
                                    orderByDirection={orderByDirection}
                                    setorderByDirection={setOrderByDirection}
                                /&amp;gt;
                            &amp;lt;/div&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/header&amp;gt;
                &amp;lt;main role=&quot;main&quot; className=&quot;w-full h-full flex-grow p-3 overflow-auto&quot;&amp;gt;
                    &amp;lt;div className=&quot;tracking-widest text-lg title-font font-medium  mb-1&quot;&amp;gt;Hits: { data?.GenericProduct?.total }&amp;lt;/div&amp;gt;  
                    
                    &amp;lt;div className=&quot;custom-screen &quot;&amp;gt;
                        &amp;lt;div className=&quot;mt-12&quot;&amp;gt;
                            &amp;lt;ul className=&quot;grid grid-cols-3 gap-10&quot;&amp;gt;
                                { data?.GenericProduct?.items?.map((item, index) =&amp;gt; {
                                    return &amp;lt;GenericProductTeaserComponent 
                                        key={index} 
                                        GenericProductTeaser={item!}
                                        setSelectedCode={setSelectedCode}
                                        setShowModal={setShowModal}
                                        /&amp;gt;
                                })}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/div&amp;gt;
                 &amp;lt;/main&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            { 
                showModal ? (
                    &amp;lt;ProductDetailComponent
                        setOpen={setShowModal}
                        code={selectedCode}
                    /&amp;gt;
                ) : null
            }
        &amp;lt;/main&amp;gt;
    )
}
 
export default ProductListingComponent&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Test search&lt;/h2&gt;
&lt;p&gt;Search for &quot;shoes&quot; and select &quot;Semantic&quot; in the &quot;order by&quot; dropdown&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2024/7/product-listing-page---using-graph/</guid>            <pubDate>Fri, 05 Jul 2024 12:21:40 GMT</pubDate>           <category>Blog post</category></item><item> <title>Making facets work as they should</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2023/3/making-facets-work-as-they-should/</link>            <description>&lt;p&gt;&quot;Faceted navigation&quot; is a powerful way of enabling structured search functionality in our applications. Many search services forces you to make multiple queries to get content result and &quot;correct&quot; facets result. We have now fixed this in Content Graph, so you only have to make one simple query to get both item result and &quot;correct&quot; facet result.&lt;/p&gt;
&lt;h2&gt;Facet filters&lt;/h2&gt;
&lt;p&gt;Facet filters is a new functionality in Content Graph, which filter content items and the facet filters in the &quot;correct way&quot;. The easiest way to explain how it works is by an example.&lt;/p&gt;
&lt;p&gt;Below is a query for &quot;location items&quot; on &quot;Foundation CMS&quot; site, which has one facet for &quot;continents&quot;, and one facet for &quot;countries&quot;. Each of the facets have a parameter of type [String!], where values will filter the item result and other facets.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1f480bed193949ffbbe51749d99a155e.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The result on our example application for Foundation CMS will look like this when not selecting any continents or countries:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/871a561a50454e5b9d9a8db18377af0a.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Adding &quot;Europe&quot; and &quot;Asia&quot; for facet filtering of Continents will filter items to only get european and asian cities.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/758e2215e53841ffb1ea430ac97415df.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The countries will also be filtered to only show countries in europa and asia. The continents facet will still contain all values, to make it possible for user to see how many other continents that exist.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1b23db6862a1417eabb19611c22c7425.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The example of selecting &quot;Europe&quot; and &quot;Asia&quot; looks like this in the example application:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/8fbd2d06dc134f49b08315affc0ef9c0.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We will finally add filtering for countries as will, which will filtering items based on combination of countries and continents.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/a4ba1e36148749ab861e0e30cee2c82f.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The facets will now return the matching items based on values for the other facet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/6c75abe273d643d2b292d82a993af3c5.aspx&quot; /&gt;The result looks like the following in the example application:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/e64babd1148840e0925028d914699106.aspx&quot; /&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2023/3/making-facets-work-as-they-should/</guid>            <pubDate>Mon, 13 Mar 2023 16:48:45 GMT</pubDate>           <category>Blog post</category></item><item> <title>Planned breaking change for Locale parameter in Content Graph</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2023/1/planned-breaking-change-for-locale-parameter-in-content-graph/</link>            <description>&lt;p&gt;Content Graph, a new service which makes it possible to query content using GraphQL, will have a breaking change soon. The service is currently in public beta, which still makes it possible for us to make breaking changes to improve the API. We will change the behaviour for the &quot;locale&quot; parameter, to make it more valuable and easy to understand.&lt;/p&gt;
&lt;h2&gt;Locale&lt;/h2&gt;
&lt;p&gt;The &quot;locale&quot; parameter are used for selecting which languages to query. The parameter type is an array, which makes it possible to query for many languages at the same time. The problem with the current implementation is that you can select a fixed number of languages in the query, which doesn&#39;t reflect the languages you have in your CMS.&lt;/p&gt;
&lt;p&gt;We will soon change the implementation, to instead dynamically create an enum that reflects the CMS languages that has been selected in your CMS. This makes it possible to differentiate between for example en-Gb and en-US, in case you have multiple English versions of your pages. The only difference in language name is that we are using _ instead of - when selecting a culture specific language. en-GB becomes en_GB in Content Graph and en-US becomes en_US.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;query MyQuery {
  StartPage(locale: en_GB) {
    items {
      Name
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/45766c0deeab4b4495705cb459e2e955.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Read more about the update here: &lt;a href=&quot;https://docs.developers.optimizely.com/digital-experience-platform/v1.4.0-content-graph/docs/locale&quot;&gt;https://docs.developers.optimizely.com/digital-experience-platform/v1.4.0-content-graph/docs/locale&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2023/1/planned-breaking-change-for-locale-parameter-in-content-graph/</guid>            <pubDate>Mon, 23 Jan 2023 08:58:17 GMT</pubDate>           <category>Blog post</category></item><item> <title>Strongly typed CMS content with TypeScript &amp; React</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2022/9/strongly-typed-cms-content-with-typescript--react3/</link>            <description>&lt;p&gt;Its now easy to create applications with strongly typed CMS content, using any language of you choice, and host it anywhere you like. You can try out an example account we have created using the following tutorial: &lt;a href=&quot;https://docs.developers.optimizely.com/digital-experience-platform/v1.4.0-content-graph/docs/tutorial-create-a-site-with-strongly-typed-music-festival-content-using-react-and-content-graph&quot;&gt;https://docs.developers.optimizely.com/digital-experience-platform/v1.4.0-content-graph/docs/tutorial-create-a-site-with-strongly-typed-music-festival-content-using-react-and-content-graph&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Content Graph&lt;/h2&gt;
&lt;p&gt;Content Graph is the service which enables this functionality. Content Graph is a &quot;versionless&quot;, stand alone service, hosted in the CDN. Here are some benifits of using Content Graph when creating your application&lt;/p&gt;
&lt;h3&gt;Work with strongly typed content in your decoupled application&lt;/h3&gt;
&lt;p&gt;You can auto-generate classes/types for most existing languages, just by specifying the url to Content Graphs GraphQL endpoint together with your authentication key. This will enable you to work strongly typed against your content types, even if you haven&#39;t specified the content types in your decoupled application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/fc553ce8ea2c414d8a6a1a3478e01ba9.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Following is an example where the developer used a property doesn&#39;t exist on the content type and got a suggestion by compiler.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/214e8d7ad64d4f1f8f909dddb16963ac.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Seperation between queries and components&lt;/h3&gt;
&lt;p&gt;Content Graph makes it easy to separate your queries from the components. Add your queries in a special folder, and run &quot;yarn generate&quot; to generate classes/types for both queries and content types from the schema.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/15c540044339475ea554b882f507bb13.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Write your queries in Visual Studio Code for your decoupled application, with intellisense and validation&lt;/h3&gt;
&lt;p&gt;You can write your queries with intellisense directly in Visual Studio Code for your decoupled application. You just have to specify the url to Content Graphs GraphQL API together with the authentication key, and you will be able to quickly write your queries.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/de9c5f19ee034507b1988a5a40023bb2.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Following is an example where the query was not created correctly. Content Graph gave a suggestion for fixing the query:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;img src=&quot;/link/7a586e5e94824ef5befc55449b3b79aa.aspx&quot; /&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Query children of result in several layers - all in one query&lt;/h3&gt;
&lt;p&gt;Following example will, in case the result from main query is of type &quot;LandingPage&quot;, return some properties from LandingPage. It will also get children of type ArtistContainerPage with &quot;Name&quot; and &quot;RelativePath&quot;. It will finally get children for the returned ArtistContainerPages of type ArtistDetailPage, where &quot;ArtistHeadliner&quot; is true for the ArtistDetailPages. All in one query.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/6f8ed8e147654c6a83df815d97a6bbca.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Get items in Content Areas recursively&lt;/h3&gt;
&lt;p&gt;A block type can contain a ContentArea property, where other blocks can be added. An instance of the block can then be placed in another ContentArea property, for example an ContentArea on a page. This creates a recursively Content Area item fetching, which can be problematic for many services.&lt;/p&gt;
&lt;p&gt;Content Graph supports retrieving items in ContentArea properties recursivly - in several levels. Another important feature in Content Graph is that you must specify which properties to fetch for the different content types that could exist in the ContentArea property. This will reduce the data size send from the service to your application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/efe466075b85407e86dff87889aa40a1.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/54fb947e30d0400697508262c757e7ce.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Search capabilities directly in the service&lt;/h3&gt;
&lt;p&gt;We have implemented free text search support in Content Graph, with stemming and decompounding support. We have also added support for facets of strings and numbers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/3f6fb822f3a84471bafb97d3670b0030.aspx&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Online IDE&lt;/h3&gt;
&lt;p&gt;An online IDE are available using &lt;a href=&quot;https://cg.optimizely.com/app/graphiql?auth={yourkey}&quot;&gt;https://cg.optimizely.com/app/graphiql?auth={yourkey}&lt;/a&gt;. You can use the example account we have created for Music Festival: &lt;a href=&quot;https://cg.optimizely.com/app/graphiql?auth=XoXsOl2bScWZzguDUylvaaZ5PEAAH5tg5qme0NebCHzEpyO4&quot;&gt;https://cg.optimizely.com/app/graphiql?auth=XoXsOl2bScWZzguDUylvaaZ5PEAAH5tg5qme0NebCHzEpyO4&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2022/9/strongly-typed-cms-content-with-typescript--react3/</guid>            <pubDate>Fri, 02 Sep 2022 14:32:57 GMT</pubDate>           <category>Blog post</category></item><item> <title>Find 13: New language routing</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2018/4/find-13-new-language-routing/</link>            <description>&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;p&gt;In order to optimize indexing performance, and increase query precision, indexing documents should be done by specifying language routing. The use of language routing, when indexing, does not require any changes when querying. The only effect language routing has on querying is that it increases query precision. This is done&amp;nbsp;by reducing the number of false positive matches, as documents only are analyzed for a specific language. Previously, all documents were analyzed for all available languages supported, sometimes causing noise when stemming rules for one language collided with stemming rules for another.&lt;/p&gt;
&lt;h2&gt;Adding language routing on any type of object&lt;/h2&gt;
&lt;p&gt;Adding language routing for any type of object can be done in different ways.&lt;/p&gt;
&lt;h3&gt;LanguageRoutingAttribute&lt;/h3&gt;
&lt;p&gt;One option is to use LanguageRoutingAttribute on the property, and set it to the desired language when initializing the object.&lt;/p&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class WithLanguageRoutingAttribute
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [Id] 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public string Id { get; set; } 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [LanguageRouting] 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public LanguageRouting LanguageRouting { get; set; } 
} &lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var indexedObject = new WithLanguageRoutingAttribute()

{ 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Id = &quot;123&quot;, 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LanguageRouting = new LanguageRouting(Language.Swedish) 
}; &lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Conventions&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Another option is to use the conventions API in Find.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;client.Conventions.ForInstancesOf&amp;lt;WithLanguageRouting&amp;gt;().LanguageRoutingIs(x =&amp;gt; x.LanguageRouting);&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CMS integration&lt;/h2&gt;
&lt;p&gt;The Find CMS integration automatically adds language routing support for content implementing ILocale. Most content base type, like PageData, ProductContent, NodeContent, and VariationContent, all implements ILocale. The behavior can be changed by either using conventions, or by overriding LanguageRoutingFactory.&lt;/p&gt;
&lt;h3&gt;Conventions&lt;/h3&gt;
&lt;p&gt;The following example changes the behavior for all content implementing ILocale, to store the documents in the old way. It’s not recommended to do this, because it prevents the performance improvements.&lt;/p&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;client.Conventions.ForInstancesOf&amp;lt;ILocale&amp;gt;().LanguageRoutingIs(x =&amp;gt; null);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This example changes the behavior only for MediaData. You might want to do this for some specific content types, if it’s necessary to analyze the content using all analyzers.&lt;/p&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;client.Conventions.ForInstancesOf&amp;lt;MediaData&amp;gt;().LanguageRoutingIs(x =&amp;gt; null);&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;LanguageRoutingFactory&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A LanguageRoutingFactory is, by default, used for all ILocale content, to create language routing for content that implements ILocale. It’s possible to change the default behavior by creating your own factory that inherits from LanguageRoutingFactory, and override any of the protected virtual methods. You also need to register your language routing factory using a configurable module.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[InitializableModule]
[ModuleDependency(typeof(IndexingModule))]
public class MyFindInitializationModule : IConfigurableModule
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Initialize(InitializationEngine context)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void Uninitialize(InitializationEngine context)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void ConfigureContainer(ServiceConfigurationContext context)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; context.Services.AddSingleton&amp;lt;LanguageRoutingFactory, MyLanguageRoutingFactory&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;The following example changes the behaviour, to use all analyzers instead of the “standard” analyzer, for all content which uses a language we can’t map to any analyzer.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;4bed3df3-01f7-45de-af8f-949f7c00fe53&quot; class=&quot;postBody&quot;&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class MyLanguageRoutingFactory : LanguageRoutingFactory
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public override LanguageRouting CreateLanguageRouting(ILocale locale)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var languageRouting = base.CreateLanguageRouting(locale);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (languageRouting.FieldSuffix == Language.None.FieldSuffix)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return languageRouting;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Reindex the site after upgrade to Find 13&lt;/h2&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The changes in the language routing make it necessary to reindex the site after upgrading to Find 13. The site will work directly when you start the site, before the content has been reindexed. It will work because queries will still hit the content that hasn’t been indexed using the language routing.&lt;/p&gt;
&lt;p&gt;The problem (if site isn’t reindexed) will occur when content is reindexed after being triggered from a content event. The content might be stored in a different way as before, which might make the old content continue to exist. You can end up having the same content stored twice, but in different versions.&lt;/p&gt;
&lt;p&gt;Conclusion: A&lt;span&gt;fter upgrading to Find 13&lt;/span&gt;, reindex the whole site as soon as possible.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2018/4/find-13-new-language-routing/</guid>            <pubDate>Tue, 03 Apr 2018 16:22:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Using Personalized Find</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2017/10/using-personalized-find/</link>            <description>&lt;p&gt;Episerver recently released Personalized Find (PF), a new product that delivers improved relevancy to visitors thereby helping to increase conversions on your website. Specifically, when used with Episerver Commerce, PF uses each individual&#39;s browsing and purchase activity to boost search results.&lt;/p&gt;
&lt;p&gt;Without personalization, everyone gets the same Find experience. Yet every visitor is different and is looking for different things on your website. Moreover, each user&#39;s behavior changes depending on their wants and needs at that time.&lt;/p&gt;
&lt;h2&gt;How&amp;nbsp;Personalized Find works&lt;/h2&gt;
&lt;p&gt;Every click and action on a website is used to build a visitor&#39;s profile. For example&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;traffic source&lt;/li&gt;
&lt;li&gt;search query&lt;/li&gt;
&lt;li&gt;categories visited&lt;/li&gt;
&lt;li&gt;selected facets: color, size, brand, and so on&amp;nbsp;&lt;/li&gt;
&lt;li&gt;use of ratings and reviews&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This data is captured, analyzed then visualized to determine a personalized search relevancy. The relevancy logic&amp;nbsp;for each individual is based on everything PF knows about the visitor: preferences, previous purchases, current behavioral interactions, and so on.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Ranking and weighting can also consider a merchandiser&#39;s business rules or strategies. So, best-selling items, high conversion rates, most revenue, or most units sold can be used to affect search results. The result of this processing is an optimal ordering of search results for every user.&lt;/p&gt;
&lt;h2&gt;Benefits of Personalized Find&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Increased sales&lt;/strong&gt;. Optimizing the search experience has been shown to produced increased revenue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better engagement&lt;/strong&gt;. Improved experience encourages customers to keep shopping.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better conversion rates&lt;/strong&gt;. Easier for customers to find products they are looking for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better retention&lt;/strong&gt;. Customers who have a positive experience are more likely to return to your site.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Using&amp;nbsp;Personalized Find on your site&lt;/h2&gt;
&lt;p&gt;The Developer Guider article &quot;&lt;a href=&quot;/link/17595d656a2a46c5834814ee14d5f1fd.aspx&quot; target=&quot;_blank&quot;&gt;Using Personalized Find&lt;/a&gt;&quot; provides the technical details about using PF. This post provides a quick introduction to a few PF functions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fetching site visitor information.&amp;nbsp;&lt;br /&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;using EPiServer.Find.Personalization;

namespace EpiserverSite.Controllers
{
    public class MyController : PageController&amp;lt;MyPage&amp;gt;
    {
        private readonly IClient _client;

        public MyController(IClient client)
        {
            _client = client;
        }

        public ActionResult Index(MyPage currentPage)
        {
           &lt;span style=&quot;color: red;&quot;&gt;_client.Personalization().Refresh();&lt;/span&gt;
           return View(currentPage);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Implementing &lt;/span&gt;&lt;strong&gt;UsingPersonalization()&lt;/strong&gt; to &lt;span&gt;boost a query with site visitor information.&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var result = _client.Search&amp;lt;FashionProduct&amp;gt;()
     .For(&quot;ferrari&quot;)
     &lt;span style=&quot;color: red;&quot;&gt;.UsingPersonalization() &lt;/span&gt;
     .GetResult();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span class=&quot;informationBox&quot;&gt;Note: At this time, site visitor information is only captured for Commerce content. While you can use &lt;strong&gt;.UsingPersonalization()&lt;/strong&gt; on any query, it only boosts Commerce content properties.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Example of implementation&lt;/h2&gt;
&lt;p&gt;Here is an example, where a user has viewed several expensive puma products. The developers has created a recommendation ordering view, to help the visitors.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;/link/4f108b32f3c4455eabc26de687708d3c.aspx&quot; height=&quot;728&quot; width=&quot;1290&quot; /&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2017/10/using-personalized-find/</guid>            <pubDate>Fri, 20 Oct 2017 16:36:43 GMT</pubDate>           <category>Blog post</category></item><item> <title>Exceptions in Find</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/12/exceptions-in-find/</link>            <description>&lt;div class=&quot;post&quot;&gt;
&lt;div class=&quot;body&quot;&gt;
&lt;div id=&quot;ead064bc-6bf5-4e03-a1f4-69258f5a14a8&quot; class=&quot;postBody&quot;&gt;
&lt;p&gt;Episerver Find can be used in many places on a site, like menus, navigations, listings, and searches. One thing that usually are forgotten when using Find is that it&amp;rsquo;s a service in the &amp;ldquo;cloud&amp;rdquo;, where you should handle possible exceptions.&lt;/p&gt;
&lt;h2&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.ServiceException&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;The &amp;ldquo;ServiceException&amp;rdquo; is the most important exception that should be handled in every place where Find is used. This exception will be thrown if the service is down, or if there is any other communication problem.&lt;/p&gt;
&lt;p&gt;This exception inherits from ApplicationException.&lt;/p&gt;
&lt;h2&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.ClientException&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;The &amp;ldquo;ClientException&amp;rdquo; can be thrown when the query contains errors that is found at runtime. There is also several specific exceptions that inherit from &amp;ldquo;ClientException&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;This exception inherits from ApplicationException.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.SelfReferencingLoopException&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;SelfReferencingLoopException&amp;rdquo; will be thrown if a circular reference is found at runtime. There can for example exist a &amp;ldquo;Parent&amp;rdquo; property on a class, where the class of the parent property has a reference to the current class.&lt;/p&gt;
&lt;p&gt;This exception inherits from ClientException.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.ProjectionException&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;ProjectionException&amp;rdquo; will be thrown if the projection (Select) is used in the wrong way in the query.&lt;/p&gt;
&lt;p&gt;This exception inherits from ClientException.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.Api.InvalidIndexIdException&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;InvalidIndexIdException&amp;rdquo; will be thrown if the id of the index is null, or over 100 characters.&lt;/p&gt;
&lt;p&gt;This exception inherits from ClientException.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.Api.InvalidDocumentIdException&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;InvalidDocumentIdException&amp;rdquo; will be thrown if the id of the document is null, or over 100 characters.&lt;/p&gt;
&lt;p&gt;This exception inherits from ClientException.&lt;/p&gt;
&lt;h3&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;EPiServer.Find.Api.InvalidSearchRequestException&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;The &amp;ldquo;InvalidSearchRequestException&amp;rdquo; will be thrown if the terms facet has been set to a value less than 0, or higher than 1000.&lt;/p&gt;
&lt;p&gt;This exception inherits from ClientException.&lt;/p&gt;
&lt;h2&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;Conclusion&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Handle &amp;ldquo;EPiServer.Find.ServiceException&amp;rdquo; and &amp;ldquo;EPiServer.Find.ClientException&amp;rdquo; on all places where a query is created and executed against Find.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/12/exceptions-in-find/</guid>            <pubDate>Thu, 08 Dec 2016 08:55:34 GMT</pubDate>           <category>Blog post</category></item><item> <title>Automatic landing page in Quicksilver using the search provider system</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/1/automatic-landing-page-in-quicksilver-using-the-search-provider-system/</link>            <description>&lt;p&gt;&lt;a href=&quot;/link/1090adf249a444cf8b34c71b837cc44c.aspx&quot;&gt;My last blog post&lt;/a&gt;&amp;nbsp;was an overview over &amp;ldquo;automatic landing pages&amp;rdquo;. This blog post will show how we can implement this on Quicksilver, using the search provider system. We will change the current facet navigation in Quicksilver to instead use url-based facet navigation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/d6855e09047a49979d4e464a433cb0d2.aspx&quot; width=&quot;1045&quot; alt=&quot;Image greenShoes.PNG&quot; height=&quot;665&quot; /&gt;&#39;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/00d8758c23ca48fb84d6d46046e0c7f1.aspx&quot; width=&quot;1051&quot; alt=&quot;Image greenShoesHtml.PNG&quot; height=&quot;790&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hackday project&lt;/h2&gt;
&lt;p&gt;This is a hackday project, which was very quickly developed. There is alot of improvement possibilities that I see now directly when I&#39;m writing this blog post. You can easily&amp;nbsp;take the code and prettify it.&lt;/p&gt;
&lt;h2&gt;Adding nuget package or copy files from github&lt;/h2&gt;
&lt;p&gt;We will start with adding the nuget package &amp;ldquo;brilliantcut.automaticlandingpage&amp;rdquo; to quicksilver. The easiest way to install the packages are to create a new package source in visual studio that points to &quot;http://nuget.jobe.employee.episerver.com/&quot; (https://docs.nuget.org/consume/Package-Manager-Dialog). Now it&#39;s possible to install the packages by writing &quot;install-package brilliantcut.automaticlandingpage&quot; in the &quot;Package manager console&quot;.&lt;/p&gt;
&lt;p&gt;Another alternative to install the nuget package is to copy the c# classes from github:&amp;nbsp;&lt;a href=&quot;https://github.com/jonasbergqvist/BrilliantCut.AutomaticLandingPage&quot;&gt;https://github.com/jonasbergqvist/BrilliantCut.AutomaticLandingPage&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we have some nice classes that we can use to change the current facet navigation on the site.&lt;/p&gt;
&lt;h2&gt;Disable the default partial route&lt;/h2&gt;
&lt;p&gt;We will disable the default partial route in &amp;ldquo;SiteInitialization.cs&amp;rdquo;. The following line should be removed in the class: CatalogRouteHelper.MapDefaultHierarchialRouter(RouteTable.Routes, false);&lt;/p&gt;
&lt;h2&gt;New feature folder&lt;/h2&gt;
&lt;p&gt;Create a new folder under the &amp;ldquo;Features&amp;rdquo; folder called &amp;ldquo;AutomaticLandingPage&amp;rdquo;.&lt;/p&gt;
&lt;h2&gt;Configurable module&lt;/h2&gt;
&lt;p&gt;Create a new configurable module by creating a new class that implements &amp;ldquo;IConfigurableModule&amp;rdquo; in the new folder. Set a module dependency to the site initialization by adding the following attribute on the class: [ModuleDependency(typeof(SiteInitialization))].&lt;/p&gt;
&lt;p&gt;We will now add a new partial route using the service &amp;ldquo;CatalogContentRouteRegistration&amp;rdquo; in &amp;ldquo;BrilliantCut.AutomatedLandingPage&amp;rdquo;. Add the following line in the initialization method: context.Locate.Advanced.GetInstance&amp;lt;CatalogContentRouteRegistration&amp;gt;().RegisterDefaultRoute();&lt;/p&gt;
&lt;p&gt;Let the &amp;ldquo;ConfigureContainer&amp;rdquo; method be empty for now. We will come back to this soon.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(typeof(SiteInitialization))]
public class AutomaticLandingPageInitializationModule : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
    }

    public void Initialize(InitializationEngine context)
    {
        context.Locate.Advanced.GetInstance&amp;lt;CatalogContentRouteRegistration&amp;gt;().RegisterDefaultRoute();
    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;New FilterOptionFormModelBinder implementation&lt;/h2&gt;
&lt;p&gt;The default FilterOptionFormModelBinder in Quicksilver needs to be overridden to make the automated landing page work. We will create a class called &amp;ldquo;AutomaticLandingPageFilterOptionFormModelBinder&amp;rdquo;, which will get the values caught in the partial route, and add those to the FilterOptionFormModel:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class AutomaticLandingPageFilterOptionFormModelBinder : FilterOptionFormModelBinder
{
    private readonly DefaultModelBinder _defaultModelBinder;
    private readonly IContentLoader _contentLoader;

    public AutomaticLandingPageFilterOptionFormModelBinder(IContentLoader contentLoader, LocalizationService localizationService, Func&amp;lt;CultureInfo&amp;gt; preferredCulture, DefaultModelBinder defaultModelBinder) 
        : base(contentLoader, localizationService, preferredCulture)
    {
        _defaultModelBinder = defaultModelBinder;
        _contentLoader = contentLoader;
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = (FilterOptionFormModel)_defaultModelBinder.BindModel(controllerContext, bindingContext);
        if (model == null)
        {
            return model;
        }

        AddFacets(controllerContext, model);

        var contentLink = controllerContext.RequestContext.GetContentLink();
        IContent content = null;
        if (!ContentReference.IsNullOrEmpty(contentLink))
        {
            content = _contentLoader.Get&amp;lt;IContent&amp;gt;(contentLink);
        }

        var query = controllerContext.HttpContext.Request.QueryString[&quot;q&quot;];
        var sort = controllerContext.HttpContext.Request.QueryString[&quot;sort&quot;];
        SetupModel(model, query, sort, null, content);
        return model;
    }

    private static void AddFacets(ControllerContext controllerContext, FilterOptionFormModel model)
    {
        var routeFacets = controllerContext.RouteData.Values[FacetUrlService.RouteFacets] as ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;;
        if (routeFacets != null)
        {
            model.FacetGroups = new List&amp;lt;FacetGroupOption&amp;gt;();
            foreach (var routeFacet in routeFacets.Keys)
            {
                var facetGroupOption = new FacetGroupOption
                {
                    GroupName = routeFacet.FacetName,
                    GroupFieldName = routeFacet.FacetPath,
                    Facets = new List&amp;lt;FacetOption&amp;gt;()
                };

                foreach (var facetValue in routeFacets[routeFacet].Select(x =&amp;gt; x.ToString()))
                {
                    facetGroupOption.Facets.Add(new FacetOption
                    {
                        Name = facetValue,
                        Key = facetValue,
                        Selected = true
                    });
                }

                model.FacetGroups.Add(facetGroupOption);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update Configurable module&lt;/h2&gt;
&lt;p&gt;The configurable module can now be updated to set the &amp;ldquo;AutomaticLandingPageFilterOptionFormModelBinder&amp;rdquo; as the implementation for &amp;ldquo;FilterOptionFormModelBinder&amp;rdquo;:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(typeof(SiteInitialization))]
public class AutomaticLandingPageInitializationModule : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Container.Configure(c =&amp;gt; c.For&amp;lt;FilterOptionFormModelBinder&amp;gt;().Use&amp;lt;AutomaticLandingPageFilterOptionFormModelBinder&amp;gt;());
    }

    public void Initialize(InitializationEngine context)
    {
        context.Locate.Advanced.GetInstance&amp;lt;CatalogContentRouteRegistration&amp;gt;().RegisterDefaultRoute();
    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;_Facet.cshtml&lt;/h2&gt;
&lt;p&gt;We need to make changes to _Facet.cshtml to use the HtmlHelper in BrilliantCut.AutomaticLandingPage. The following line can be used in the &amp;lt;li&amp;gt; tags:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;&amp;lt;a href=&quot;@Html.FacetContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;(), typeof(string).FullName, facetGroup.GroupFieldName, facetGroup.GroupName, facet.Key)&quot;&amp;gt;@facet.Key (@facet.Count)&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The whole file:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@using System.Web.Mvc.Html
@using BrilliantCut.AutomaticLandingPage
@using EPiServer.Commerce.Catalog.ContentTypes
@using EPiServer.Shell.Web.Mvc.Html
@using EPiServer.Web.Routing

@model EPiServer.Reference.Commerce.Site.Features.Search.Models.FilterOptionFormModel
@{
    Layout = null;
}
&amp;lt;div class=&quot;col-sm-3 facets-wrapper jsSearchFacets&quot;&amp;gt;
    @if (Model.FacetGroups.Any(x =&amp;gt; x.Facets.Any(y =&amp;gt; y.Selected)))
    {
        &amp;lt;div class=&quot;well facets-summary product-filtering choices&quot;&amp;gt;
            &amp;lt;h3&amp;gt;@Html.Translate(&quot;/Category/Filters&quot;)&amp;lt;/h3&amp;gt;
            &amp;lt;ul class=&quot;nav&quot;&amp;gt;
                @for (var i = 0; i &amp;lt; Model.FacetGroups.Count; i++)
                {
                    var facetGroup = Model.FacetGroups[i];
                    for (var j = 0; j &amp;lt; facetGroup.Facets.Count; j++)
                    {
                        var facet = facetGroup.Facets[j];
                        if (!facet.Selected)
                        {
                            continue;
                        }
                        &amp;lt;li class=&quot;facet-active&quot;&amp;gt;
                            &amp;lt;a href=&quot;@Html.FacetContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;(), typeof(string).FullName, facetGroup.GroupFieldName, facetGroup.GroupName, facet.Key)&quot;&amp;gt;@facet.Key (@facet.Count)&amp;lt;/a&amp;gt;
                        &amp;lt;/li&amp;gt;
                    }
                }
                &amp;lt;li class=&quot;facets-amount&quot;&amp;gt;
                    @Html.Translate(&quot;/Facet/Choices&quot;) &amp;lt;strong&amp;gt;@Model.TotalCount&amp;lt;/strong&amp;gt;
                &amp;lt;/li&amp;gt;
            &amp;lt;/ul&amp;gt;
            &amp;lt;a href=&quot;@Html.ContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;())&quot;&amp;gt;Remove all&amp;lt;/a&amp;gt;          
        &amp;lt;/div&amp;gt;
    }

    &amp;lt;ul class=&quot;nav&quot;&amp;gt;
        &amp;lt;li&amp;gt;
            &amp;lt;h3&amp;gt;@Html.Translate(&quot;/Category/SortBy&quot;)&amp;lt;/h3&amp;gt;
            @Html.DropDownList(&quot;FormModel.Sort&quot;, Model.Sorting, new { @class = &quot;form-control jsSearchSort&quot; })&amp;lt;br /&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    @for (var i = 0; i &amp;lt; Model.FacetGroups.Count; i++)
    {
        var facetGroup = Model.FacetGroups[i];
       
        &amp;lt;ul class=&quot;nav facet-group&quot;&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;h3&amp;gt;@facetGroup.GroupName&amp;lt;/h3&amp;gt;
                @Html.TextBox(string.Format(&quot;FormModel.FacetGroups[{0}].GroupFieldName&quot;, i), facetGroup.GroupFieldName, new { @hidden = &quot;true&quot; })
            &amp;lt;/li&amp;gt;
            @for (var j = 0; j &amp;lt; facetGroup.Facets.Count; j++)
            {
                var facet = facetGroup.Facets[j];
                if (facet.Selected)
                {
                    continue;
                }
                &amp;lt;li&amp;gt;
                    &amp;lt;a href=&quot;@Html.FacetContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;(), typeof(string).FullName, facetGroup.GroupFieldName, facetGroup.GroupName, facet.Key)&quot;&amp;gt;@facet.Key (@facet.Count)&amp;lt;/a&amp;gt;
                &amp;lt;/li&amp;gt;
            }
        &amp;lt;/ul&amp;gt;
    }
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The whole implementation&lt;/h2&gt;
&lt;h3&gt;AutomaticLandingPageInitializationModule&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(typeof(SiteInitialization))]
public class AutomaticLandingPageInitializationModule : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        context.Container.Configure(c =&amp;gt; c.For&amp;lt;FilterOptionFormModelBinder&amp;gt;().Use&amp;lt;AutomaticLandingPageFilterOptionFormModelBinder&amp;gt;());
    }

    public void Initialize(InitializationEngine context)
    {
        context.Locate.Advanced.GetInstance&amp;lt;CatalogContentRouteRegistration&amp;gt;().RegisterDefaultRoute();
    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AutomaticLandingPageFilterOptionFormModelBinder&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class AutomaticLandingPageFilterOptionFormModelBinder : FilterOptionFormModelBinder
{
    private readonly DefaultModelBinder _defaultModelBinder;
    private readonly IContentLoader _contentLoader;

    public AutomaticLandingPageFilterOptionFormModelBinder(IContentLoader contentLoader, LocalizationService localizationService, Func&amp;lt;CultureInfo&amp;gt; preferredCulture, DefaultModelBinder defaultModelBinder) 
        : base(contentLoader, localizationService, preferredCulture)
    {
        _defaultModelBinder = defaultModelBinder;
        _contentLoader = contentLoader;
    }

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = (FilterOptionFormModel)_defaultModelBinder.BindModel(controllerContext, bindingContext);
        if (model == null)
        {
            return model;
        }

        AddFacets(controllerContext, model);

        var contentLink = controllerContext.RequestContext.GetContentLink();
        IContent content = null;
        if (!ContentReference.IsNullOrEmpty(contentLink))
        {
            content = _contentLoader.Get&amp;lt;IContent&amp;gt;(contentLink);
        }

        var query = controllerContext.HttpContext.Request.QueryString[&quot;q&quot;];
        var sort = controllerContext.HttpContext.Request.QueryString[&quot;sort&quot;];
        SetupModel(model, query, sort, null, content);
        return model;
    }

    private static void AddFacets(ControllerContext controllerContext, FilterOptionFormModel model)
    {
        var routeFacets = controllerContext.RouteData.Values[FacetUrlService.RouteFacets] as ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;;
        if (routeFacets != null)
        {
            model.FacetGroups = new List&amp;lt;FacetGroupOption&amp;gt;();
            foreach (var routeFacet in routeFacets.Keys)
            {
                var facetGroupOption = new FacetGroupOption
                {
                    GroupName = routeFacet.FacetName,
                    GroupFieldName = routeFacet.FacetPath,
                    Facets = new List&amp;lt;FacetOption&amp;gt;()
                };

                foreach (var facetValue in routeFacets[routeFacet].Select(x =&amp;gt; x.ToString()))
                {
                    facetGroupOption.Facets.Add(new FacetOption
                    {
                        Name = facetValue,
                        Key = facetValue,
                        Selected = true
                    });
                }

                model.FacetGroups.Add(facetGroupOption);
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;_Facet.cshtml&lt;/h3&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;@using System.Web.Mvc.Html
@using BrilliantCut.AutomaticLandingPage
@using EPiServer.Commerce.Catalog.ContentTypes
@using EPiServer.Shell.Web.Mvc.Html
@using EPiServer.Web.Routing

@model EPiServer.Reference.Commerce.Site.Features.Search.Models.FilterOptionFormModel
@{
    Layout = null;
}
&amp;lt;div class=&quot;col-sm-3 facets-wrapper jsSearchFacets&quot;&amp;gt;
    @if (Model.FacetGroups.Any(x =&amp;gt; x.Facets.Any(y =&amp;gt; y.Selected)))
    {
        &amp;lt;div class=&quot;well facets-summary product-filtering choices&quot;&amp;gt;
            &amp;lt;h3&amp;gt;@Html.Translate(&quot;/Category/Filters&quot;)&amp;lt;/h3&amp;gt;
            &amp;lt;ul class=&quot;nav&quot;&amp;gt;
                @for (var i = 0; i &amp;lt; Model.FacetGroups.Count; i++)
                {
                    var facetGroup = Model.FacetGroups[i];
                    for (var j = 0; j &amp;lt; facetGroup.Facets.Count; j++)
                    {
                        var facet = facetGroup.Facets[j];
                        if (!facet.Selected)
                        {
                            continue;
                        }
                        &amp;lt;li class=&quot;facet-active&quot;&amp;gt;
                            &amp;lt;a href=&quot;@Html.FacetContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;(), typeof(string).FullName, facetGroup.GroupFieldName, facetGroup.GroupName, facet.Key)&quot;&amp;gt;@facet.Key (@facet.Count)&amp;lt;/a&amp;gt;
                        &amp;lt;/li&amp;gt;
                    }
                }
                &amp;lt;li class=&quot;facets-amount&quot;&amp;gt;
                    @Html.Translate(&quot;/Facet/Choices&quot;) &amp;lt;strong&amp;gt;@Model.TotalCount&amp;lt;/strong&amp;gt;
                &amp;lt;/li&amp;gt;
            &amp;lt;/ul&amp;gt;
            &amp;lt;a href=&quot;@Html.ContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;())&quot;&amp;gt;Remove all&amp;lt;/a&amp;gt;          
        &amp;lt;/div&amp;gt;
    }

    &amp;lt;ul class=&quot;nav&quot;&amp;gt;
        &amp;lt;li&amp;gt;
            &amp;lt;h3&amp;gt;@Html.Translate(&quot;/Category/SortBy&quot;)&amp;lt;/h3&amp;gt;
            @Html.DropDownList(&quot;FormModel.Sort&quot;, Model.Sorting, new { @class = &quot;form-control jsSearchSort&quot; })&amp;lt;br /&amp;gt;
        &amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;

    @for (var i = 0; i &amp;lt; Model.FacetGroups.Count; i++)
    {
        var facetGroup = Model.FacetGroups[i];
       
        &amp;lt;ul class=&quot;nav facet-group&quot;&amp;gt;
            &amp;lt;li&amp;gt;
                &amp;lt;h3&amp;gt;@facetGroup.GroupName&amp;lt;/h3&amp;gt;
                @Html.TextBox(string.Format(&quot;FormModel.FacetGroups[{0}].GroupFieldName&quot;, i), facetGroup.GroupFieldName, new { @hidden = &quot;true&quot; })
            &amp;lt;/li&amp;gt;
            @for (var j = 0; j &amp;lt; facetGroup.Facets.Count; j++)
            {
                var facet = facetGroup.Facets[j];
                if (facet.Selected)
                {
                    continue;
                }
                &amp;lt;li&amp;gt;
                    &amp;lt;a href=&quot;@Html.FacetContentUrl(ViewContext.RequestContext.GetRoutedData&amp;lt;CatalogContentBase&amp;gt;(), typeof(string).FullName, facetGroup.GroupFieldName, facetGroup.GroupName, facet.Key)&quot;&amp;gt;@facet.Key (@facet.Count)&amp;lt;/a&amp;gt;
                &amp;lt;/li&amp;gt;
            }
        &amp;lt;/ul&amp;gt;
    }
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt; &lt;/code&gt;&lt;/pre&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/1/automatic-landing-page-in-quicksilver-using-the-search-provider-system/</guid>            <pubDate>Fri, 08 Jan 2016 07:27:17 GMT</pubDate>           <category>Blog post</category></item><item> <title>Automatic landing page</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/1/automatic-landing-page/</link>            <description>&lt;p&gt;An “Automatic landing page” is content (for example an page type instance) which renders&amp;nbsp;differently depending on user input. A form of personalized content-type. A google hit can, for example, point to an automatic landing page, which shows content related to the google search.&lt;/p&gt;
&lt;p&gt;This is the first of a blog series about automatic landing pages. This first blog post is an overview over what automatic landing pages&amp;nbsp;are about. The next blog will show how you can implement automatic landing pages in Quicksilver (Commerce demo site) with the search provider system. Another one will show how&amp;nbsp;to use native Find in Quicksilver to accomplish the same thing.&lt;/p&gt;
&lt;h2&gt;Partial route&lt;/h2&gt;
&lt;p&gt;The challenge in building an automatic landing page is to make a search engine accept dynamic rendered content as different search hits. That is easiest accomplished by creating a partial route.&lt;/p&gt;
&lt;p&gt;One url will render one unique “page” for one content instance. Different &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; are not supposed to render the same content. A partial route makes it possible to use a nice url instead of query parameters to specify what content that should be rendered on a page. Look at the following example, taken from the Quicksilver implementation, which goes to the same content instance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;http://quicksilver/en/fashion/mens/Category/jackets/Color/blue&lt;/span&gt;: Blue jackets for men.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;http://quicksilver/en/fashion/womens/Category/dresses/Color/black/Size/m&lt;/span&gt;: Black dresses in size medium for women.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A partial route can be used to both create &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; and read incoming requests. I will only use the partial route for reading incoming requests. The rendering of &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; will be done in a separate service. The examples&amp;nbsp;use Episerver Commerce, but it’s possible to make the same functionality for CMS sites as well.&lt;/p&gt;
&lt;p&gt;I will create a class that inherits from &quot;HierarchicalCatalogPartialRouter&quot;, and override &quot;RoutePartial&quot;. This method should first get the catalog content by calling the base method. Then it’s time to figure out when the rest of the url means.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;public class FacetPartialRoute : HierarchicalCatalogPartialRouter
    {
        private readonly FacetUrlService _facetUrlCreator;

        public FacetPartialRoute(Func&amp;lt;ContentReference&amp;gt; routeStartingPoint, CatalogContentBase commerceRoot,
            bool enableOutgoingSeoUri)
            : this(
            routeStartingPoint, 
            commerceRoot, 
            enableOutgoingSeoUri,
            ServiceLocator.Current.GetInstance&amp;lt;IContentLoader&amp;gt;(),
            ServiceLocator.Current.GetInstance&amp;lt;IRoutingSegmentLoader&amp;gt;(),
            ServiceLocator.Current.GetInstance&amp;lt;IContentVersionRepository&amp;gt;(), 
            ServiceLocator.Current.GetInstance&amp;lt;IUrlSegmentRouter&amp;gt;(),
            ServiceLocator.Current.GetInstance&amp;lt;IContentLanguageSettingsHandler&amp;gt;(),
            ServiceLocator.Current.GetInstance&amp;lt;FacetUrlService&amp;gt;())
        {
        }

        [DefaultConstructor]
        public FacetPartialRoute(Func&amp;lt;ContentReference&amp;gt; routeStartingPoint, CatalogContentBase commerceRoot,
            bool supportSeoUri, IContentLoader contentLoader, IRoutingSegmentLoader routingSegmentLoader,
            IContentVersionRepository contentVersionRepository, IUrlSegmentRouter urlSegmentRouter,
            IContentLanguageSettingsHandler contentLanguageSettingsHandler,
            FacetUrlService facetUrlCreator)
            : base(
                routeStartingPoint, commerceRoot, supportSeoUri, contentLoader, routingSegmentLoader,
                contentVersionRepository, urlSegmentRouter, contentLanguageSettingsHandler)
        {
            _facetUrlCreator = facetUrlCreator;
        }

        public override object RoutePartial(PageData content, SegmentContext segmentContext)
        {
            var routedContet = base.RoutePartial(content, segmentContext);

            var segmentPair = segmentContext.GetNextValue(segmentContext.RemainingPath);
            if (String.IsNullOrEmpty(segmentPair.Next))
            {
                return routedContet;
            }

            var facetNames = _facetUrlCreator.GetFacetModels().ToArray();

            var nextSegment = _facetUrlCreator.GetFacetValue(facetNames, segmentPair.Next);
            if (String.IsNullOrEmpty(nextSegment))
            {
                return routedContet;
            }

            var routeFacets = segmentContext.RouteData.Values[FacetUrlService.RouteFacets] as ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;;
            if (routeFacets == null)
            {
                segmentContext.RouteData.Values[FacetUrlService.RouteFacets] = new ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;();
                routeFacets = (ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;)segmentContext.RouteData.Values[FacetUrlService.RouteFacets];
            }

            AddFacetsToSegmentContext(routeFacets, segmentContext, facetNames, nextSegment, segmentPair.Remaining, null);
            return routedContet;
        }

        private void AddFacetsToSegmentContext(ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt; routeFacets, SegmentContext segmentContext, RouteFacetModel[] facetNames, string nextSegment, string remaining, RouteFacetModel currentFacet)
        {
            if (String.IsNullOrEmpty(nextSegment))
            {
                return;
            }

            var value = facetNames.FirstOrDefault(x =&amp;gt; x.FacetName == nextSegment);
            if (value != null)
            {
                currentFacet = value;
                
            }
            else if (currentFacet != null)
            {
                var facetValue = _facetUrlCreator.GetFacetValue(facetNames, nextSegment);

                routeFacets.AddOrUpdate(currentFacet,
                   (key) =&amp;gt; new HashSet&amp;lt;object&amp;gt; { facetValue },
                   (key, list) =&amp;gt;
                   {
                       list.Add(facetValue);
                       return list;
                   });
            }

            segmentContext.RemainingPath = remaining;

            var segmentPair = segmentContext.GetNextValue(segmentContext.RemainingPath);
            nextSegment = _facetUrlCreator.GetFacetValue(facetNames, segmentPair.Next);

            AddFacetsToSegmentContext(routeFacets, segmentContext, facetNames, nextSegment, segmentPair.Remaining, currentFacet);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Route registration&lt;/h2&gt;
&lt;p&gt;We need a way of registering our route(s). We will create a class for the registrations called CatalogContentRouteRegistration:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ServiceConfiguration(Lifecycle = ServiceInstanceScope.Singleton)]
    public class CatalogContentRouteRegistration
    {
        private readonly IContentLoader _contentLoader;
        private readonly ReferenceConverter _referenceConverter;

        public CatalogContentRouteRegistration(IContentLoader contentLoader, ReferenceConverter referenceConverter)
        {
            _contentLoader = contentLoader;
            _referenceConverter = referenceConverter;
        }

        public void RegisterDefaultRoute()
        {
            RegisterDefaultRoute(false);
        }

        public void RegisterDefaultRoute(bool enableOutgoingSeoUri)
        {
            var commerceRootContent = _contentLoader.Get&amp;lt;CatalogContentBase&amp;gt;(_referenceConverter.GetRootLink());

            var pageLink = ContentReference.IsNullOrEmpty(SiteDefinition.Current.StartPage)
                ? SiteDefinition.Current.RootPage
                : SiteDefinition.Current.StartPage;

            RegisterRoute(pageLink, commerceRootContent, enableOutgoingSeoUri);
        }

        public void RegisterRoute(ContentReference pageLink, ContentReference catalogLink, bool enableOutgoingSeoUri)
        {
            var commerceRootContent = _contentLoader.Get&amp;lt;CatalogContentBase&amp;gt;(catalogLink);
            RegisterRoute(pageLink, commerceRootContent, enableOutgoingSeoUri);
        }

        public void RegisterRoute(ContentReference pageLink, CatalogContentBase catalogContentBase, bool enableOutgoingSeoUri)
        {
            RouteTable.Routes.RegisterPartialRouter(new FacetPartialRoute(() =&amp;gt; pageLink, catalogContentBase, enableOutgoingSeoUri));
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;FacetUrlService&lt;/h2&gt;
&lt;p&gt;I will create a new service named “FacetUrlService”. The main responsibility for this service is to create a virtual path for selected facets on a site. A method called “GetFilterPath” will take the standard virtual path as one parameter, and a dictionary containing selected facets as the other parameter. The return value will be a string containing the virtual path including facets from the parameter.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ServiceConfiguration(Lifecycle = ServiceInstanceScope.Singleton)]
    public class FacetUrlService
    {
        public const string RouteFacets = &quot;routeFacets&quot;;
        private readonly DynamicDataStoreFactory _dynamicDataStoreFactory;
        private readonly ISynchronizedObjectInstanceCache _objectInstanceCache;
        private readonly UrlResolver _urlResolver;

        public FacetUrlService(DynamicDataStoreFactory dynamicDataStoreFactory, ISynchronizedObjectInstanceCache objectInstanceCache, UrlResolver urlResolver)
        {
            _dynamicDataStoreFactory = dynamicDataStoreFactory;
            _objectInstanceCache = objectInstanceCache;
            _urlResolver = urlResolver;
        }

        public IEnumerable&amp;lt;RouteFacetModel&amp;gt; GetFacetModels()
        {
            var facetNames = GetCachedFacetNames();
            if (facetNames != null)
            {
                return facetNames;
            }

            var routingFacetNameStore = GetRoutingFacetNameStore();
            var allRouteFacetModels = routingFacetNameStore.LoadAll&amp;lt;RouteFacetModel&amp;gt;();

            var cacheKey = GetCacheName();
            _objectInstanceCache.Insert(cacheKey, allRouteFacetModels, new CacheEvictionPolicy(new string[0]));

            return allRouteFacetModels;
        }

        internal string GetUrl(IContent currentContent, RouteValueDictionary routeValues, string facetType, string facetKeyPath, string facetKey, object facetValue)
        {
            var originalRouteFacets = routeValues[RouteFacets] as ConcurrentDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;;

            var routeFacets = new Dictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt;();
            if (originalRouteFacets != null)
            {
                foreach (var routeFacetModel in originalRouteFacets.Keys)
                {
                    routeFacets.Add(routeFacetModel, new HashSet&amp;lt;object&amp;gt;());
                    foreach (var value in originalRouteFacets[routeFacetModel])
                    {
                        routeFacets[routeFacetModel].Add(value);
                    }
                }
            }

            var model = routeFacets.Select(x =&amp;gt; x.Key).SingleOrDefault(x =&amp;gt; x.FacetName == facetKey);
            if (model != null)
            {
                routeFacets[model].Add(facetValue);
            }
            else
            {
                model = new RouteFacetModel
                {
                    FacetName = facetKey,
                    FacetPath = facetKeyPath,
                    FacetType = facetType
                };
                routeFacets.Add(model, new HashSet&amp;lt;object&amp;gt; { facetValue });
            }

            string language = null;
            var languageContent = currentContent as ILocalizable;
            if (languageContent != null)
            {
                language = languageContent.Language.Name;
            }

            var url = _urlResolver.GetUrl(currentContent.ContentLink, language);
            return url.Length &amp;gt; 1 ? GetUrl(url.Substring(0, url.Length - 1), routeFacets) : url;
        }

        internal string GetUrl(string partialVirtualPath, IDictionary&amp;lt;RouteFacetModel, HashSet&amp;lt;object&amp;gt;&amp;gt; routeFacets)
        {
            var path = new StringBuilder(partialVirtualPath);

            var routeFacetKeys = routeFacets.Keys.OrderBy(x =&amp;gt; x.FacetName);
            foreach (var routeFacetKey in routeFacetKeys)
            {
                HashSet&amp;lt;object&amp;gt; keyValues;
                if (routeFacets.TryGetValue(routeFacetKey, out keyValues))
                {
                    SaveIfNotExist(routeFacetKey);
                    path.Append(String.Concat(&quot;/&quot;, routeFacetKey.FacetName));

                    var keyValueStrings = keyValues.Select(x =&amp;gt; x.ToString()).OrderBy(x =&amp;gt; x);
                    foreach (var keyValueString in keyValueStrings)
                    {
                        var facetValue = GetFacetValueWhenCreatingUrl(keyValueString);
                        path.Append(String.Concat(&quot;/&quot;, facetValue));
                    }
                }
            }

            return path.ToString();
        }

        internal string GetFacetValue(IEnumerable&amp;lt;RouteFacetModel&amp;gt; facetNames, string originalName)
        {
            var possibleProblems = facetNames.Where(x =&amp;gt; x.FacetName.EndsWith(originalName));
            if (!possibleProblems.Any())
            {
                return originalName;
            }

            var modifiedName = originalName;
            while (modifiedName.Length &amp;gt; 0)
            {
                modifiedName = modifiedName.Substring(1);
                if (!facetNames.Any(x =&amp;gt; x.FacetName.EndsWith(originalName)))
                {
                    return modifiedName;
                }
            }

            return originalName;
        }

        private string GetFacetValueWhenCreatingUrl(string originalName)
        {
            var facetNames = GetFacetModels();
            return GetFacetValueWhenCreatingUrl(facetNames, originalName);
        }

        private static string GetFacetValueWhenCreatingUrl(IEnumerable&amp;lt;RouteFacetModel&amp;gt; facetNames, string originalName)
        {
            if (facetNames == null || !facetNames.Any(x =&amp;gt; x.FacetName == originalName))
            {
                return originalName;
            }

            return GetFacetValueWhenCreatingUrl(facetNames, String.Concat(&quot;f&quot;, originalName));
        }

        private void SaveIfNotExist(RouteFacetModel facetName)
        {
            var facetNames = GetFacetModels();
            if (facetNames != null &amp;amp;&amp;amp; facetNames.Any(x =&amp;gt; x.FacetName == facetName.FacetName))
            {
                return;
            }

            var routingFacetNameStore = GetRoutingFacetNameStore();
            routingFacetNameStore.Save(facetName);
            ClearFacetNamesCache();
        }

        private IEnumerable&amp;lt;RouteFacetModel&amp;gt; GetCachedFacetNames()
        {
            return _objectInstanceCache.Get(GetCacheName()) as IEnumerable&amp;lt;RouteFacetModel&amp;gt;;
        }

        private void ClearFacetNamesCache()
        {
            _objectInstanceCache.Remove(GetCacheName());
        }

        private static string GetCacheName()
        {
            return &quot;bc:routingfacetnames&quot;;
        }

        private DynamicDataStore GetRoutingFacetNameStore()
        {
            const string routingFacetNames = &quot;RoutingFacetNames&quot;;
            return _dynamicDataStoreFactory.GetStore(routingFacetNames) ??
                _dynamicDataStoreFactory.CreateStore(routingFacetNames, typeof(RouteFacetModel));
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;HtmlHelper&lt;/h2&gt;
&lt;p&gt;The last thing I need is a HtmlHelper extension method, which will take the current content, the facet name, and facet value as parameters. We will then call FacetUrlService with the current url and facets to get a new url for a specific facet value.&lt;/p&gt;
&lt;p&gt;We can now call this helper method for every facet value in a facet navigation. The &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; will be unique for every possible combination if we make sure to sort the facets and facet values in our service. The &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; will also be normal &lt;a href=&quot;/manage-your-blogs/EditBlog/url:s&quot;&gt;url:s&lt;/a&gt; that a crawler easily can index. This can make a crawler index every possible facet combination on your facet navigated page as individual pages.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;        public static MvcHtmlString FacetContentUrl(this HtmlHelper htmlHelper, IContent currentContent, string facetType, string facetKeyPath, string facetKey, object facetValue)
        {
            var url = ServiceLocator.Current.GetInstance&amp;lt;FacetUrlService&amp;gt;().GetUrl(currentContent, htmlHelper.ViewContext.RouteData.Values, facetType, facetKeyPath, facetKey, facetValue);

            return new MvcHtmlString(url);
        }&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Implementation on Quicksilver&lt;/h2&gt;
&lt;p&gt;I will make a new blog post soon where I use the nuget package blow to implement this on Quicksilver.&lt;/p&gt;
&lt;h2&gt;Source code&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jonasbergqvist/BrilliantCut.AutomaticLandingPage&quot;&gt;https://github.com/jonasbergqvist/BrilliantCut.AutomaticLandingPage&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Nuget package&lt;/h2&gt;
&lt;p&gt;The easiest way to install the packages are to create a new package source in visual studio that points to &quot;&lt;a href=&quot;http://nuget.jobe.employee.episerver.com/&quot;&gt;http://nuget.jobe.employee.episerver.com/&lt;/a&gt;&quot; (&lt;a href=&quot;https://docs.nuget.org/consume/Package-Manager-Dialog&quot;&gt;https://docs.nuget.org/consume/Package-Manager-Dialog&lt;/a&gt;). Now it&#39;s possible to install the packages by writing &quot;&lt;em&gt;install-package brilliantcut.automaticlandingpage&lt;/em&gt;&quot; in the &quot;Package manager console&quot;.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2016/1/automatic-landing-page/</guid>            <pubDate>Thu, 07 Jan 2016 16:34:26 GMT</pubDate>           <category>Blog post</category></item><item> <title>Automatically &quot;Related items&quot; using Episerver Find</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/12/automatic-related-items-using-find/</link>            <description>&lt;p&gt;How about calling a service with a set of content to&amp;nbsp;receive &quot;related content&quot; for the original&amp;nbsp;content? This is possible with &quot;BrilliantCut.RelatedQuery&quot;. The only thing needed is to registrate which properties that should be used when creating a&amp;nbsp;related query.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;A common site scenario is that you want to&amp;nbsp;get related content for&amp;nbsp;some&amp;nbsp;content.&amp;nbsp;This can be used on a commerce site to&amp;nbsp;get&amp;nbsp;additional sales, but also on big CMS sites where you want the user to find related content. An easy CMS example is a news site, where you want to show related news to the current news item.&lt;/p&gt;
&lt;p&gt;Nuget source:&amp;nbsp;&lt;a href=&quot;http://nuget.jobe.employee.episerver.com/&quot;&gt;http://nuget.jobe.employee.episerver.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Git hub:&amp;nbsp;&lt;a href=&quot;https://github.com/jonasbergqvist/FindRelatedQuery&quot;&gt;https://github.com/jonasbergqvist/FindRelatedQuery&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;BrilliantCut.RelatedQuery&lt;/h2&gt;
&lt;p&gt;I have created a nuget package that I call &quot;BrilliantCut.RelatedQuery&quot;, which uses EPiServer.Find to get related content automatically. The only thing you need to do is to specify which properties that should be involved in the related query. Here is an example of a registration,&amp;nbsp;which will use two properties with different boosting:&lt;/p&gt;
&lt;pre&gt;public&amp;nbsp;class&amp;nbsp;DefaultRelationQuery&amp;nbsp;:&amp;nbsp;IRelatedQuery
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RelatedFilterRegistration&amp;nbsp;RegistryQuery(RelatedQueryRegistration&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;ISize&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Size,&amp;nbsp;1.2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;ITemperature&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Temperature,&amp;nbsp;1.4);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;p&gt;The registrated query can now be used to search for related content:&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;private&lt;/span&gt;&amp;nbsp;&lt;span&gt;ITypeSearch&lt;/span&gt;&amp;lt;&lt;span&gt;ProductContent&lt;/span&gt;&amp;gt;&amp;nbsp;CreateRelatedQuery(&lt;span&gt;RelatedQueryFactory&lt;/span&gt;&amp;nbsp;relatedQueryFactory,&amp;nbsp;&lt;span&gt;params&lt;/span&gt;&amp;nbsp;&lt;span&gt;object&lt;/span&gt;[]&amp;nbsp;content)
{
&lt;span&gt;    return&lt;/span&gt;&amp;nbsp;relatedQueryFactory.CreateQuery&amp;lt;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;,&amp;nbsp;&lt;span&gt;ProductContent&lt;/span&gt;&amp;gt;(content);
}&lt;/pre&gt;
&lt;h3&gt;Register&amp;nbsp;a query&lt;/h3&gt;
&lt;p&gt;A query needs to be regitred to be able to create a related query. A query will be reigstrated by creating a class, which implements IRelatedQuery. The interface contains one method, which has a &quot;RelatedQueryRegistration&quot; as parameter. The method returns a &quot;RelatedFilterRegistration&quot;. The &quot;RelatedQueryRegistration&quot; class contains methods for register boost filters, modify the main search, and modify the filters.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IRelatedQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;RelatedFilterRegistration&lt;/span&gt;&amp;nbsp;RegistryQuery(&lt;span&gt;RelatedQueryRegistration&lt;/span&gt;&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.ModifySearchQuery&amp;lt;&lt;span&gt;DefaultSearchQuery&lt;/span&gt;&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.ModifyExclusionFilterQuery&amp;lt;Exclude&lt;span&gt;TypeFilterQuery&lt;/span&gt;&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddMatchFilter&amp;lt;&lt;span&gt;ISeason&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Season,&amp;nbsp;1.2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddMatchFilter&amp;lt;&lt;span&gt;IEvent&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Event,&amp;nbsp;1.4)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;&lt;span&gt;ISize&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Size,&amp;nbsp;1.6)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;&lt;span&gt;ITemperature&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Temperature,&amp;nbsp;1.3);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;AddMatchFilter&lt;/h3&gt;
&lt;p&gt;AddMatchFilter registers a property match. Specify an interface, base class, or concrete class as the generic argument, and choose which property that will be matched in the query. An exact match will be done between the content send in to the related search, and the indexed content.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IRelatedQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;RelatedFilterRegistration&lt;/span&gt;&amp;nbsp;RegistryQuery(&lt;span&gt;RelatedQueryRegistration&lt;/span&gt;&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddMatchFilter&amp;lt;&lt;span&gt;ISeason&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Season,&amp;nbsp;2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;AddRangeFilter&lt;/h3&gt;
&lt;p&gt;AddRangeFilter&amp;nbsp;registers a filter that will take the min and max value from the supplied content for a specific property. Specify an interface, base class, or concrete class as the generic argument, and choose which property that will be used&amp;nbsp;in the query. An range&amp;nbsp;filter&amp;nbsp;will be done between the content send in to the related search, and the indexed content.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IRelatedQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;RelatedFilterRegistration&lt;/span&gt;&amp;nbsp;RegistryQuery(&lt;span&gt;RelatedQueryRegistration&lt;/span&gt;&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;&lt;span&gt;ITemperature&lt;/span&gt;&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Temperature,&amp;nbsp;2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;AddFilter&lt;/h3&gt;
&lt;p&gt;AddFilter&amp;nbsp;registers a custom filter. Specify an interface, base class, or concrete class as the generic argument, and choose which property that will be&amp;nbsp;used&amp;nbsp;in the query. The filter will be used&amp;nbsp;between the content send in to the related search, and the indexed content.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;CustomFilter&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IRelatedFilter&lt;/span&gt;&amp;lt;&lt;span&gt;ISeason&lt;/span&gt;&amp;gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;private&lt;/span&gt;&amp;nbsp;&lt;span&gt;readonly&lt;/span&gt;&amp;nbsp;&lt;span&gt;IClient&lt;/span&gt;&amp;nbsp;_client;
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;CustomFilter(&lt;span&gt;IClient&lt;/span&gt;&amp;nbsp;client)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_client&amp;nbsp;=&amp;nbsp;client;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;Filter&lt;/span&gt;&amp;nbsp;CreateFilter(&lt;span&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span&gt;object&lt;/span&gt;&amp;gt;&amp;nbsp;content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;var&lt;/span&gt;&amp;nbsp;seasonContents&amp;nbsp;=&amp;nbsp;content.OfType&amp;lt;&lt;span&gt;ISeason&lt;/span&gt;&amp;gt;();
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;var&lt;/span&gt;&amp;nbsp;filter&amp;nbsp;=&amp;nbsp;&lt;span&gt;new&lt;/span&gt;&amp;nbsp;&lt;span&gt;FilterBuilder&lt;/span&gt;&amp;lt;&lt;span&gt;ISeason&lt;/span&gt;&amp;gt;(_client);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;foreach&lt;/span&gt;&amp;nbsp;(&lt;span&gt;var&lt;/span&gt;&amp;nbsp;seasonContent&amp;nbsp;&lt;span&gt;in&lt;/span&gt;&amp;nbsp;seasonContents)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filter&amp;nbsp;=&amp;nbsp;filter.Or(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Season.Match(seasonContent.Season));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;filter;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;double&lt;/span&gt;&amp;nbsp;Boost&amp;nbsp;{&amp;nbsp;&lt;span&gt;get&lt;/span&gt;&amp;nbsp;{&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;1.5;&amp;nbsp;}}
}&lt;/pre&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IRelatedQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;RelatedFilterRegistration&lt;/span&gt;&amp;nbsp;RegistryQuery(&lt;span&gt;RelatedQueryRegistration&lt;/span&gt;&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddFilter&amp;lt;&lt;span&gt;CustomFilter&lt;/span&gt;&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;Change the default&amp;nbsp;search&lt;/h3&gt;
&lt;p&gt;The methods AddMatchFilter, AddRangeFilter, and AddFilter will perform boost matching against the index. A search query will be created before those boost filters are added to the query. The search query can be modified, to contain whatever you like before the boost matches are added. The default implementation is a &quot;.For(&#39;&#39;)&quot; on IClient.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;CustomSearchQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IModifySearchQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;IQueriedSearch&lt;/span&gt;&amp;lt;TQuery,&amp;nbsp;&lt;span&gt;QueryStringQuery&lt;/span&gt;&amp;gt;&amp;nbsp;CreateSearchQuery&amp;lt;TQuery&amp;gt;(&lt;span&gt;IClient&lt;/span&gt;&amp;nbsp;client,&amp;nbsp;&lt;span&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span&gt;object&lt;/span&gt;&amp;gt;&amp;nbsp;content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;client.Search&amp;lt;TQuery&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.For(&lt;span&gt;&quot;something&quot;&lt;/span&gt;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;pre&gt;public&amp;nbsp;class&amp;nbsp;DefaultRelationQuery&amp;nbsp;:&amp;nbsp;IRelatedQuery
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RelatedFilterRegistration&amp;nbsp;RegistryQuery(RelatedQueryRegistration&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.ModifySearchQuery&amp;lt;CustomSearchQuery&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddRangeFilter&amp;lt;ITemperature&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Temperature,&amp;nbsp;1.3);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;Change the default exclusion filtering&lt;/h3&gt;
&lt;p&gt;When the boost filters has been added, some &quot;real&quot; filters will be used to exclude some content from the result. The default implementation will exclude the types that was supplied to the related query search. This will result in related content that is of a different type, but that has properties with similar values.&lt;/p&gt;
&lt;p&gt;There is also another filter implementation build in that will exclude the items send in to the related query search. Other items of the same types will be returned by the related query search if this one is used.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;class&lt;/span&gt;&amp;nbsp;&lt;span&gt;CustomExclusionFilterQuery&lt;/span&gt;&amp;nbsp;:&amp;nbsp;&lt;span&gt;IModifyExclusionFilterQuery&lt;/span&gt;
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;public&lt;/span&gt;&amp;nbsp;&lt;span&gt;ITypeSearch&lt;/span&gt;&amp;lt;TQuery&amp;gt;&amp;nbsp;Filter&amp;lt;TQuery&amp;gt;(&lt;span&gt;IQueriedSearch&lt;/span&gt;&amp;lt;TQuery,&amp;nbsp;&lt;span&gt;CustomFiltersScoreQuery&lt;/span&gt;&amp;gt;&amp;nbsp;boostQuery,&amp;nbsp;&lt;span&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span&gt;object&lt;/span&gt;&amp;gt;&amp;nbsp;content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;var&lt;/span&gt;&amp;nbsp;typeBuilder&amp;nbsp;=&amp;nbsp;&lt;span&gt;new&lt;/span&gt;&amp;nbsp;&lt;span&gt;FilterBuilder&lt;/span&gt;&amp;lt;&lt;span&gt;ProductContent&lt;/span&gt;&amp;gt;(boostQuery.Client)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.And(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Created.GreaterThan(&lt;span&gt;new&lt;/span&gt;&amp;nbsp;&lt;span&gt;DateTime&lt;/span&gt;(2015,&amp;nbsp;1,&amp;nbsp;1)));
 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;boostQuery.Filter(typeBuilder);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;pre&gt;public&amp;nbsp;class&amp;nbsp;DefaultRelationQuery&amp;nbsp;:&amp;nbsp;IRelatedQuery
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;RelatedFilterRegistration&amp;nbsp;RegistryQuery(RelatedQueryRegistration&amp;nbsp;relatedQueryRegistration)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;relatedQueryRegistration
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.ModifyExclusionFilterQuery&amp;lt;CustomExclusionFilterQuery&amp;gt;()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.AddMatchFilter&amp;lt;ISeason&amp;gt;(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.Season,&amp;nbsp;1.2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;
&lt;h3&gt;Search for related items&lt;/h3&gt;
&lt;p&gt;RelatedQueryFactory is the class that will create a query. The method &quot;CreateQuery&amp;lt;T&amp;gt;&quot; takes ContentReferences or objects as parameters, and will return an ITypeSearch&amp;lt;T&amp;gt;. Other filters can then be added to the query, like &quot;FilterForVisitor&quot;. It&#39;s also possible, and recommended, to use projection before executing the query to elastic search. Finaly it&#39;s recomended to use &quot;GetRelatedResult&quot; as executing method. This method will do post-filtering, to make sure only related items are recieved.&lt;/p&gt;
&lt;pre&gt;&lt;span&gt;private&lt;/span&gt;&amp;nbsp;&lt;span&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span&gt;ContentReference&lt;/span&gt;&amp;gt;&amp;nbsp;GetRelatedContentReferences(&lt;span&gt;RelatedQueryFactory&lt;/span&gt;&amp;nbsp;relatedQueryFactory,&amp;nbsp;&lt;span&gt;params&lt;/span&gt;&amp;nbsp;&lt;span&gt;object&lt;/span&gt;[]&amp;nbsp;content)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span&gt;return&lt;/span&gt;&amp;nbsp;relatedQueryFactory.CreateQuery&amp;lt;&lt;span&gt;DefaultRelationQuery&lt;/span&gt;,&amp;nbsp;&lt;span&gt;ProductContent&lt;/span&gt;&amp;gt;(content)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.FilterForVisitor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.Take(5)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.Select(x&amp;nbsp;=&amp;gt;&amp;nbsp;x.ContentLink)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.GetRelatedResult();
}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;
&lt;h2&gt;Examples&lt;/h2&gt;
&lt;h3&gt;Example 1: Searching for mens summer jackets&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/link/4947ed79f32e41279353aa4defbb97ee.aspx&quot; alt=&quot;Image SummerShoe.PNG&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Example 2: Searching for mens winter jackets that is warm in -25 to -20 degrees&amp;nbsp;&lt;/h3&gt;
&lt;h2&gt;&lt;img src=&quot;/link/81621b56119e40ceade2868d53f70e76.aspx&quot; alt=&quot;Image WinterShoe.PNG&quot; /&gt;&lt;/h2&gt;
&lt;h2&gt;Install package&lt;/h2&gt;
&lt;p&gt;To install the package on your site, add the following source as a package source in Visual studio:&amp;nbsp;&lt;a href=&quot;http://nuget.jobe.employee.episerver.com/&quot;&gt;http://nuget.jobe.employee.episerver.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For more information how to add a package source, look at the package source section at:&amp;nbsp;&lt;a href=&quot;https://docs.nuget.org/consume/package-manager-dialog&quot;&gt;https://docs.nuget.org/consume/package-manager-dialog&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Get the source code&lt;/h2&gt;
&lt;p&gt;The source code can be found on github:&amp;nbsp;&lt;a href=&quot;https://github.com/jonasbergqvist/FindRelatedQuery&quot;&gt;https://github.com/jonasbergqvist/FindRelatedQuery&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;/pre&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/12/automatic-related-items-using-find/</guid>            <pubDate>Fri, 11 Dec 2015 08:53:49 GMT</pubDate>           <category>Blog post</category></item><item> <title>New version of EPiServer.Find.Commerce</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/11/new-version-of-episerver-find-commerce/</link>            <description>&lt;p&gt;A new version of EPiServer.Find.Commerce (9.3.0) was recently released. This version makes it easier to use Find together with Commerce. We now listen&amp;nbsp;to ecf events, price updates, and inventory updates. We also index markets and relations. More features&amp;nbsp;will be released&amp;nbsp;soon, as the nested querying is being developed by the Find team.&lt;/p&gt;
&lt;h2&gt;Listening of ECF events&lt;/h2&gt;
&lt;p&gt;We listen&amp;nbsp;to ECF events. We&amp;nbsp;add your typed content to the indexing queue when a change&amp;nbsp;is made in the ECF layer.&lt;/p&gt;
&lt;h3&gt;Service API&lt;/h3&gt;
&lt;p&gt;Your typed content&amp;nbsp;is now&amp;nbsp;indexed/reindexed/removed from the index&amp;nbsp;after&amp;nbsp;a request is made to the service API.&lt;/p&gt;
&lt;h3&gt;Commerce manager&lt;/h3&gt;
&lt;p&gt;We&amp;nbsp;listen on remote events from commerce manager, and index your typed content when something changes in commerce manager.&lt;/p&gt;
&lt;p&gt;It&#39;s not 100% sure that the front end site will receive the event from commerce manager. If the front-end site is down when an event is&amp;nbsp;triggered, the typed content is not&amp;nbsp; added to the indexing queue. A safer approach will be developed later.&lt;/p&gt;
&lt;h2&gt;Indexing prices&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;We index both the default price and the whole price collection for variation content. Performance will be a lot better when receiving prices from the index, instead of using the price provider. It&amp;nbsp;will be&amp;nbsp;possible to make price filters for a specific user when nested query support is released in EPiServer.Find.&lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Reindex content on price updates&lt;/h3&gt;
&lt;p&gt;If your document indexes a property of type Price, or IEnumerable&amp;lt;Price&amp;gt;, the document is reindexed when prices are changed, if the price provider raises the price update event. The default provider&amp;nbsp;raises the price update event. More information how to raise the event from a custom price provider can be found here:&amp;nbsp;&lt;a href=&quot;/link/f31898af82dd496ba4ff38457220d3ab.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Search/find-integration/raise-price-and-inventory-event-updates/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Remove prices from index&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;It&#39;s easy to remove the indexing of prices for variation content. Documentation how to remove the extension can be found here:&amp;nbsp;&lt;a href=&quot;/link/e6cdab0f1cc34b44ba4d9f4267d56adc.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Search/find-integration/overriding-default-conventions/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Indexing&amp;nbsp;inventory&lt;/h2&gt;
&lt;p&gt;We index the inventory&amp;nbsp;collection for the variation content. Performance will be a lot better when receiving the inventory&amp;nbsp;from the index, instead of using the inventory&amp;nbsp;provider. There can be a problem indexing inventories if the inventory is changed too often. In that case, the inventory should be removed from the index.&lt;/p&gt;
&lt;h3&gt;Reindex content on inventory&amp;nbsp;updates&lt;/h3&gt;
&lt;p&gt;If your document indexes a property of type Inventory, or IEnumerable&amp;lt;Inventory&amp;gt;, the document is re-indexed when the inventory&amp;nbsp;is changed, if the inventory&amp;nbsp;provider raises the inventory&amp;nbsp;update event. The default provider raises the inventory&amp;nbsp;update event. More information how to raise the event from a custom inventory&amp;nbsp;provider can be found here:&amp;nbsp;&lt;a href=&quot;/link/f31898af82dd496ba4ff38457220d3ab.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Search/find-integration/raise-price-and-inventory-event-updates/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Remove inventories&amp;nbsp;from index&lt;/h3&gt;
&lt;p&gt;It&#39;s easy to remove the indexing of inventory&amp;nbsp;for variation content. Documentation how to remove the extension can be found here:&amp;nbsp;&lt;a href=&quot;/link/e6cdab0f1cc34b44ba4d9f4267d56adc.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Search/find-integration/overriding-default-conventions/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Relations&lt;/h2&gt;
&lt;p&gt;We now index relations between different catalog content.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Products: Variation references for a product&lt;/li&gt;
&lt;li&gt;Variations: Product(s) the variation are related to.&lt;/li&gt;
&lt;li&gt;Packages: Package entries for a package, and the related package(s) for the entries.&lt;/li&gt;
&lt;li&gt;Bundles: Bundle&amp;nbsp;entries for a bundle, and the related bundle(s) for the entries.&lt;/li&gt;
&lt;li&gt;Nodes: Both&amp;nbsp;parent node relations, and child node relations.&lt;/li&gt;
&lt;li&gt;Associations&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Markets&lt;/h3&gt;
&lt;p&gt;Markets&amp;nbsp;are indexed for EntryContentBase, which makes it easy to filter content based on a market. The filter &quot;FilterOnCurrentMarket&quot; only receives entries&amp;nbsp;that are available on the current market.&lt;/p&gt;
&lt;h2&gt;New version of Brilliant cut&lt;/h2&gt;
&lt;p&gt;There is an updated version available of Brilliant cut, which uses the new EPiServer.Find.Commerce package.&lt;/p&gt;
&lt;p&gt;Github:&amp;nbsp;&lt;a href=&quot;https://github.com/jonasbergqvist/BrilliantCut&quot;&gt;https://github.com/jonasbergqvist/BrilliantCut&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Site:&amp;nbsp;&lt;a href=&quot;http://www.brilliantcut.se/&quot;&gt;http://www.brilliantcut.se/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Documentation&lt;/h2&gt;
&lt;p&gt;Documentation about the find integration can be found here:&amp;nbsp;&lt;a href=&quot;/link/94d1dfade0f849a1bf813b9054565b12.aspx&quot;&gt;http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-Commerce/9/Search/find-integration/find-integration/&lt;/a&gt;&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/11/new-version-of-episerver-find-commerce/</guid>            <pubDate>Wed, 04 Nov 2015 07:39:51 GMT</pubDate>           <category>Blog post</category></item><item> <title>Introducing BrilliantCut</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/4/brilliantcut/</link>            <description>&lt;p&gt;BrilliantCut makes it possible to filter the catalog UI using facets. The project includes facets like language, market, and category. It also includes a free text search, and the possibility to choose if the search should apply to children or descendants. The more important feature in BrilliantCut is the possibility to create your own facets for filtering the catalog UI. It’s very easy to creating your own facets in the catalog UI using a fluent API.&lt;/p&gt;
&lt;div id=&quot;scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:b498f75a-ba60-406d-930d-f01387fffc1f&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; margin: 0px; display: inline; padding: 0px;&quot;&gt;&lt;object width=&quot;1024&quot; height=&quot;768&quot; data=&quot;http://www.youtube.com/v/vNMtr6gCkIg&quot; type=&quot;application/x-shockwave-flash&quot;&gt;&lt;param name=&quot;src&quot; value=&quot;http://www.youtube.com/v/vNMtr6gCkIg&quot; /&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot; /&gt;&lt;/object&gt;&lt;/div&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;The video can also be watched here: &lt;a href=&quot;http://www.youtube.com/watch?v=vNMtr6gCkIg&quot;&gt;http://www.youtube.com/watch?v=vNMtr6gCkIg&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Github&lt;/h2&gt;
&lt;p&gt;The code is open source and can be found at &lt;a href=&quot;http://github.com/jonasbergqvist/brilliantcut&quot;&gt;http://github.com/jonasbergqvist/brilliantcut&lt;/a&gt;. More information about the project will be found at that page.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;The nuget packages is located at “&lt;a title=&quot;http://jobe.employee.episerver.com/nuget/&quot; href=&quot;http://nuget.jobe.employee.episerver.com/&quot;&gt;http://nuget.jobe.employee.episerver.com/&lt;/a&gt;”. Add a nuget package source to that directory in Visual Studio to be able to install “BrilliantCut”. When the package source has been added in Visual Studio, the packages can be installed to your site by writing “install-package brilliantcut.widget” in the package manager console.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/4/brilliantcut/</guid>            <pubDate>Thu, 16 Apr 2015 08:17:32 GMT</pubDate>           <category>Blog post</category></item><item> <title>Installing Commerce using Visual Studio extensions</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/3/installing-commerce-using-visual-studio-extensions/</link>            <description>&lt;p&gt;With the &lt;a href=&quot;/link/86a13130b82f4221b152cb7641d175fe.aspx?id=117324&quot;&gt;update 56&lt;/a&gt;, we now have support for installing both the front-end site and Commerce Manager using the Visual Studio extension.&lt;/p&gt;
&lt;p&gt;From now on, deployment center shouldn&amp;rsquo;t be used when installing a new empty commerce site. The steps to create a new site are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new CMS site using the visual studio extension.&lt;/li&gt;
&lt;li&gt;Add the EPiServer.Commerce nuget package on the project.&lt;/li&gt;
&lt;li&gt;Add the EPiServer.Commerce.UI.ManagerIntegration on the project.&lt;/li&gt;
&lt;li&gt;Create a new empty asp.net project in the solution.&lt;/li&gt;
&lt;li&gt;Add the EPiServer.CommerceManager nuget package on the empty asp.net project.&lt;/li&gt;
&lt;li&gt;Run update-epidatabase in the package manager console.&lt;/li&gt;
&lt;li&gt;Compile.&lt;/li&gt;
&lt;li&gt;Start the Commerce manager site (Set as startup project, and then Ctrl+F5).&lt;/li&gt;
&lt;li&gt;Run the front-end site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, there is still some steps that needs to be performed after added a site using the Visual Studio extension, but the procedure is still much better than before. It should not take you more than 5 minutes to create a full Commerce site from Visual Studio.&lt;/p&gt;
&lt;p&gt;To read more about how a commerce site should be installed, go to the updated &lt;a href=&quot;/link/346d7c513a6449e4881a009f404d2232.aspx?id=74154&quot;&gt;installation document&lt;/a&gt;.&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2015/3/installing-commerce-using-visual-studio-extensions/</guid>            <pubDate>Mon, 09 Mar 2015 21:23:00 GMT</pubDate>           <category>Blog post</category></item><item> <title>Comments and Ratings in Commerce 8.0</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/10/Comments-and-Ratings-in-Commerce-80/</link>            <description>&lt;p&gt;In Commerce 8.0, we have dropped the dependency to EPiServer Common Framework. When dropping the dependency, we had to remove all classes in EPiServer.Business.Commerce which had dependencies to EPiServer Common Framework. With the dropped classes, we dropped some functionality. This blog post will show how to recreate the missing functionality.&lt;/p&gt;  &lt;h2&gt;Dropped functionality&lt;/h2&gt;  &lt;p&gt;When we dropped the dependency to EPiServer Common Framework, we lost comments and ratings, which has been a part of Commerce in earlier versions. This will cause problems when upgrading to Commerce 8, if the site uses comments or rating.&lt;/p&gt;  &lt;h3&gt;Dropped classes&lt;/h3&gt;  &lt;p&gt;The following classes have been dropped from EPiServer.Business.Commerce:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Comment &lt;/li&gt;    &lt;li&gt;CommentExtensions &lt;/li&gt;    &lt;li&gt;DynamicEntry &lt;/li&gt;    &lt;li&gt;DynamicEntryHandler &lt;/li&gt;    &lt;li&gt;DynamicEntryProvider &lt;/li&gt;    &lt;li&gt;IMessage &lt;/li&gt;    &lt;li&gt;IProductComment &lt;/li&gt;    &lt;li&gt;IProductRating &lt;/li&gt;    &lt;li&gt;ProductComment &lt;/li&gt;    &lt;li&gt;ProductCommentResult &lt;/li&gt;    &lt;li&gt;ProductEntry &lt;/li&gt;    &lt;li&gt;ProductEntryHandler &lt;/li&gt;    &lt;li&gt;ProductRating &lt;/li&gt;    &lt;li&gt;RatingMessage &lt;/li&gt;    &lt;li&gt;UserExtensions &lt;/li&gt; &lt;/ul&gt;  &lt;h2&gt;Make logging work&lt;/h2&gt;  &lt;p&gt;In “EPiServer CommonFramework”, there is functionality for logging, which will help developers to find problems in the code. This code will not be available anymore as long as the “EPiServer CommonFramework” nuget package isn’t explicitly installed on the site. Projects that has used EPiServer Common logging should change to work directly against “log4net”. By changing the using statement to “log4net”, log4net will start handling the logging. &lt;/p&gt;  &lt;h2&gt;Recreate functionality&lt;/h2&gt;  &lt;p&gt;To add back comments and ratings after upgraded to Commerce 8, the following steps are needed:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Add the package “EPiServer Common” to your project. &lt;/li&gt;    &lt;li&gt;Download the zip-package below. &lt;/li&gt;    &lt;li&gt;Add the classes to your project after you have unzipped the package. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;After you have the classes in to your project, you have to add a couple of lines to an initialization module:&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;rem&quot;&gt;// register entity provider with supported types&lt;/span&gt;
EntityProviderHandler.Instance.RegisterEntityProvider&amp;lt;DynamicEntityProvider, DynamicEntity&amp;gt;();
EntityProviderHandler.Instance.RegisterEntityProvider&amp;lt;DynamicEntityProvider, ProductEntity&amp;gt;();&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;&lt;/h2&gt;
&lt;style type=&quot;text/css&quot;&gt;













.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Download&lt;/h3&gt;

&lt;div id=&quot;scid:8eb9d37f-1541-4f29-b6f4-1eea890d4876:7420c3a6-e938-4797-b256-816a1c978816&quot; class=&quot;wlWriterEditableSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;div id=&quot;scid:8eb9d37f-1541-4f29-b6f4-1eea890d4876:7420c3a6-e938-4797-b256-816a1c978816&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;

&lt;div id=&quot;scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:33a96d10-9748-48dc-b53c-c3dc231f6c16&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;p&gt;Zip file containting comments and ratings: &lt;a href=&quot;/link/af1a0c09b4934b8c836972d21c29d9f6.zip&quot; target=&quot;_blank&quot;&gt;CommentsAndRatings.zip&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/10/Comments-and-Ratings-in-Commerce-80/</guid>            <pubDate>Mon, 06 Oct 2014 12:40:42 GMT</pubDate>           <category>Blog post</category></item><item> <title>How to register the partial routing in Commerce</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/4/How-to-register-the-partial-routing-in-Commerce/</link>            <description>&lt;p&gt;To make a site work in Commerce 7.5, we have to register a partial router that will handle the catalog routing. The partial router, that comes out of the box, is called ‘hierarical catalog partial router’. This partial router needs to be registered during initialization if it’s intended to be used.&lt;/p&gt;  &lt;h1&gt;Different ways of register the partial route in an initialization module&lt;/h1&gt;  &lt;p&gt;We should always use an initialization module for the registration of the partial route. If there is no initialization module in the project that fits this purpose, create one. &lt;/p&gt;  &lt;p&gt;An initialization module is a class that implements the interface “IInitializableModule”. When used in commerce, the class should also be decorated with the “ModuleDependency” attribute, with it’s type set to Commerce.Initialization.InitializationModule.&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(&lt;span class=&quot;kwrd&quot;&gt;typeof&lt;/span&gt;(EPiServer.Commerce.Initialization.InitializationModule))]
&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; Initialization : IInitializableModule
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;void&lt;/span&gt; Initialize(InitializationEngine context)
    {
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;PartialRouteHandler&lt;/h2&gt;

&lt;p&gt;To register the partial router, we can use the “PartialRouteHandler” inside the “Initialize” method of our initialization module.&lt;/p&gt;

&lt;p&gt;We will first get the services “ReferenceConverter”, “IContentLoader”, and “PartialRouteHandler”. Those will be used for getting the catalog root content, and for the registration.&lt;/p&gt;

&lt;p&gt;We will get the catalog root link using the reference converter. When we have the content link, it’s easy to get the content using the content loader.&lt;/p&gt;

&lt;p&gt;At last we will create a hierarchical catalog partial router. We will in this example set the start page for the route to the start page of the route. This could be changed to be a setting property on the start page type. We will also tell the partial router that it should not use SEO Uri:s when creating links. By changing false to true for the last parameter, SEO Uris would be generated as links for content on the site. &lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var referenceConverter = context.Locate.Advanced.GetInstance&amp;lt;Mediachase.Commerce.Catalog.ReferenceConverter&amp;gt;();
var contentLoader = context.Locate.Advanced.GetInstance&amp;lt;IContentLoader&amp;gt;();
var partialRouteHandler = context.Locate.Advanced.GetInstance&amp;lt;PartialRouteHandler&amp;gt;();

var commerceRootContent = contentLoader.Get&amp;lt;Commerce.Catalog.ContentTypes.CatalogContentBase&amp;gt;(referenceConverter.GetRootLink());
var hierarchicalCatalogPartialRouter = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; HierarchicalCatalogPartialRouter(() =&amp;gt; SiteDefinition.Current.StartPage, commerceRootContent, &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;);

partialRouteHandler.RegisterPartialRouter(&lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; PartialRouter&amp;lt;Core.PageData, Commerce.Catalog.ContentTypes.CatalogContentBase&amp;gt;(hierarchicalCatalogPartialRouter));&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;RegisterPartialRouter extension method&lt;/h2&gt;

&lt;p&gt;There is an extension method in the namespace EPiServer.Web.Routing, that makes the registration a little bit easier. By using the method “RegisterPartialRouter”, we don’t have to use the partial route handler directly.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;var referenceConverter = context.Locate.Advanced.GetInstance&amp;lt;Mediachase.Commerce.Catalog.ReferenceConverter&amp;gt;();
var contentLoader = context.Locate.Advanced.GetInstance&amp;lt;IContentLoader&amp;gt;();

var commerceRootContent = contentLoader.Get&amp;lt;Commerce.Catalog.ContentTypes.CatalogContentBase&amp;gt;(referenceConverter.GetRootLink());
var hierarchicalCatalogPartialRouter = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; HierarchicalCatalogPartialRouter(() =&amp;gt; SiteDefinition.Current.StartPage, commerceRootContent, &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;);

RouteTable.Routes.RegisterPartialRouter(hierarchicalCatalogPartialRouter);&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;MapDefaultHierarchialRouter extension method&lt;/h2&gt;

&lt;p&gt;In cases where the the catalog content should be set to the catalog root (as in the examples above), another extension method can be used, which is located in the namespace EPiServer.Commerce.Routing. The extension method is called “MapDefaultHierarchialRouter”, and it’s only possible to set the start page, and SEO Uri flag on that one.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;CatalogRouteHelper.MapDefaultHierarchialRouter(RouteTable.Routes, () =&amp;gt; SiteDefinition.Current.StartPage, &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is also an overload that only set the SEO Uri flag. This one will use the SiteDefinition.Current.StartPage as the start page for the partial route.&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;CatalogRouteHelper.MapDefaultHierarchialRouter(RouteTable.Routes, &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;);&lt;/code&gt;&lt;/pre&gt;

&lt;h1&gt;Using a node as the catalog root for routing&lt;/h1&gt;

&lt;p&gt;You might want to change the catalog root for the partial routing. In our example site, we have a catalog called “Departmental Catalog”, with a “Departments” node under it. We could change the registration to have Department as the root content by the following code:&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ModuleDependency(&lt;span class=&quot;kwrd&quot;&gt;typeof&lt;/span&gt;(EPiServer.Commerce.Initialization.InitializationModule))]
&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; Initialization : IInitializableModule
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;void&lt;/span&gt; Initialize(InitializationEngine context)
    {
        var referenceConverter = context.Locate.Advanced.GetInstance&amp;lt;Mediachase.Commerce.Catalog.ReferenceConverter&amp;gt;();
        var contentLoader = context.Locate.Advanced.GetInstance&amp;lt;IContentLoader&amp;gt;();

        var departmentCatalog = contentLoader.GetChildren&amp;lt;Commerce.Catalog.ContentTypes.CatalogContent&amp;gt;(referenceConverter.GetRootLink()).First();
        var departmentsNode = contentLoader.GetChildren&amp;lt;Commerce.Catalog.ContentTypes.NodeContent&amp;gt;(departmentCatalog.ContentLink).First();

        var hierarchicalCatalogPartialRouter = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; HierarchicalCatalogPartialRouter(() =&amp;gt; SiteDefinition.Current.StartPage, departmentsNode, &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;);

        RouteTable.Routes.RegisterPartialRouter(hierarchicalCatalogPartialRouter);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/4/How-to-register-the-partial-routing-in-Commerce/</guid>            <pubDate>Tue, 29 Apr 2014 07:14:44 GMT</pubDate>           <category>Blog post</category></item><item> <title>Indexing catalog content using EPiServer Find Content Indexing Job</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/1/Indexing-catalog-content-using-EPiServer-Find-Content-Indexing-Job/</link>            <description>&lt;p&gt;When developing a commerce site, a good search product might be the key to success. Working search driven can make the site fast, easy to understand, and simple to use. For me, EPiServer Find is the obvious choice when working with EPiServer Commerce.&lt;/p&gt;  &lt;h2&gt;Find search provider&lt;/h2&gt;  &lt;p&gt;When using EPiServer Find in a commerce site, you might end up using the “Find seach provider” on the site, which isn’t that good. Instead you should work with the strongly typed “catalog content”, that are being indexed when adding or updating content in the new catalog UI. The problem with the typed catalog content, is that there’s no indexing job for it.&lt;/p&gt;  &lt;p&gt;So, why should we use catalog content (the content types you create in code) instead of the content stored by the find search provider? The Find search provider index the data untyped in different dictionaries. The provider system can be used for free text search and maybe an simple facet, but is not useful for more complex operations. Indexing the content types you have created, makes it possible to make strongly typed search queries, facets, and listings. All the properties you create in code, and all the properties in the base classes can be used in the search queries. The code will also be pretty and easy to understand when working against typed data. You will also get help from the compiler to write correct code when working with typed data.&lt;/p&gt;  &lt;h2&gt;Indexing catalog content using the find indexing job&lt;/h2&gt;  &lt;p&gt;We can make the “EPiServer Find Content Indexing Job” index catalog content for us using the code below.&lt;/p&gt;  &lt;div id=&quot;codeSnippetWrapper&quot; style=&quot;overflow: auto; cursor: text; font-size: 8pt; border-top: silver 1px solid; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; border-right: silver 1px solid; width: 97.5%; border-bottom: silver 1px solid; padding-bottom: 4px; direction: ltr; text-align: left; padding-top: 4px; padding-left: 4px; margin: 20px 0px 10px; border-left: silver 1px solid; line-height: 12pt; padding-right: 4px; max-height: 200px; background-color: #f4f4f4&quot;&gt;   &lt;div id=&quot;codeSnippet&quot; style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;     &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;[ServiceConfiguration(&lt;span style=&quot;color: #0000ff&quot;&gt;typeof&lt;/span&gt;(IReindexInformation))]&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;&lt;span style=&quot;color: #0000ff&quot;&gt;public&lt;/span&gt; &lt;span style=&quot;color: #0000ff&quot;&gt;class&lt;/span&gt; DescendetLinksOfCatalogRoot : IReindexInformation&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;{&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0000ff&quot;&gt;readonly&lt;/span&gt; IContentLoader _contentLoader;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0000ff&quot;&gt;readonly&lt;/span&gt; ReferenceConverter _referenceConverter;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;private&lt;/span&gt; &lt;span style=&quot;color: #0000ff&quot;&gt;readonly&lt;/span&gt; ILanguageBranchRepository _languageBranchRepository;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;public&lt;/span&gt; DescendetLinksOfCatalogRoot(ReferenceConverter referenceConverter,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;        IContentLoader contentLoader,&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;        ILanguageBranchRepository languageBranchRepository)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;        _contentLoader = contentLoader;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;        _referenceConverter = referenceConverter;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;        _languageBranchRepository = languageBranchRepository;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// Returns all descendents of the &amp;lt;see cref=&amp;quot;Root&amp;quot;/&amp;gt;.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;public&lt;/span&gt; IEnumerable&amp;lt;ReindexTarget&amp;gt; ReindexTargets&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;        get&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;        {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;            var catalogs = _contentLoader.GetChildren&amp;lt;CatalogContent&amp;gt;(Root);&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;            &lt;span style=&quot;color: #0000ff&quot;&gt;foreach&lt;/span&gt; (var catalogContent &lt;span style=&quot;color: #0000ff&quot;&gt;in&lt;/span&gt; catalogs)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;            {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;                var reindexTarget = &lt;span style=&quot;color: #0000ff&quot;&gt;new&lt;/span&gt; ReindexTarget()&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;                    ContentLinks = _contentLoader.GetDescendents(catalogContent.ContentLink)&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                };&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                var languages = catalogContent.ExistingLanguages.ToList();&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;                &lt;span style=&quot;color: #0000ff&quot;&gt;if&lt;/span&gt; (!languages.Select(x =&amp;gt; x.Name).Contains(catalogContent.DefaultLanguage))&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;                    languages.Add(CultureInfo.GetCultureInfo(catalogContent.DefaultLanguage));&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;                reindexTarget.Languages = languages;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;                &lt;span style=&quot;color: #0000ff&quot;&gt;yield&lt;/span&gt; &lt;span style=&quot;color: #0000ff&quot;&gt;return&lt;/span&gt; reindexTarget;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;            }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;        }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;&amp;#160;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// Gets the reference of the catalog root.&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    &lt;span style=&quot;color: #008000&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;    &lt;span style=&quot;color: #0000ff&quot;&gt;public&lt;/span&gt; ContentReference Root&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    {&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;        get { &lt;span style=&quot;color: #0000ff&quot;&gt;return&lt;/span&gt; _referenceConverter.GetRootLink(); }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: #f4f4f4&quot;&gt;    }&lt;/pre&gt;
&lt;!--CRLF--&gt;

    &lt;pre style=&quot;border-top-style: none; overflow: visible; font-size: 8pt; font-family: &amp;#39;Courier New&amp;#39;, courier, monospace; width: 100%; border-bottom-style: none; color: black; padding-bottom: 0px; direction: ltr; text-align: left; padding-top: 0px; border-right-style: none; padding-left: 0px; margin: 0em; border-left-style: none; line-height: 12pt; padding-right: 0px; background-color: white&quot;&gt;}&lt;/pre&gt;
&lt;!--CRLF--&gt;&lt;/div&gt;
&lt;/div&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2014/1/Indexing-catalog-content-using-EPiServer-Find-Content-Indexing-Job/</guid>            <pubDate>Tue, 14 Jan 2014 16:32:24 GMT</pubDate>           <category>Blog post</category></item><item> <title>Creating a commerce site in MVC – Part 2: Menus</title>            <link>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2013/12/Creating-a-commerce-site-in-MVC--Part-2-Menus/</link>            <description>&lt;p&gt;In my last blog post (&lt;a href=&quot;http://world.episerver.com/Blogs/Jonas-Bergqvist/Dates/2013/12/Tutorial-Creating-an-EPiServer-Commerce-site-in-MVC--Part-1/&quot;&gt;Creating a commerce site in MVC - Part 1&lt;/a&gt;), we created a site with core functionality that can be used on all your commerce MVC sites. We will continue with core functionality in this blog, but with focus on menus.&lt;/p&gt;  &lt;p&gt;I have got several questions the last year about menus, and how to create them in EPiServer projects. There is a good example for menus in the MVC sample project for CMS. We will create menu that looks similar as the MVC sample menu, but created for a commerce site. We will also create breadcrumb support, with inspiration from the CMS MVC sample site.&lt;/p&gt;  &lt;div id=&quot;scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:c34773b4-cc96-492b-92fa-28ac42a7d124&quot; class=&quot;wlWriterEditableSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;p&gt;The code for this tutorial can be downloaded &lt;a href=&quot;/link/b256468330bc4ef396b12be253586f24.zip&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;      &lt;h1&gt;Menu&lt;/h1&gt;  &lt;p&gt;The menu should be done very generic, with the possibility to define how many levels down in the tree the menu should render.&lt;/p&gt;  &lt;h2&gt;Models&lt;/h2&gt;  &lt;p&gt;We will create view models to make it possible to define how the menu will be rendered.&lt;/p&gt;  &lt;h3&gt;Menu model&lt;/h3&gt;  &lt;p&gt;We will start by creating a view model called “MenuModel”. The model will contain a starting point for the menu, and properties for the level in the menu. The model will also contain a property specifying if only content marked as “visible in menu” should be rendered.&lt;/p&gt;  &lt;p&gt;1. Create a new class called “MenuModel”, and place it it the ViewModels folder.    &lt;br /&gt;2. Add a property of type ContentReference, and name if “ContentLink”.     &lt;br /&gt;3. Add a property of type nullable byte, and name it “Levels”.     &lt;br /&gt;4. Add a property of type byte, and name it “CurrentLevel”.     &lt;br /&gt;5. Add a property of type boolean, and name it “RequireVisibleInMenu”.&lt;/p&gt;  &lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; MenuModel
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; ContentReference ContentLink { get; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;byte&lt;/span&gt;? Levels { get; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;byte&lt;/span&gt; CurrentLevel { get; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;bool&lt;/span&gt; RequireVisibleInMenu { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Menu model item&lt;/h3&gt;

&lt;p&gt;We will create another model called “MenuItem”. This model will represent each item that will be rendered. The model will contain a property for the menu model created earlier, and a property for the content that will be rendered. The view model will also contain a property for the routed content link, and a lazy property specifying if the content contains children.&lt;/p&gt;

&lt;p&gt;1. Create a new class called “MenuItem”, and place it it the ViewModels folder. 
  &lt;br /&gt;2. Add a property of type MenuModel, and name if “MenuModel”. Make the setter private. 

  &lt;br /&gt;3. Add a property of type IContent, and name it “Content”. Make the setter private. 

  &lt;br /&gt;4. Add a property of type ContentReference, and name it “RoutedContentLink”. Make the setter private. 

  &lt;br /&gt;5. Add a property of type lazy boolean, and name it “HasChildren”. Make the setter private. 

  &lt;br /&gt;6. Add a constructor taking parameters of the same types as the properties. Set the properties in the constructor.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; MenuItem
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; MenuItem(MenuModel menuModel, IContent content, ContentReference routedContentLink,Lazy&amp;lt;&lt;span class=&quot;kwrd&quot;&gt;bool&lt;/span&gt;&amp;gt; hasChildren)
    {
        MenuModel = menuModel;
        Content = content;
        RoutedContentLink = routedContentLink;
        HasChildren = hasChildren;
    }

    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; MenuModel MenuModel { get; &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; IContent Content { get; &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; ContentReference RoutedContentLink { get; &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; set; }
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; Lazy&amp;lt;&lt;span class=&quot;kwrd&quot;&gt;bool&lt;/span&gt;&amp;gt; HasChildren { get; &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; set; }
}&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Menu renderer&lt;/h2&gt;

&lt;p&gt;We will create a class that will render the menu. The class will use the view models we just created.&lt;/p&gt;

&lt;p&gt;1. Create a new class called “MenuRenderer”, and place it it the Business folder. 
  &lt;br /&gt;2. Create a read only field of type IContentLoader. Name it _contentLoader. 

  &lt;br /&gt;3. Create a read only field of type ILanguageSelector. Name it _languageSelector. 

  &lt;br /&gt;4. Create a read only field of type ILinksRepository. Name it _linksRepository. 

  &lt;br /&gt;5. Create a read only field of type PartialRouteHandler. Name it _partialRouteHandler. 

  &lt;br /&gt;6. Create a constructor that takes IContent, ILanguageSelectoras , ILinksRepositoryas, and PartialRouteHandler as it’s parameters. Set the fields using the parameters.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; MenuRenderer
{
    &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;readonly&lt;/span&gt; IContentLoader _contentLoader;
    &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;readonly&lt;/span&gt; ILanguageSelector _languageSelector;
    &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;readonly&lt;/span&gt; ILinksRepository _linksRepository;
    &lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;readonly&lt;/span&gt; PartialRouteHandler _partialRouteHandler;

    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; MenuRenderer(IContentLoader contentLoader, ILanguageSelector languageSelector,
                        ILinksRepository linksRepository, PartialRouteHandler partialRouteHandler)
    {
        _contentLoader = contentLoader;
        _languageSelector = languageSelector;
        _linksRepository = linksRepository;
        _partialRouteHandler = partialRouteHandler;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Visible in menu&lt;/h3&gt;

&lt;p&gt;We will create a private method that tells us if a specified content should be visible in the menu. The method will take one parameter of type content, and one of type MenuModel.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “VisibleInMenu”, which will return a boolean. 
  &lt;br /&gt;2. Add a parameter of type IContent, and call it “content”. 

  &lt;br /&gt;3. Add a parameter of type MenuModel, and call it “menu”. 

  &lt;br /&gt;4. Check if the property RequireVisibleInMenu is false in the menu parameter. If that’s the case, return true. 

  &lt;br /&gt;5. Try to cast the content to page data, if the content isn’t a page data object, return false. 

  &lt;br /&gt;6. If “VisibleInMenu” is set on the page data object, then return true, otherwise false.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;bool&lt;/span&gt; VisibleInMenu(IContent content, MenuModel menu)
{
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (!menu.RequireVisibleInMenu)
    {
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;false&lt;/span&gt;;
    }

    var pageData = content &lt;span class=&quot;kwrd&quot;&gt;as&lt;/span&gt; PageData;
    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; pageData != &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; pageData.VisibleInMenu;
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Get related entries&lt;/h3&gt;

&lt;p&gt;In CMS, it’s easy to get children for content using &amp;quot;GetChildren” in the content loader. In commerce, it’s possible to get the children in the same way for nodes, but not for products. If a product contains child products, or variants, the links repository has to be used to get the “children”. We will create a method, which gets all child entries for content that can contain variants.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “GetRelatedEntries”, which will return a IEnumerable&amp;lt;EntryContentBase&amp;gt;. 
  &lt;br /&gt;2. Add a parameter of type IVariantContainer, and call it “content”. 

  &lt;br /&gt;3. Get the variant relations using the extension method “GetVariantRelations” on the content parameter. Make a projection on the target property of the result, and store the result in a variable. Name the variable “relatedItems”. 

  &lt;br /&gt;4. Use the content loader to load the related items. Filter the result to only receive relations that inherit from EntryContentBase. Return the result.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; IEnumerable&amp;lt;EntryContentBase&amp;gt; GetRelatedEntries(IVariantContainer content)
{
    var relatedItems = content.GetVariantRelations(_linksRepository).Select(x =&amp;gt; x.Target);
    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; _contentLoader.GetItems(relatedItems, _languageSelector).OfType&amp;lt;EntryContentBase&amp;gt;();
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Create menu item&lt;/h3&gt;

&lt;p&gt;We will create another private method. This time it will be a method that creates an instance of the view model “MenuItem”. The method will take parameters for the menu model, the content for the menu item, and the routed content link.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “CreateMenuItem”, which will return a MenuItem. 
  &lt;br /&gt;2. Add a parameter of type IContent, and call it “content”. 

  &lt;br /&gt;3. Add a parameter of type MenuModel, and call it “menuModel”. 

  &lt;br /&gt;4. Add a parameter of type ContentReference, and call it “routedContentLink”. 

  &lt;br /&gt;5. Create a new lazy&amp;lt;bool&amp;gt; variable in the method, and use the content loader to determine if the content contains any children. Name the variable “hasChildren”. 

  &lt;br /&gt;6. Create a new instance of MenuModel, and set the properties RequireVisibleInMenu, and Levels to the same values as the menuModel parameter. 

  &lt;br /&gt;7. Set the content link in the new menu model to the content link of the content parameter. 

  &lt;br /&gt;8. Set the CurrentLevel property in the new menu model to the same value as the parameter menu model + 1. 

  &lt;br /&gt;9. Create and return an instance of MenuItem. Use the new model menu, the content, the routed content link, and the has children variables.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; MenuItem CreateMenuItem(IContent content, MenuModel menuModel, ContentReference routedContentLink)
{
    var hasChildren = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; Lazy&amp;lt;&lt;span class=&quot;kwrd&quot;&gt;bool&lt;/span&gt;&amp;gt;(() =&amp;gt; _contentLoader.GetChildren&amp;lt;IContent&amp;gt;(content.ContentLink).Any());
    var menuModelForItem = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; MenuModel
        {
            RequireVisibleInMenu = menuModel.RequireVisibleInMenu,
            Levels = menuModel.Levels,
            ContentLink = content.ContentLink,
            CurrentLevel = ++menuModel.CurrentLevel
        };

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; MenuItem(menuModelForItem, content, routedContentLink, hasChildren);
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;Create menu items&lt;/h3&gt;

&lt;p&gt;After we have created the method for creating a menu item, we will create a method that creates several menu items. The only parameters to this method will be the menu model, and the routed content link.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “CreateMenuItems”, which will return a IEnumerable&amp;lt;MenuItem&amp;gt;. 
  &lt;br /&gt;2. Add a parameter of type MenuModel, and call it “menuModel”. 

  &lt;br /&gt;3. Add a parameter of type ContentReference, and call it “routedContentLink”. 

  &lt;br /&gt;4. Get the children using “GetChildren” in the content loader. Save the result in a variable. Name the variable “children”. 

  &lt;br /&gt;5. Try to get the content for the menu content link as EntryContentBase. If the content is of type EntryContentBase, try to cast it to IVariantContainer. If the content is IVariantContainer, add related entries to the children variable using the “GetRelatedEntries” method. 

  &lt;br /&gt;6. Filter the children using the method “VisibleInMenu”. 

  &lt;br /&gt;7. Create menu items for the filtered children using “CreateMenuItem”. Return the menu items.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; IEnumerable&amp;lt;MenuItem&amp;gt; CreateMenuItems(MenuModel menuModel, ContentReference routedContentLink)
{
    var children = _contentLoader.GetChildren&amp;lt;IContent&amp;gt;(menuModel.ContentLink).ToList();

    EntryContentBase entry;
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (_contentLoader.TryGet(menuModel.ContentLink, _languageSelector, &lt;span class=&quot;kwrd&quot;&gt;out&lt;/span&gt; entry))
    {
        var variantContainer = entry &lt;span class=&quot;kwrd&quot;&gt;as&lt;/span&gt; IVariantContainer;
        &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (variantContainer != &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt;)
        {
            var relatedItems = GetRelatedEntries(variantContainer);
            children.AddRange(relatedItems);
        }
    }

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; children
        .Where(x =&amp;gt; VisibleInMenu(x, menuModel))
        .Select(x =&amp;gt; CreateMenuItem(x, menuModel, routedContentLink));
}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Render menu list&lt;/h3&gt;

&lt;p&gt;We will create a method called “RenderMenuList”. This method will create a html string containing the whole menu, supporting recursion. The method will use a delegate for rendering the menu items. By using a delegate, the method itself don’t have to contain any html.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “RenderMenuList”, which will return an “IHtmlString” instance. 
  &lt;br /&gt;2. Add a parameter of type MenuModel, and call it “menuModel”. 

  &lt;br /&gt;3. Add a parameter of type RequestContext, and call it “requestContext”. 

  &lt;br /&gt;4. Add a parameter of type Func&amp;lt;MenuItem, HelperResult&amp;gt;, and call it “itemTemplate”. 

  &lt;br /&gt;5. Check if the current menu level is greater than the specified menu levels. If that’s the case, return an empty MvcHtmlString. 

  &lt;br /&gt;6. Check if the ContentLinkin the menu is an empty reference. Return an empty MvcHtmlString if thats the case 

  &lt;br /&gt;7. Get the routed data from the requestContext parameter using the extension method “GetRoutedData&amp;lt;IContent&amp;gt;” in the EPiServer.Web.Routing namespace. Create a variable called “currentContent” that will hold the result. 

  &lt;br /&gt;8. Get the menu items using the method “CreateMenuItems”. 

  &lt;br /&gt;9. Create a string writer, and call the delegate for each menu item. Call the method “WriteTo” for each result from the deleage, and pass the string writer. 

  &lt;br /&gt;10. Create a MVC html string using the result from the string writer. Return the html string.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; IHtmlString RenderMenuList(MenuModel menuModel, RequestContext requestContext,
                                    Func&amp;lt;MenuItem, HelperResult&amp;gt; itemTemplate)
{
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (menuModel.Levels.HasValue &amp;amp;&amp;amp; menuModel.CurrentLevel &amp;gt; menuModel.Levels.Value)
    {
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; MvcHtmlString.Empty;
    }

    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (menuModel.ContentLink == ContentReference.EmptyReference)
    {
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; MvcHtmlString.Empty;
    }

    var routedData = requestContext.GetRoutedData&amp;lt;IContent&amp;gt;();
    var menuItems = CreateMenuItems(menuModel, routedData.ContentLink);

    var buffer = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; StringBuilder();
    &lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; (var writer = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; StringWriter(buffer))
    {
        &lt;span class=&quot;kwrd&quot;&gt;foreach&lt;/span&gt; (var menuItem &lt;span class=&quot;kwrd&quot;&gt;in&lt;/span&gt; menuItems)
        {
            itemTemplate(menuItem).WriteTo(writer);
        }
    }

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; MvcHtmlString(buffer.ToString());
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;Helper methods&lt;/h2&gt;

&lt;p&gt;We now have a class that can render a menu. One thing that is missing is an easy way for a view to call the class. We will create an extension method on “HtmlHelper” to solve that problem.&lt;/p&gt;

&lt;p&gt;1. Create a new folder called “Helpers” in the root of the site. 
  &lt;br /&gt;2. Create a new class in the folder called “HtmlHelpers”. Make the class static. 

  &lt;br /&gt;3. Create an extension method for HtmlHelper, that takes MenuModel, and Func&amp;lt;MenuItem, HelperResult&amp;gt; as paraneters. Name the method “MenuList”, and return an instance of IHtmlString.&amp;#160; &lt;br /&gt;4. Get an instance of the MenuRenderer class using the service locator. Set the result to a variable named “menuRenderer”. 

  &lt;br /&gt;5. Call “RenderMenuList” on the instance. Get the requestContext using helper.ViewContext.RequestContext. 

  &lt;br /&gt;6. Return the result.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; HtmlHelpers
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;static&lt;/span&gt; IHtmlString MenuList(&lt;span class=&quot;kwrd&quot;&gt;this&lt;/span&gt; HtmlHelper helper, MenuModel menuModel,
                                Func&amp;lt;MenuItem, HelperResult&amp;gt; itemTemplate)
    {
        var menuRenderer = ServiceLocator.Current.GetInstance&amp;lt;MenuRenderer&amp;gt;();
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; menuRenderer.RenderMenuList(menuModel, helper.ViewContext.RequestContext, itemTemplate);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;Partial view&lt;/h2&gt;

&lt;p&gt;We will now make use of the class we have created. The view we will create is just an example of a menu that uses the helper method. Many views can be created with different HTML, that uses the method “MenuList”.&lt;/p&gt;

&lt;h3&gt;Menu&lt;/h3&gt;

&lt;p&gt;We will add a partial view, where we will place a function that will be used as the delegate in the “RenderMenuList” method. The method will work recursive, by calling the “RenderMenuList” method inside the method with it self as delegate.&lt;/p&gt;

&lt;p&gt;1. Create a new partial view in the “Shared” folder under “Views”. Call the view “Menu”, and set the model to MenuModel. 
  &lt;br /&gt;2. Register a method using the @helper keyword. Name the method “ItemTemplate”, and let it take a MenuItem as parameter. 

  &lt;br /&gt;3. In the method, create a link to the menu item content using the HTML helper “ContentLink”. The helper method is located in the EPiServer.Web.Mvc.Html namespace. 

  &lt;br /&gt;4. Call the HTML helper “MenuList” with the menu model, and the method (ItemTemplate). 

  &lt;br /&gt;5. Outside the method, call the HTML helper “MenuList” with the model, and the method (ItemTemplate).&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@using EPiServer.Commerce.SampleMvc.Models.ViewModels
@using EPiServer.Web.Mvc.Html
@using EPiServer.Commerce.SampleMvc.Helpers

@model MenuModel

@helper ItemTemplate(MenuItem menuItem)
{
    @Html.ContentLink(menuItem.Content)
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;/&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    @Html.MenuList(menuItem.MenuModel, ItemTemplate)
}

@Html.MenuList(Model, ItemTemplate)&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;IPageViewModel&lt;/h2&gt;

&lt;p&gt;We will create a top menu property for the interface IPageViewModel. The IPageViewModel will be implemented by all view models in our project, which makes it possible for us to use the property in the layout file.&lt;/p&gt;

&lt;p&gt;1. Add a new property to the interface of type MenuModel. Name it “TopMenu”.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;interface&lt;/span&gt; IPageViewModel&amp;lt;&lt;span class=&quot;kwrd&quot;&gt;out&lt;/span&gt; T&amp;gt;
    &lt;span class=&quot;kwrd&quot;&gt;where&lt;/span&gt; T : PageData
{
    T CurrentPage { get; }
    MenuModel TopMenu { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;PageViewModel&lt;/h2&gt;

&lt;p&gt;We have to implement the new property that we just added to the interface.&lt;/p&gt;

&lt;p&gt;1. Add a new property to the interface of type MenuModel. Name it “TopMenu”.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;class&lt;/span&gt; PageViewModel&amp;lt;T&amp;gt; : IPageViewModel&amp;lt;T&amp;gt;
    &lt;span class=&quot;kwrd&quot;&gt;where&lt;/span&gt; T : PageData
{
    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; PageViewModel(T currentPage)
    {
        CurrentPage = currentPage;
    }

    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; T CurrentPage
    {
        get;
        set;
    }

    &lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; MenuModel TopMenu { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;ContentViewModelFactory&lt;/h2&gt;

&lt;p&gt;We now have a property on the IPageViewModel, which should be set in the ContentViewModelFactory.&lt;/p&gt;

&lt;p&gt;When looking at the factory class, we can see that we made a little miss in the first tutorial. The methods “InitializeCatalogContentViewModel” and “InitializeVariationContentViewModel” should have called the method “InitializePageViewModel”. We will set the TopMenu property in the “InitializePageViewModel”, and therefore it’s important to fix our miss at this point. The menu should only render children at the top level.&lt;/p&gt;

&lt;p&gt;1. Call InitializePageViewModel with the model inside the InitializeVariationContentViewModel method. 
  &lt;br /&gt;2. Call InitializePageViewModel with the model inside the InitializeCatalogContentViewModel method. 

  &lt;br /&gt;3. Inside the InitializePageViewModel method, create a new instance of the menu model. 

  &lt;br /&gt;4. Set the content link property in the menu model instance to the start page of the site, and levels to 1. 

  &lt;br /&gt;5. Set the levels property in the menu model instance to 1.&amp;#160; &lt;br /&gt;6. Set the menu model to require visible in menu.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;void&lt;/span&gt; InitializeVariationContentViewModel&amp;lt;TViewModel&amp;gt;(TViewModel model, &lt;span class=&quot;kwrd&quot;&gt;string&lt;/span&gt; warehouseCode)
    &lt;span class=&quot;kwrd&quot;&gt;where&lt;/span&gt; TViewModel : IVariationContentViewModel&amp;lt;VariationContent, PageData&amp;gt;
{
    InitializePageViewModel(model);
    model.Inventory = GetInventory(model.CatalogContent, warehouseCode);
    model.Price = GetPrices(model.CatalogContent);
    model.ParentProduct = GetParentProduct(model.CatalogContent, model.CurrentPage);
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;void&lt;/span&gt; InitializeCatalogContentViewModel&amp;lt;TViewModel&amp;gt;(TViewModel model)
    &lt;span class=&quot;kwrd&quot;&gt;where&lt;/span&gt; TViewModel : ICatalogContentViewModel&amp;lt;CatalogContentBase, PageData&amp;gt;
{
    InitializePageViewModel(model);
    model.ChildCategories = GetChildNodes(model.CatalogContent.ContentLink);
    model.Products = CreateLazyProductContentViewModels(model.CatalogContent, model.CurrentPage);
    model.Variants = CreateLazyVariantContentViewModels(model.CatalogContent, model.CurrentPage);
}&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;void&lt;/span&gt; InitializePageViewModel&amp;lt;TViewModel&amp;gt;(TViewModel model)
    &lt;span class=&quot;kwrd&quot;&gt;where&lt;/span&gt; TViewModel : IPageViewModel&amp;lt;PageData&amp;gt;
{
    model.TopMenu = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; MenuModel
        {
            ContentLink = ContentReference.StartPage,
            Levels = 1
            RequireVisibleInMenu = &lt;span class=&quot;kwrd&quot;&gt;true&lt;/span&gt;
        };
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;
  

  &lt;h2&gt;Layout&lt;/h2&gt;

  
&lt;/h2&gt;

&lt;p&gt;We will make use of everything we just have created in the layout file.&lt;/p&gt;

&lt;p&gt;1. In the _layout.cshtml (Views-&amp;gt;Shared), add a partial request to the “Menu” view inside the body element. Use the top menu property on the model as the model for the menu.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;Menu: @Html.Partial(&amp;quot;Menu&amp;quot;, Model.TopMenu)&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attr&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;=&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
        @RenderBody()
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h1&gt;Breadcrumb&lt;/h1&gt;

&lt;p&gt;A common type of menu is the breadcrumb, which pretty much is a must on a commerce site.&lt;/p&gt;

&lt;h2&gt;Menu renderer&lt;/h2&gt;

&lt;p&gt;We will extend the menu renderer to get support for breadcrumbs.&lt;/p&gt;

&lt;h3&gt;GetCommerceRouters&lt;/h3&gt;

&lt;p&gt;To be able to get a whole breadcrumb for catalog content, we need to be able to get the commerce route that are used for the current catalog content. We will create a method that gets all commerce routers for a specific partial route handler. We will also cache the result to gain some performance.&lt;/p&gt;

&lt;p&gt;1. In the “MenuRenderer” class create a static field of type IEnumerable&amp;lt;ICommerceRouter&amp;gt;. Name it _commerceRouters. 
  &lt;br /&gt;2. Create a new method called “GetCommerceRouters”, which will return IEnumerable&amp;lt;ICommerceRouter&amp;gt;. 

  &lt;br /&gt;3. If the _commerceRouters field is set, return the field. 

  &lt;br /&gt;4. If the field is null, get the routers using “GetIncomingRouters” with the page data type on the partial route handler. 

  &lt;br /&gt;5. Filter the result to only get routes of type “PartialRouter&amp;lt;PageData, CatalogContentBase&amp;gt;. 

  &lt;br /&gt;6. Make a projection of the filter items to only receive the router, and cast the result to ICommerceRouter. 

  &lt;br /&gt;7. Set the field _commerceRouters to the result. 

  &lt;br /&gt;8. Return the field.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; IEnumerable&amp;lt;ICommerceRouter&amp;gt; _commerceRouters;&lt;/code&gt;&lt;/pre&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;private&lt;/span&gt; IEnumerable&amp;lt;ICommerceRouter&amp;gt; GetCommerceRouters()
{
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (_commerceRouters == &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt;)
    {
        _commerceRouters = _partialRouteHandler.GetIncomingRouters(&lt;span class=&quot;kwrd&quot;&gt;typeof&lt;/span&gt;(PageData))
                            .OfType&amp;lt;PartialRouter&amp;lt;PageData, CatalogContentBase&amp;gt;&amp;gt;()
                            .Select(x =&amp;gt; x.Router)
                            .OfType&amp;lt;ICommerceRouter&amp;gt;();
    }

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; _commerceRouters;
}&lt;style type=&quot;text/css&quot;&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;ExecuteForContent&lt;/h3&gt;

&lt;p&gt;We will now create a method, which will execute a delegate for a specified content link. The method will use the method “GetCommerceRouters” to change from catalog content to page data when the content link is the route starting point.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “ExecuteForContent”, which will return an “IHtmlString” instance. 
  &lt;br /&gt;2. Add a parameter of type ContentReference, and call it “contentLink”. 

  &lt;br /&gt;3. Add a parameter of type Func&amp;lt;IContent, HelperResult&amp;gt;, and call it “itemTemplate”. 

  &lt;br /&gt;4. In the method, check if the content link is empty or the root page. If that’s the case, return an empty html string. 

  &lt;br /&gt;5. Get the commerce route using the method “GetCommerceRouters”, and check if any of those has the content link set as it’s commerce root. 

  &lt;br /&gt;6. If a commerce route was found with the root set to the content link, set the content link to the starting point of the commerce route. 

  &lt;br /&gt;7. Try to get the content for the content link using the content loader. If the content can’t be found, return an empty html string. 

  &lt;br /&gt;8. Create a string writer, and call the delegate for the content. Call the method “WriteTo” for the result from the deleage, and pass the string writer. 

  &lt;br /&gt;9. Create a MVC html string using the result from the string writer. Return the html string.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; IHtmlString ExecuteForContent(ContentReference contentLink, Func&amp;lt;IContent, HelperResult&amp;gt; itemTemplate)
{
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (contentLink == ContentReference.EmptyReference || contentLink == ContentReference.RootPage)
    {
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; MvcHtmlString.Empty;
    }

    var commerceRoute = GetCommerceRouters().FirstOrDefault(x =&amp;gt; x.CommerceRoot.ContentLink.CompareToIgnoreWorkID(contentLink));
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (commerceRoute != &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt;)
    {
        contentLink = commerceRoute.RouteStartingPoint;
    }

    IContent content;
    &lt;span class=&quot;kwrd&quot;&gt;if&lt;/span&gt; (!_contentLoader.TryGet(contentLink, _languageSelector, &lt;span class=&quot;kwrd&quot;&gt;out&lt;/span&gt; content))
    {
        &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; MvcHtmlString.Empty;
    }

    var buffer = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; StringBuilder();
    &lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; (var writer = &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; StringWriter(buffer))
    {
        itemTemplate(content).WriteTo(writer);
    }

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; MvcHtmlString(buffer.ToString());
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h3&gt;ExecuteForParent&lt;/h3&gt;

&lt;p&gt;We have a method for executing a delegate for a content link. We will now create another method for executing a delegate for the parent. In this method, we will use the current model to get the parent item.&lt;/p&gt;

&lt;p&gt;1. Create a new method called “ExecuteForParent”, which will return an “IHtmlString” instance. 
  &lt;br /&gt;2. Add a parameter of type IPageViewModel&amp;lt;PageData&amp;gt;, and call it “model”. 

  &lt;br /&gt;3. Add a parameter of type Func&amp;lt;IContent, HelperResult&amp;gt;, and call it “itemTemplate”. 

  &lt;br /&gt;4. In the method, try to cast the model to ICatalogContentLeafViewModel&amp;lt;CatalogContentBase, PageData&amp;gt;. 

  &lt;br /&gt;5. Create a new variable, and set it to the catalog content if the model was successfully casted, otherwise set it to current page using the model parameter. Name the variable “content”. 

  &lt;br /&gt;6. Call “ExecuteForContent” with the the parent link for the content, and the delegate. Return the result.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; IHtmlString ExecuteForParent(IPageViewModel&amp;lt;PageData&amp;gt; model, Func&amp;lt;IContent, HelperResult&amp;gt; itemTemplate)
{
    var catalogContentViewModel = model &lt;span class=&quot;kwrd&quot;&gt;as&lt;/span&gt; ICatalogContentLeafViewModel&amp;lt;CatalogContentBase, PageData&amp;gt;;
    var currentContent = catalogContentViewModel != &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt; ? catalogContentViewModel.CatalogContent : model.CurrentPage &lt;span class=&quot;kwrd&quot;&gt;as&lt;/span&gt; IContent;

    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; ExecuteForContent(currentContent.ParentLink, itemTemplate);
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;Helper methods&lt;/h2&gt;

&lt;p&gt;We will create helper methods for the methods “ExecuteForContent”, and “ExecuteForParent”. We will also create a helper method that returns the name of the routed content.&lt;/p&gt;

&lt;p&gt;1. Create an extension method for HtmlHelper in the HtmlHelpers class, that takes ContentReference, and Func&amp;lt;IContent, HelperResult&amp;gt; as parameters. Name the method “ExecuteForContent”, and set the return type to IHtmlString.
  &lt;br /&gt;2. Get an instance of the MenuRenderer class using the service locator. Set the result to a variable named “menuRenderer”. 

  &lt;br /&gt;3. Call “ExecuteForContent” on the instance, and return the result. 

  &lt;br /&gt;4. Create another extension method for HtmlHelper, that takes IPageViewModel&amp;lt;PageData&amp;gt;, and Func&amp;lt;IContent, HelperResult&amp;gt; as parameters. Name the method “ExecuteForParent”, and set the return type to IHtmlString.

  &lt;br /&gt;5. Get an instance of the MenuRenderer class using the service locator. Set the result to a variable named “menuRenderer”. 

  &lt;br /&gt;6. Call “ExecuteForParent” on the instance, and return the result. 

  &lt;br /&gt;7. Create an extension method for HtmlHelper in the HtmlHelpers class, and name it RoutedContentName. Return IHtmlString for the method. 

  &lt;br /&gt;8. Create a variable called “routedData” and set it to the routed data using the extension method “GetRoutedData” in the EPiServer.Web.Routing namespace. 

  &lt;br /&gt;9. If the routed data isn’t null, return the html string containing the name of the routed data. Otherwise return an empty html string.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;static&lt;/span&gt; IHtmlString ExecuteForContent(&lt;span class=&quot;kwrd&quot;&gt;this&lt;/span&gt; HtmlHelper helper, ContentReference contentLink,
                            Func&amp;lt;IContent, HelperResult&amp;gt; itemTemplate)
{
    var menuRenderer = ServiceLocator.Current.GetInstance&amp;lt;MenuRenderer&amp;gt;();
    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; menuRenderer.ExecuteForContent(contentLink, itemTemplate);
}

&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;static&lt;/span&gt; IHtmlString ExecuteForParent(&lt;span class=&quot;kwrd&quot;&gt;this&lt;/span&gt; HtmlHelper helper, IPageViewModel&amp;lt;PageData&amp;gt; model,
                            Func&amp;lt;IContent, HelperResult&amp;gt; itemTemplate)
{
    var menuRenderer = ServiceLocator.Current.GetInstance&amp;lt;MenuRenderer&amp;gt;();
    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; menuRenderer.ExecuteForParent(model, itemTemplate);
}

&lt;span class=&quot;kwrd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kwrd&quot;&gt;static&lt;/span&gt; IHtmlString RoutedContentName(&lt;span class=&quot;kwrd&quot;&gt;this&lt;/span&gt; HtmlHelper helper)
{
    var routedData = helper.ViewContext.RequestContext.GetRoutedData&amp;lt;IContent&amp;gt;();
    &lt;span class=&quot;kwrd&quot;&gt;return&lt;/span&gt; routedData != &lt;span class=&quot;kwrd&quot;&gt;null&lt;/span&gt; ? &lt;span class=&quot;kwrd&quot;&gt;new&lt;/span&gt; HtmlString(routedData.Name) : MvcHtmlString.Empty;
}&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;Partial view&lt;/h2&gt;

&lt;p&gt;We will now make use of the methods we have created.&lt;/p&gt;

&lt;h3&gt;Breadcrumb&lt;/h3&gt;

&lt;p&gt;We will add a partial view, where we will place a function that will be used as the delegate in the “ExecuteForContent” and “ExecuteForParent” methods. The method will work recursive, by calling the “ExecuteForContent” method inside the method with it self as delegate.&lt;/p&gt;

&lt;p&gt;1. Create a new partial view in the “Shared” folder under “Views”. Call the view “Breadcrumb”, and set the model to IPageViewModel&amp;lt;PageData&amp;gt;. 
  &lt;br /&gt;2. Register a method using the @helper keyword. Name the method “ItemTemplate”, and let it take a IContent as parameter. 

  &lt;br /&gt;3. Call the HTML helper “ExecuteForContent” with the parent link from the parameter, and the method (ItemTemplate). 

  &lt;br /&gt;4. In the method, create a link to the menu item content using the HTML helper “ContentLink”. The extension method is located in the EPiServer.Web.Mvc.Html namespace. 

  &lt;br /&gt;5. Outside the method, call the HTML helper “ExecuteForParent” with the model, and the method (ItemTemplate). 

  &lt;br /&gt;6. Outside the method, call the HTML helper “RoutedContentName”. 

  &lt;br /&gt;&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;@&lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; EPiServer.Commerce.SampleMvc.Models.ViewModels
@&lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; EPiServer.Core
@&lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; EPiServer.Commerce.SampleMvc.Helpers
@&lt;span class=&quot;kwrd&quot;&gt;using&lt;/span&gt; EPiServer.Web.Mvc.Html

@model IPageViewModel&amp;lt;PageData&amp;gt;

@helper ItemTemplate(IContent content)
{
    @Html.ExecuteForContent(content.ParentLink, ItemTemplate)
    &amp;lt;span&amp;gt;/&amp;lt;/span&amp;gt;
    @Html.ContentLink(content)
}

@Html.ExecuteForParent(Model, ItemTemplate)
&amp;lt;span&amp;gt;/&amp;lt;/span&amp;gt;
@Html.RoutedContentName()&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h2&gt;Layout&lt;/h2&gt;

&lt;p&gt;We will make use of the breadcrumb we just have created in the layout file.&lt;/p&gt;

&lt;p&gt;1. In the _layout.cshtml (Views-&amp;gt;Shared), add a partial request to the “Breadcrumb” view inside the body element. Use the current model as model for the breadcrumb.&lt;/p&gt;

&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;Menu: @Html.Partial(&amp;quot;Menu&amp;quot;, Model.TopMenu)&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;Breadcrumb: @Html.Partial(&amp;quot;Breadcrumb&amp;quot;, Model)&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;attr&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;=&amp;quot;container&amp;quot;&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;
        @RenderBody()
    &lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;kwrd&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;html&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;kwrd&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;style type=&quot;text/css&quot;&gt;


.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, &quot;Courier New&quot;, courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }&lt;/style&gt;

&lt;h1&gt;Next part&lt;/h1&gt;

&lt;p&gt;Next part will probably contain interaction with EPiServer Find/Owl. Happy coding!&lt;/p&gt;</description>            <guid>https://world.optimizely.com/blogs/Jonas-Bergqvist/Dates/2013/12/Creating-a-commerce-site-in-MVC--Part-2-Menus/</guid>            <pubDate>Mon, 23 Dec 2013 22:18:41 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>