Using Collection(Edm.String) in Azure Search

Mike McDermot asked on July 1, 2020 16:03

I would like to be able to use the Azure search.in operator to quickly filter out search documents based on custom tags. That operator requires the corresponding field to both be tagged as filterable on the index definition and to be a collection data type (in my case I'd like to use Collection(Edm.String)

Does anyone have an example where they have registered a custom data type (say...string[]) to Collection(Edm.String) in Kentico? One problem I see is that the data types for fields on a page type (which is being used to drive the creation of custom fields on the search index) do not support collections in any sort of capacity.

Here is the documentation I've been using. The example here is with a decimal and I've seen a blog post that maps a string to GeoLocation Azure Search type, but I need an example that maps a string (or better yet, string[]) to a Collection

https://docs.kentico.com/k12sp/configuring-kentico/setting-up-search-on-your-website/using-azure-search/customizing-azure-search/registering-custom-data-types-for-azure-search

Correct Answer

Brenden Kehren answered on July 1, 2020 22:41

Following the documentation link you shared, are you able to use something like this? Where field is the actual field being created in the the global handler?

using CMS;  
using CMS.DataEngine;
using CMS.Search.Azure;
using System;
using CMS.Helpers;
using CMS.DocumentEngine;
using Microsoft.Azure.Search.Models;
using System.Collections.ObjectModel;

[assembly: RegisterModule(typeof(CustomAzureSearchModule))]
public class CustomAzureSearchModule : Module
{
    public const string AZURE_SEARCH_INDEX = "customallcontent";

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

    protected override void OnInit()
    {
        base.OnInit();
        DocumentFieldCreator.Instance.CreatingField.After += CreatingField_After;
    }

    /// <summary>
    /// Add some custom properties to specific fields which cannot be set in the UI. 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    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("CustomFieldName", StringComparison.OrdinalIgnoreCase))
        {
            field.IsSortable = true;
            field.IsSearchable = true;
            field.Type = Microsoft.Azure.Search.Models.DataType.Collection(Microsoft.Azure.Search.Models.DataType.String);
        }
    }
}
0 votesVote for this answer Unmark Correct answer

Recent Answers


Mike McDermot answered on July 2, 2020 15:05

Yes, that works. It is basically what I have now, the problem is that my Kentico field is a string, and the corresponding Azure Search index field is a collection of strings.

When Kentico goes to rebuild the index, and error is thrown because it tries to stuff a null into the Azure Search index...and field types that are collections don't allow nulls, they have to be empty collections if there is no data (and in this case, there is legitimately no data for this new custom field for some of my content)

So what I really need is a way to map my Kentico string into a collection. I plan to store the string as a comma delimited value, so with the mapping function I can split on the comma and return a collection. If the string is null or empty I can return an empty collection, that way Kentico never sends Azure a null value for the collection.

It's just that the existing example code I've seen doesn't show how to map to a collection...in fact I'm not even sure it's possible, but I would expect it is since Collection(Edm.String) is a default type in Azure Search...there must be some way for Kentico to support that type.

0 votesVote for this answer Mark as a Correct answer

Brenden Kehren answered on July 2, 2020 15:16 (last edited on July 2, 2020 15:16)

Given MS SQL doesn't have a datatype of Collection(Edm.String), I'm not sure why they would offer any type of conversion for that datatype, except to support Azure Search only.

That being said in that same code I posted above, add another Global Event to modify the values of the field(s) you're looking to convert.

DocumentCreator.Instance.AddingDocumentValue.Execute += AddingDocumentValue_Execute;

/// <summary>
/// Updating the path to be absoulte vs. relative.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddingDocumentValue_Execute(object sender, AddDocumentValueEventArgs e)
{
    if (!e.Name.Equals("nodealiaspath", StringComparison.OrdinalIgnoreCase))
    {
        return;
    }

    string nodeAliasPath = e.Value as string;
    string culture = e.SearchDocument.GetValue("documentculture").ToString().ToLower();

    if (string.IsNullOrWhiteSpace(nodeAliasPath))
    {
        return;
    }

    e.Value = URLHelper.GetAbsoluteUrl(DocumentURLProvider.GetUrl(nodeAliasPath.ToLowerInvariant(), null, null, culture, null));
}

As you can see by the comment for this event, I'm updating the path to be absolute vs. relative (client requirement to mix with results from other datasources). I'd think you'd be able to get that comma separated value and parse it out into a Collection(Edm.Sting) in that event. If you're looking for specifics on how to create that collection, you'd have to check the Azure/Microsoft docs for that.

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.