Skip to content

Conversation

@callumbwhyte
Copy link
Contributor

@callumbwhyte callumbwhyte commented Nov 22, 2025

As discussed on #20267 v16 introduced a behavioural breaking change whereby Umbraco's own Examine index configuration now happens at an undetermined point in the startup lifecycle, compared with previously running as part of the core Umbraco boot steps.

This can cause issues when trying to customise the Examine index, e.g. setting field types, as you don't know whether your code is going to run before or after Umbraco's.

I explained a bit more about how this came to be here:

This was caused by #18988 - where the registration of Examine (and therefore Umbraco's own configuration of LuceneDirectoryIndexOptions) was moved from happening inside builder.AddBackOffice() to a composer. This meant it went from being (almost) guaranteed to run first, as AddBackOffice is called first from Program.cs, to an unknown order.

Other than the [ComposeBefore] / [ComposeAfter] attributes I can't see a way of forcing Umbraco's Examine composer to run before any user registered ones...

The solution is of course to add decorate your IComposer with [ComposeAfter(typeof(AddExamineComposer))] as per the docs - but this has been problematic for me in my Search Extensions package where I am multi-targeting 🙈 and can't reliably target this change to V16+ as V15 also uses .NET 9 where AddExamineComposer does not exist...

After digging into things I found the real issue is Umbraco assumes it's okay to set the FieldDefinitions property during configuration, without consideration for if this has already been set. The fix is simple: when Umbraco's configuration code runs, ensure any existing config is applied too. I have added an overload to UmbracoFieldDefinitionCollection that takes an existing FieldDefinitionCollection and tries to add each existing definition to the UmbracoFieldDefinitionCollection. Examine sets this property to an empty collection on initialisation (here) anyway so it should always be set.

I believe it would be good to get this fix into both V16 and V17 to prevent confusion as others have been experiencing!

Copilot AI review requested due to automatic review settings November 22, 2025 09:30
@github-actions
Copy link

Hi there @callumbwhyte, thank you for this contribution! 👍

While we wait for one of the Core Collaborators team to have a look at your work, we wanted to let you know about that we have a checklist for some of the things we will consider during review:

  • It's clear what problem this is solving, there's a connected issue or a description of what the changes do and how to test them
  • The automated tests all pass (see "Checks" tab on this PR)
  • The level of security for this contribution is the same or improved
  • The level of performance for this contribution is the same or improved
  • Avoids creating breaking changes; note that behavioral changes might also be perceived as breaking
  • If this is a new feature, Umbraco HQ provided guidance on the implementation beforehand
  • 💡 The contribution looks original and the contributor is presumably allowed to share it

Don't worry if you got something wrong. We like to think of a pull request as the start of a conversation, we're happy to provide guidance on improving your contribution.

If you realize that you might want to make some changes then you can do that by adding new commits to the branch you created for this work and pushing new commits. They should then automatically show up as updates to this pull request.

Thanks, from your friendly Umbraco GitHub bot 🤖 🙂

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds functionality to preserve existing field definitions when configuring Umbraco Examine indexes. Previously, the ConfigureIndexOptions class would always create a new UmbracoFieldDefinitionCollection from scratch, which would discard any custom field definitions that may have been registered by extension code before the configuration runs.

Key Changes:

  • Added a new constructor to UmbracoFieldDefinitionCollection that accepts an existing FieldDefinitionCollection and merges its definitions with the base Umbraco field definitions
  • Updated ConfigureIndexOptions to use the new constructor, passing the existing options.FieldDefinitions to preserve any pre-existing customizations

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Umbraco.Infrastructure/Examine/UmbracoFieldDefinitionCollection.cs Adds a new constructor that accepts and merges existing field definitions
src/Umbraco.Examine.Lucene/DependencyInjection/ConfigureIndexOptions.cs Updates all three index configurations (Internal, External, Members) to preserve existing field definitions using the new constructor

Comment on lines +41 to +43
foreach (var definition in definitions)
{
AddOrUpdate(definition);
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new constructor does not handle the case where definitions parameter is null. If options.FieldDefinitions is null when this constructor is called (from ConfigureIndexOptions.cs), it will throw a NullReferenceException when trying to iterate. Consider adding a null check:

public UmbracoFieldDefinitionCollection(FieldDefinitionCollection? definitions)
    : base(UmbracoIndexFieldDefinitions)
{
    if (definitions != null)
    {
        foreach (var definition in definitions)
        {
            AddOrUpdate(definition);
        }
    }
}
Suggested change
foreach (var definition in definitions)
{
AddOrUpdate(definition);
if (definitions != null)
{
foreach (var definition in definitions)
{
AddOrUpdate(definition);
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant