Umbraco.Forms.Search 18.0.0-beta.1

Prefix Reserved
This is a prerelease version of Umbraco.Forms.Search.
dotnet add package Umbraco.Forms.Search --version 18.0.0-beta.1
                    
NuGet\Install-Package Umbraco.Forms.Search -Version 18.0.0-beta.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Umbraco.Forms.Search" Version="18.0.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Umbraco.Forms.Search" Version="18.0.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="Umbraco.Forms.Search" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Umbraco.Forms.Search --version 18.0.0-beta.1
                    
#r "nuget: Umbraco.Forms.Search, 18.0.0-beta.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Umbraco.Forms.Search@18.0.0-beta.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Umbraco.Forms.Search&version=18.0.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Umbraco.Forms.Search&version=18.0.0-beta.1&prerelease
                    
Install as a Cake Tool

Umbraco.Forms.Search

Provides record indexing and querying for Umbraco Forms via the Umbraco.Cms.Search abstractions.

Compatible with Umbraco CMS and Forms 18+.

Installation

Umbraco.Forms.Search builds on Umbraco.Cms.Search.Core and, for the physical index and field registration, the official Umbraco.Cms.Search.Provider.Examine provider. Register the search core, the Examine provider, and then this package:

builder.CreateUmbracoBuilder()
    .AddBackOffice()
    .AddWebsite()
    .AddDeliveryApi()
    .AddSearchCore()              // Umbraco.Cms.Search.Core
    .AddExamineSearchProvider()   // Umbraco.Cms.Search.Provider.Examine
    .AddComposers()
    .AddUmbracoForms()
    .AddUmbracoFormsSearch()      // this package
    .Build();

AddUmbracoFormsSearch() does everything needed to put the Forms records index (UmbracoFormsRecordsIndex) onto Umbraco Search — it registers the index, the physical Examine index, and the field options (see How it works). You do not register the Examine index or FieldOptions yourself.

Disable the built-in Examine record indexing (required)

Umbraco Forms ships its own Examine-backed record indexing, which registers an index under the same name (UmbracoFormsRecordsIndex). Left enabled alongside this package, you get a duplicate-index error at startup (The indexer with name UmbracoFormsRecordsIndex already exists) and records indexed twice. Turn the built-in indexing off via configuration:

{
  "Umbraco": {
    "Forms": {
      "Options": {
        "DisableRecordIndexing": true
      }
    }
  }
}

With this set, Forms skips registering its Examine record index, indexer, and IRecordReaderService, leaving this package as the sole owner of the records index. The database-backed record listing in the backoffice (IFormRecordSearcher) is unaffected — it is registered independently of record indexing.

How it works

The package routes the Forms records index through the Umbraco.Cms.Search abstractions, reusing the existing records index alias (Umbraco.Forms.Core.Constants.ExamineIndex.RecordIndexName, i.e. UmbracoFormsRecordsIndex).

Registration

UmbracoBuilderExtensions.AddUmbracoFormsSearch wires up:

  1. Registers the records index alias with Umbraco.Cms.Search via IndexOptions.RegisterIndex<IIndexer, ISearcher>. This is a custom (self-managed) index, not a content index — so it is intentionally not listed in the Settings → Search dashboard and is not driven by the content-change pipeline.
  2. Registers a physical Examine index for the alias via AddExamineLuceneIndex<LuceneIndex, ConfigurationEnabledDirectoryFactory>. The Examine provider does not create one automatically for a registered alias.
  3. Declares the record FieldOptions (Examine provider) for the non-textual fields used in filtering, sorting and faceting — state, form, memberKey, umbracoPageId, created, updated. Without this, keyword/integer filters and date/state sorting misbehave (unregistered keyword fields are analysed rather than stored raw, and sortable keyword fields are not created).
  4. Overrides IRecordReaderService with the Umbraco.Cms.Search-backed implementation.
  5. Adds IFormRecordIndexingService plus the startup populator and the save/delete notification handlers that keep the index in sync.

Steps 2 and 3 use Examine-specific APIs, so the package depends on Umbraco.Cms.Search.Provider.Examine.

Note on IRecordReaderService: this is a public read API. In Umbraco Forms 18 the backoffice "Entries" list does not use it — that goes through the database-backed IFormRecordSearcher. Overriding IRecordReaderService changes the read path for any code that calls it, but does not by itself change the backoffice entries screen.

Indexing (FormRecordIndexingService)

For every record save, each field is projected into an IndexField on a single document keyed by Record.UniqueId:

Field Value type Source
blob TextsR1 All record field values concatenated, for relevance-ranked full-text search
state Keywords FormState enum name (Submitted, Approved, …)
ip Keywords Record.IP
uniqueId Keywords Record.UniqueId
created DateTimeOffsets Record.Created (coerced to UTC)
updated DateTimeOffsets Record.Updated (coerced to UTC)
form Keywords Record.Form (form id)
memberKey Keywords Record.MemberKey
currentPage Keywords Record.CurrentPage
umbracoPageId Integers Record.UmbracoPageId
recordFields Texts Record.RecordFields serialized as JSON

Documents are single-variation (Variation(null, null)) and carry no protection.

Querying (RecordReaderService)

Implements every IRecordReaderService overload — approved/all records scoped by page, form, or member, including the RecordSortOptions overloads — by composing KeywordFilter / IntegerExactFilter filters and a Sorter (mapping RecordSortField to a DateTimeOffsetSorter for Created/Updated or a KeywordSorter for State; default is Created descending), paging with skip / take.

Because the index stores only record IDs, the service rehydrates full Record instances:

  • Form is known (e.g. GetRecordsFromForm): resolves the Form once and calls IRecordStorage.GetRecords(uniqueIds, form).
  • Form is unknown (e.g. GetRecordsFromPage): a lightweight includeFields: false load discovers which forms the IDs belong to, then issues one full GetRecords call per form so each record is loaded against its own schema.

The loaded records are re-projected onto the order the searcher returned, so the requested sort is preserved.

Startup population (FormRecordIndexPopulationHandler)

On UmbracoApplicationStartedNotification the handler checks IIndexer.GetMetadataAsync. If the index is empty it indexes all existing records; otherwise it logs and returns (so it does not re-index on every boot). The work runs on a background thread so it does not block startup, with per-form error isolation.

Keeping the index in sync (RecordSearchNotificationHandler)

Handles RecordSavingNotification (upsert each saved record) and RecordDeletingNotification (delete by UniqueId). Delete targets the record id directly; no index-wide scan.

Known limitations

  • Examine zero-downtime indexing: the package registers a single physical index under the plain alias name. If you enable Umbraco:CMS:Search:Examine:ZeroDowntimeIndexing, the provider resolves _a/_b shadow index names and the records index will not be found. It is off by default.
  • No management UI: as a custom Umbraco Search index, the records index is not shown in Settings → Search and has no built-in "rebuild" action. The startup populator and the save/delete handlers are the sync mechanism.

Project layout

src/Umbraco.Forms.Search/
├── Extensions/
│   └── UmbracoBuilderExtensions.cs        // AddUmbracoFormsSearch()
├── NotificationHandlers/
│   ├── FormRecordIndexPopulationHandler.cs // Background backfill on startup
│   └── RecordSearchNotificationHandler.cs  // Save/delete sync
└── Services/
    ├── IFormRecordIndexingService.cs       // Public indexing contract
    ├── FormRecordIndexingService.cs        // Document projection → IIndexer
    └── RecordReaderService.cs              // IRecordReaderService over ISearcher
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
18.0.0-beta.1 58 7/16/2026
18.0.0-beta 62 7/16/2026
17.0.0-beta.1 55 7/16/2026
17.0.0-beta 57 7/16/2026
17.0.0--beta.1.preview.17... 56 7/16/2026