Umbraco uses the Lucene search index - with a wrapper called Examine. If you want to customize the search index, you can attach an event handler to an event that fires when the index is updated and change/add to the data that is being stored.

Adding the application started event handler

Unfortunately, there’s no Umbraco notification handler for when the index is updated. But there is an event that fires when the application starts, and we can use that to attach our event handler to the index update event.

This is done by calling AddEventHandler to the IServiceCollection object in Startup:

public void ConfigureServices(IServiceCollection services)
{
  services
    .AddUmbraco(_env, _config)
    .AddBackOffice()
    .AddWebsite()
    .AddNotificationHandler<UmbracoApplicationStartingNotification, ExtraIndexDataHandler>()
}

Creating the event handler

The event handler needs to implement INotificationHandler<UmbracoApplicationStartingNotification> and will get the index we want to use (Umbraco has ExternalIndex, InternalIndex and MemberIndex built in) then add the event handler to the TransformingIndexValues event:

public class ExtraIndexDataHandler : INotificationHandler<UmbracoApplicationStartingNotification>
{
    private readonly IExamineManager _examineManager;
    private readonly IExtraIndexDataHandler _handler;

    public ExtraIndexDataHandler(
        IExamineManager examineManager,
        IExtraIndexDataHandler handler)
    {
        _examineManager = examineManager;
        _handler = handler;
    }

    public void Handle(UmbracoApplicationStartingNotification notification)
    {
        if (_examineManager.TryGetIndex("ExternalIndex", out var index))
        {
            ((BaseIndexProvider)index).TransformingIndexValues += _handler.Handle;
        }
    }
}

Creating the index data handler

Now we can do the work of adding the data to the index in our IExtraIndexDataHandler implementation. We need to wrap the code in a try/catch block otherwise an error when indexing one document can affect the rest of the index.

This handler will add the value of the Interesting property to the index for any document type with the alias InterestingDocType:

public class ExtraIndexDataHandler : IExtraIndexDataHandler
{
    private readonly IContentService _contentService;
    private readonly ILogger<ExtraIndexDataHandler> _logger;

    public ExtraIndexDataHandler(
        IContentService contentService,
        ILogger<ExtraIndexDataHandler> logger)
    {
        _contentService = contentService;
        _logger = logger;
    }

    public void Handle(object? sender, IndexingItemEventArgs e)
    {
        try
        {
            HandleUnsafely(e);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error attempting to add extra data to index while processing nodeId {nodeId}", e.ValueSet.Id);
        }
    }

    private void HandleUnsafely(IndexingItemEventArgs e)
    {
        if (!e.ValueSet.ItemType.InvariantEquals("InterestingDocType")
            || !int.TryParse(e.ValueSet.Id, out var nodeId))
        {
            return;
        }

        var node = _contentService.GetById(nodeId);

        if (node == null)
        {
            return;
        }

        var values = e.ValueSet.Values
            .ToDictionary(x => x.Key, x => (IEnumerable<object>)x.Value);

        var interestingPropertyValue = node.GetValue("Interesting")?.GetValue();
        values.Add("Interesting", new [] { interestingPropertyValue });
        e.SetValues(values);
    }
}

Adding implementation to DI

Of course we will also need to register the IExtraIndexDataHandler implementation in DI. This is done in Startup - also in the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
  services.
    AddUmbraco()
  ...
  services.AddTransient<IExtraIndexDataHandler, ExtraIndexDataHandler>();
}

Conclusion

Now we can add extra data to the index for any document type we want, and search for that data or include it in search results.


<
Previous Post
Vite with Umbraco for modern bundling and dev server
>
Next Post
Getting unpublished Umbraco content in Controllers (Umbraco 11+)