Is it possible for me to index values from components in a block grid field?

Hi all,

Our clients wants to search on actual content within a page. We’re using Umbraco Cloud on Umbraco 16 and we have setup the pages as follows:

We have created a document type for the homepage for example with the following fields

  • Title (textstring)
  • Description (textstring)
  • Components (block grid)

We’ve created ‘custom index filters’ to filter on title and description from the content delivery api.

Now our client also wants to filter on text within the components section (for instance the text from a rich text components). I was wondering how we can do that. I’ve created the following custom filter for it:

public class RichTextFilter() : IFilterHandler, IContentIndexHandler
{
    private const string Specifier = "richtext:";
    private const string FieldName = "text";

    public FilterOption BuildFilterOption(string filter)
    {
        var fieldValue = filter[Specifier.Length..];

        // There might be several values for the filter
        var values = fieldValue.Split(',');

        return new FilterOption
        {
            FieldName = FieldName,
            Values = values,
            Operator = FilterOperation.Is
        };
    }

    public bool CanHandle(string query)
    => query.StartsWith(Specifier, StringComparison.OrdinalIgnoreCase);

    public IEnumerable<IndexField> GetFields() =>
    [
        new IndexField
        {
            FieldName = FieldName,
            FieldType = FieldType.StringAnalyzed,
            VariesByCulture = true
        }
    ];

    // Indexing
    public IEnumerable<IndexFieldValue> GetFieldValues(IContent content, string? culture)
    {
        var componentsFieldContent = content.GetValue<IEnumerable<BlockGridItem>>("components");

        var fieldContent = content.GetValue<string>(FieldName);

        if (string.IsNullOrWhiteSpace(fieldContent))
        {
            return [];
        }                

        return
        [
            new IndexFieldValue
            {
                FieldName = FieldName,
                Values = [fieldContent]
            }
        ];
    }
}

I’m specifically trying to get the value from the “components field” within the “GetFieldValues” method, but I’m struggling to find the correct way to bind the value from it. I looked at the Umbraco docs, but they only mention using the value in a razor file, but I’m trying to convert the value in a custom filter action.

Does anyone have any pointers or do I have to create some custom code to get this to work?

Thanks in advance for the help!

I figured it out already. All I had to do was get the IPublishedContent variant of the IContent item. In that conversion, the “components” field is converted from string to a “IEnumerable” which then makes it possible to retrieve all rich text elements and get the value to add the field to the index.

I ended up doing this:

// Indexing
public IEnumerable<IndexFieldValue> GetFieldValues(IContent content, string? culture)
{
    IPublishedContent? contentItem;

    using (var serviceCollection = bgpvc.CreateScope())
    {
        var umbracoHelper = serviceCollection.ServiceProvider.GetRequiredService<UmbracoHelper>();

        contentItem = umbracoHelper.Content(content.Key);
    }

    if (contentItem is null)
    {
        return [];
    }

    var componentsFieldContent = contentItem.Value<IEnumerable<BlockGridItem>>("components");

    if (componentsFieldContent is null || !componentsFieldContent.Any(cf => cf.Content.ContentType.Alias.Equals("richText")))
    {
        return [];
    }

    var indexFieldsValues = new List<string>();

    foreach (var component in componentsFieldContent.Where(cf => cf.Content.ContentType.Alias.Equals("richText")))
    {
        var richTextContent = component.Content.Value<string>(FieldName);
    
        if (string.IsNullOrWhiteSpace(richTextContent))
        {
            return [];
        }

        var sanitizedContent = SanitizeHtmlRegex().Replace(richTextContent, " ");                

        if (string.IsNullOrWhiteSpace(sanitizedContent))
        {
            return [];
        }

        indexFieldsValues.Add(sanitizedContent);
    }

    return 
    [
        new IndexFieldValue
        {
            FieldName = FieldName,
            Values = [string.Join(' ', indexFieldsValues)]
        }
    ];
}

[GeneratedRegex("<.*?>")]
private static partial Regex SanitizeHtmlRegex();
1 Like