Adding data to an Umbraco index (Umbraco 11+)
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.