Universal Search reference

API and events

Extend Business Central search in AL: the Universal Search public API, codeunit BAA Search API with six methods, and ten integration events for custom indexing.

updated: applies-to: 1.0.0.1

This page applies to version 1.0.0.1; the current product version is 1.0.0.0.

Universal Search exposes one public codeunit and ten integration events, so a companion extension can run searches, open results, register its own tables for indexing and change how indexing and ranking behave, without depending on the app's internals. This reference lists every method and event with its exact signature, as shipped in the current release. Check the installed version on Extension Management, and see the Release notes for what each version contains.

The public API: codeunit BAA Search API

Codeunit 14291918 "BAA Search API" is the only supported entry point for other extensions. Every other codeunit can change between versions. Once on AppSource, these procedures are kept stable: new capability is added as new procedures or events, never by changing an existing signature.

procedure Search(SearchText: Text; MaxResults: Integer; var Results: JsonArray)
procedure Search(SearchText: Text; MaxResults: Integer; FilterTableNo: Integer; var Results: JsonArray; var TableCounts: JsonArray)
procedure OpenResult(TableNo: Integer; SystemIdText: Text)
procedure EnsureTableIndexed(TableNo: Integer; FieldNo: Integer; Weight: Integer; EnablePhonetic: Boolean)
procedure RebuildTable(TableNo: Integer)
procedure ProcessQueueNow()
Method What it does
Search (3 parameters) Searches every indexed table and returns up to MaxResults matches, best first, in the mixed view. Never throws.
Search (5 parameters) Same search. FilterTableNo 0 searches every table with the per-table cap from Group Cap in All View; any other value returns only that table, with no cap. Also fills TableCounts. Never throws.
OpenResult Opens the record's default page. Raises a friendly error if the user cannot read the table, the record no longer exists, or no page is available.
EnsureTableIndexed Creates or updates the Search Index Configuration row for TableNo and FieldNo with the given weight and phonetic setting, sets it to Pending rebuild, and clears the change-tracking table cache in the current session. The rebuild itself runs on the next background run, or when you call RebuildTable.
RebuildTable Rebuilds the whole index for TableNo immediately, in the caller's session. For large tables, prefer EnsureTableIndexed and let the background job do the work.
ProcessQueueNow Applies the queued record changes immediately, in the caller's session. It does not rebuild tables in Pending rebuild; call RebuildTable for those.

MaxResults is clamped to the range 1 to 200 whatever you pass.

Result shape

Each object in Results has these properties:

{
  "tableNo": 18,
  "tableCaption": "Customer",
  "systemId": "{record SystemId}",
  "display": "Omaroon Corporation",
  "fieldCaption": "Name",
  "score": 1234
}

Each object in TableCounts has tableNo, tableCaption, count and bestScore, sorted by bestScore, highest first. Counts are taken before the per-table cap. When FilterTableNo is not 0, TableCounts holds only that table, so a chip-style UI should keep the counts from the last unfiltered call.

Both arrays leave out tables the current user cannot read.

Universal Search results with table captions, matched text and field captions that the Search method also returns
The Search method returns the same data this page shows.

Example: register a table on install

codeunit 50100 "My App Install"
{
    Subtype = Install;

    trigger OnInstallAppPerCompany()
    var
        SearchApi: Codeunit "BAA Search API";
    begin
        // Make the Name field of table 50100 searchable at weight 8, with phonetic matching.
        SearchApi.EnsureTableIndexed(Database::"My Table", 2, 8, true);
    end;
}

The table and object numbers in this example are placeholders for your own extension, which must declare a dependency on Universal Search to call the API or subscribe to its events.

Integration events

All ten events are [IntegrationEvent(false, false)]. Subscribe to them on the codeunit that publishes them.

Codeunit Event Use it to
BAA Search Install (14291916) OnAfterSeedDefaults() Add your own starting Search Index Configuration rows when Universal Search is installed in a company.
BAA Search Change Capture (14291911) OnAfterIsTableConfigured(TableNo: Integer; var IsConfigured: Boolean) Opt a table in or out of change tracking dynamically.
BAA Search Index Builder (14291912) OnBeforeIndexRecord(RecRef: RecordRef; var IsHandled: Boolean) Replace the built-in indexing of one record.
BAA Search Index Builder (14291912) OnAfterIndexRecord(RecRef: RecordRef) React after every configured field of a record is indexed or cleared.
BAA Search Index Builder (14291912) OnAfterBuildIndexEntry(var Entry: Record "BAA Search Index Entry"; RecRef: RecordRef) Adjust the stored original, normalized, compact or phonetic text before the entry is saved.
BAA Search Index Builder (14291912) OnAfterRebuildTable(TableNo: Integer) React after a table is fully and successfully rebuilt.
BAA Search Query Engine (14291913) OnBeforeSearch(SearchText: Text; MaxResults: Integer; var Results: JsonArray; var IsHandled: Boolean) Replace the search entirely by filling Results and setting IsHandled.
BAA Search Query Engine (14291913) OnAfterSearch(SearchText: Text; MaxResults: Integer; var Results: JsonArray) Post-process the final ranked results.
BAA Search Query Engine (14291913) OnBeforeEmitResults(var TempResult: Record "BAA Search Result" temporary) Insert, re-score or remove candidates before sorting and the per-table cap.
BAA Search Query Engine (14291913) OnBeforeOpenResult(TableNo: Integer; SystemId: Guid; var IsHandled: Boolean) Replace opening a result, for example to insert the chosen record into a document instead of opening its card.

How failures in your subscriber are handled

  • Index Builder events. OnBeforeIndexRecord, OnAfterIndexRecord and OnAfterBuildIndexEntry are raised inside a try wrapper. If your subscriber throws, that record's table is set to Error with your message in Last Error, and indexing of other tables continues. A failure in OnAfterRebuildTable is logged to telemetry and never undoes the completed rebuild.
  • OnAfterIsTableConfigured. This event is raised on every table trigger setup and change check, and it is not wrapped. A subscriber that throws breaks the user's insert, modify, delete or rename on any table. Keep it fast and make sure it cannot fail.
  • OnBeforeSearch. When you set IsHandled, Universal Search returns your Results as they are and does not raise OnAfterSearch.
codeunit 50101 "My Search Log Subscriber"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"BAA Search Query Engine", 'OnAfterSearch', '', false, false)]
    local procedure LogSearch(SearchText: Text; MaxResults: Integer; var Results: JsonArray)
    var
        SearchLog: Record "My Search Log";
    begin
        SearchLog.Init();
        SearchLog."Search Text" := CopyStr(SearchText, 1, MaxStrLen(SearchLog."Search Text"));
        SearchLog."Result Count" := Results.Count();
        SearchLog.Insert(true);
    end;
}

My Search Log stands for a table in your own extension.

What's next

See what changed in this version: Release notes. For how the engine scores the results these methods return, see How ranking works. To discuss an integration, contact Bitta Apps.

// next step

Ready to try Universal Search?