Global Search¶
Integration¶
Services integrate with Global Search through the GlobalSearchClientModule.
A service should implement this integration when it owns entities that need to be searchable from the shared Global Search feature.
The integration model is based on document maps:
- the source service chooses which entities participate in Global Search
- a document map defines which fields are projected into the search document
- the document map defines which event triggers reindexing for that entity
- the source service registers that map in its module configuration through the client module API
Source service responsibilities¶
The source service is responsible for:
- deciding which entities should be searchable
- choosing which events should trigger reindexing
- projecting a search-oriented document from its entity
- including any related data needed to build that document
Global Search responsibilities¶
Global Search is responsible for:
- interpreting mapped fields by convention
- creating and evolving the underlying search index schema
- managing internal search fields
- applying search behavior uniformly across entity types
- exposing search and autocomplete capabilities for consumers
Producer services only decide which content belongs in the indexed document.
What the service must provide¶
To integrate an entity with Global Search, the service must provide:
-
The source entity
The domain or read-model entity that will be indexed. -
The triggering event
The event that indicates the searchable representation should be created or updated. -
A document map
A class that defines: - how to extract the event identifier used to locate the entity
- optionally, how to override the document identifier
- which related data must be eagerly loaded before building the document
-
which fields are projected into the indexed document through
Map(...) -
Module registration
The module must register the document map throughGlobalSearchClientModuleOptions.
Design guidance¶
The document map should project a search document, not a raw persistence model.
That means it should contain the fields that are useful for search scenarios, such as:
- identifiers
- names, codes, and descriptions
- lightweight related data needed for matching or display
- computed or flattened values from child collections when searching at the aggregate-root level
Avoid sending data that is:
- large but not useful for discovery
- sensitive and not intended for search
- shaped only for persistence concerns
Mapping model¶
The mapping API is content-only.
A document map should declare which values are part of the search document, for example:
| C# | |
|---|---|
The producer does not choose field roles such as SearchText, Filter, or Sortable.
Those concerns are owned by Global Search conventions.
Well-known fields and conventions¶
Some fields are engine-owned and are added by convention.
id¶
The id field is always included automatically by the base SearchDocumentMap<TEntity, TEvent> implementation. By default, it is derived from entity.CorrelationId, although maps may override GetId(...) when needed.
sharingLevelId¶
If an entity implements IShareableEntity, Global Search adds sharingLevelId automatically through an internal convention.
This means:
- producer maps should not map
sharingLevelIdmanually - the field name is canonical and reserved
- entities that implement
IShareableEntityreceive that field automatically in the indexed document
Integration example¶
The example below shows how a service can make WorkOrder searchable.
1. Source entity and event¶
In this case:
WorkOrderis the entity that should appear in Global SearchWorkOrderUpdatedis the event that triggers reindexing- because
WorkOrderimplementsIShareableEntity,sharingLevelIdis added automatically by convention
2. Document map¶
This map defines the producer-side contract for WorkOrder:
GetEventIdField(...)extracts the entity identifier from the triggering eventUseIncludes()ensures related data is loaded before the document is builtConfigure(...)declares which values are projected into the search documentidis added automatically by the base classsharingLevelIdis added automatically becauseWorkOrderimplementsIShareableEntity
3. Module registration¶
How to read this example¶
Using the previous example, the indexing flow is:
- A
WorkOrderis updated in the owning service. - The service publishes
WorkOrderUpdated. - The Global Search client integration handles that event.
- The configured
WorkOrderDocumentMapis used to: - locate the affected
WorkOrder - load the required related data
- build the indexed document from the configured mapped fields
- The resulting search document is sent to Global Search and upserted into the appropriate index.
This keeps the integration declarative: the service registers the map, and the client module uses that configuration to drive indexing.
Schema and index evolution¶
Global Search uses a stable alias derived from the entity well-known name and a versioned physical index name derived from the effective schema version.
This allows:
- consumers to keep querying the same logical entity target
- Global Search to create a new physical index when the schema changes
- alias cutover to happen independently from the UI or API contract
The effective schema may change when:
- the mapped fields in
Configure(...)change - internal conventions add or remove well-known fields
- Global Search indexing conventions evolve
When schema evolution requires rebuilding an index, Global Search should create the new physical index, reingest data, and switch the alias only after the new index is ready.
Practical notes¶
Keep documents search-oriented¶
Avoid treating the document map as a raw entity dump. A better document usually contains only the fields needed for:
- matching
- ranking
- rendering the search result
Include related data explicitly¶
If the search document depends on navigation properties or related entities, those dependencies should be declared through UseIncludes() so the map can build a complete and consistent document.
Flatten aggregate data when needed¶
When searching aggregate roots, it is often better to index one document per root and flatten child data into that document through computed mappings.
Examples include:
- child descriptions
- child product codes
- line counts
- totals
- booleans derived from child state
Do not map well-known convention fields manually¶
Fields such as id and sharingLevelId are engine-owned. Producer maps should not attempt to define them explicitly.