Kentico CMS Quick Tip: Azure Search with Faceted Document Categories
Tags: kentico, azure, azuresearch, dotnet
Published: 2019-07-08 15:16:46

---
title: Kentico CMS Quick Tip: Azure Search with Faceted Document Categories
published: true
description: Check out this quick tip to help you take advantage of Kentico's integration with Azure Search and the advanced feature it offers, like faceting.
tags: kentico, azure, azuresearch, dotnet
series: Kentico Quick Tips
---

![](https://thepracticaldev.s3.amazonaws.com/i/3nqvvw3197p3bbu7t1xl.jpg)
<figcaption>Photo by <a href="https://unsplash.com/@amartino20">Anthony Martino</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

## Kentico 💕 Azure Search

Kentico has included custom search indexing for [a long time](https://devnet.kentico.com/docs/5_0/devguide/index.html) and has traditionally provided this functionality through [Lucene.NET](https://lucenenet.apache.org/).

The locally (in-process) hosted search indexing is [simple to enable](https://docs.kentico.com/k12sp/configuring-kentico/setting-up-search-on-your-website/using-locally-stored-search-indexes) and scales well with the CMS but there are some caveats. 😯

> Note: These caveats might not apply to you. If they don't, Kentico's built-in local Smart Search is a great solution.

- ❌ Scaling search indexing and querying due to high traffic requires scaling the entire CMS.
- ❌ More complex search functionality like [faceted navigation](https://docs.microsoft.com/en-us/azure/search/search-faceted-navigation) is not supported.
- ❌ Advanced functionality like [Cognitive Search](https://docs.microsoft.com/en-us/azure/search/cognitive-search-concept-intro) and [auto-complete](https://docs.microsoft.com/en-us/azure/search/tutorial-csharp-type-ahead-and-suggestions) are not realistic possibilities.

Fortunately, Kentico also has a deep integration into [Azure Search](https://docs.microsoft.com/en-us/azure/search/search-what-is-azure-search) out of the box. 👍

It enables simple use cases [without any custom code](https://docs.kentico.com/k12sp/configuring-kentico/setting-up-search-on-your-website/using-azure-search/customizing-azure-search), in much the same way as local Smart Search.

More complex use cases can be solved through hooks into the [search index creation process](https://docs.kentico.com/k12sp/configuring-kentico/setting-up-search-on-your-website/using-azure-search/customizing-azure-search).

Since Azure Search exists as an enterprise level search solution, developers can leverage its full set of querying options directly, using the [Microsoft.Azure.Search](https://www.nuget.org/packages/Microsoft.Azure.Search) NuGet package or the OData querying API directly. 🙂

## Faceting

Faceting is an awesome feature that can help your site's users find what they are looking for or explore your content in a much more intuitive way.

To quote the Azure Search docs:

> Facets can help you find what you're looking for, while ensuring that you don’t get zero results. [...] In online retail applications, faceted navigation is often built over brands, departments (kid’s shoes), size, price, popularity, and ratings.

If we look at [HomeDepot.com](https://www.homedepot.com/) we can see faceting in action:

![](https://thepracticaldev.s3.amazonaws.com/i/b8x10dl47kliiyfe89ds.jpg)

The counts of the results on the page that fall into each of the taxonomies available are seen in this list. 🤔

In Kentico, an intuitive way of grouping content into a set of taxonomies like this is by using [Categories](https://docs.kentico.com/k12sp/managing-website-content/working-with-pages/categorizing-pages/assigning-pages-to-categories).

Kentico includes both the `CategoryInfo.CategoryDisplayName` and `CategoryInfo.CategoryID` values, for all categories for a document, in the search index as `documentcategories` and `documentcategoryids` fields respectively.

However, by default, these fields are not facetable. 😞

What we want is for the search index that Kentico generates in our Azure Search instance to make one or the other facetable. In the example below I'm going to work with the `documentcategories` field. 

Here is what we should end up with when we look at our index's fields in the Azure Portal:

![](https://thepracticaldev.s3.amazonaws.com/i/l3v0t4qej5n9gy6ji2gu.jpg)

## Custom Azure Search Module

The customization on the Kentico side is actually pretty simple - we just need to create a custom `Module` and tap into the Azure Search index creation events that Kentico exposes. 🧐

```csharp
    public class CustomAzureSearchModule : Module
    {
        public const string AZURE_SEARCH_INDEX = "put-your-index-code-name-here";

        public CustomAzureSearchModule() : base(nameof(CustomAzureSearchModule)) { }

        protected override void OnInit()
        {
            base.OnInit();

            DocumentFieldCreator.Instance.CreatingField.After += CreatingField_After;
        }

        private void CreatingField_After(object sender, CreateFieldEventArgs e)
        {
            // If you have multiple indexes and only want category faceting on one
            if (!e.SearchIndex.IndexCodeName.Equals(AZURE_SEARCH_INDEX, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            var field = e.Field;

            if (field.Name.Equals("DocumentCategories", StringComparison.OrdinalIgnoreCase))
            {
                field.IsFacetable = true;
            }
        }
    }
```

So, what are we doing here?

First, we create a custom `Module` that Kentico grabs, through reflection, and executes during the application startup.

We create an event handler for `DocumentFieldCreator.Instance.CreatingField.After` so that we can customize the search index configuration for a field after Kentico has setup the defaults for that field.

Finally, we match on a specific field that is being added to the search index - `DocumentCategories` - and we set that field's `IsFacetable` property to `true`.

After adding this customization to your site, make sure you re-build the Azure Search Index in the CMS "Smart Search" module.

Easy, peasy. 😎

## Example: Document Category Faceting

The `DocumentCategories` field is now facetable, which means when we query the Azure Search API for search results, it will return the facet counts per category for the specific results returned.

Assume we have (3) documents which are categorized as "Cool", "Fun", and "Potato" (maybe we should have a word with the content editors 🤣).

1. "Cool", "Fun", "Potato"
1. "Fun"
1. "Fun", "Potato"

A default search (no filtering applied) should return all documents and the following facet counts:

- "Cool" (1)
- "Fun" (3)
- "Potato" (2)

If you want to test out your search index configuration, go to the Azure Portal blade for your Azure Search instance, select the correct index and type the following into the "query string" field of the "Search Explorer" tab:

`facet=documentcategories&$filter=documentcategories/any(dc: dc eq 'Potato')`

This should return (2) results, documents 1 and 3. 👌

## Querying From Kentico 12 MVC

To get this faceting integrated into your Kentico 12 MVC site you'll need to do a bit more coding. There's a pretty thorough example [in the Kentico documentation](https://docs.kentico.com/k12sp/configuring-kentico/setting-up-search-on-your-website/using-azure-search/integrating-azure-search-into-mvc-projects) with tons of sample code. 👏

However, this example doesn't include faceting with a collection.

> All fields in Azure Search indexes have specific data types. The `documentcategories` field has a type of `Collection(Edm.String)` which is different than the fields queried in the Kentico docs example.

To query against a facetable field that is a collection of values you'll need to have a slightly [more advanced query string](https://docs.microsoft.com/en-us/azure/search/search-query-odata-collection-operators) passed to your `Microsoft.Azure.Search.Models.SearchParameters` instance.

> Hint: It's the one above used for testing in the Azure "Search Explorer". 😁

Here's an example of how to generate this query string:

```csharp
public class FacetFilterViewModel
{
    public string Name { get; set; }

    public string Value { get; set; }

    public bool IsSelected { get; set; }

    public FacetFilterViewModel()
    {
        Name = "";
        Value = "";
        IsSelected = false;
    }
}

// In a separate class

public const string FACET_CATEGORIES = "documentcategories";

public SearchParameters BuildSearchParameters(IEnumerable<FacetFilterViewModel> facetFilters)
{
    var categoryFilters = facetFilters
        .Where(x => x.IsSelected)
        .Select(item => $"{FACET_CATEGORIES}/any(dc: dc eq '{item.Value.Replace("'", "''")}')");

    string categoryFilterTerm = string.Join(" or ", categoryFilters);

    return new SearchParameters
    {
        Filter = categoryFilterTerm,
        Facets = new List<string>
        {
            FACET_CATEGORIES
        }
    };
}
```

These `SearchParameters` can be passed to your call to the `ISearchIndexClient` (found in the Kentico docs code example) as follows:

```csharp
// These come from the form submission, which is defined in a Razor View

// IEnumerable<FacetFilterViewModel> facetFilters

// string searchTerm

var searchParameters = BuildSearchParameters(facetFilters);

var result = await client.Documents.SearchAsync(searchTerm, searchParameters);

// See the Kentico docs example code on how to use "result"
```

This should give you faceted Category results that can be rendered out in a Razor View much like the screenshot from Home Depot above (the design is up to you!). 🤓

## Summary

Looking back, we can see we now know the following:

1. Kentico supports both local search indexes and Azure Search indexes.
1. Azure Search provides more advanced scaling and features.
1. Integrating with Azure Search in Kentico is pretty simple.
1. Faceting is a great feature for helping site visitors search your content.
1. Document Categories are not facetable by default, but this can be customized.
1. Customizing Azure Search in Kentico is done through indexing events.
1. To render faceted search results for a collection field you need to generate a more complex query string.

This post was meant to serve as a helpful walkthrough to integrating Kentico with Azure Search - specifically the faceting feature when applied to Document Categories.

Thanks for reading! 🙏

---

I hope you enjoyed this Quick Tip. I plan on posting [more tips in this series](https://dev.to/search?q=Kentico%20Quick%20Tips).

If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:

{% tag kentico %}

or my [Kentico 12: Design Patterns](https://dev.to/search?q=Kentico%2012%20-%20Design%20Patterns) series.