Owns full-text search and search-index management for a tenant, backed by Elasticsearch. It maintains one physical index per registered AppIndexer (plus one per website branch where relevant), keeps a local cache of which indexes and type mappings exist, and propagates index invalidation across the cluster over a Channel so every server's cache stays consistent. Content updates are applied immediately or deferred until the owning Hibernate transaction commits, and the class also drives full and incremental re-indexing, either synchronously or as background jobs. On top of per-index queries it implements the cross-application "omni search" used by the admin search box, aggregating results and facets from every SearchableApplication. A small set of methods are exported to GraalJS so app developers can run searches and inspect index metadata directly.
Group: Managers
Properties
| Property | Returns | Description |
|---|---|---|
| appIndexers | Map<String,AppIndexer> | All AppIndexer instances registered by active apps for the current tenant, keyed by indexer ID. Each entry represents one searchable content type, such as content items, products, or forum posts. |
| clusterHealth | Map<String,Object> | The current Elasticsearch cluster health status, as reported by the default Elasticsearch backend. |
| currentRequestQueries | List<RecentQuery> | The up to 50 most recent Elasticsearch queries run while handling the current HTTP request, useful for diagnosing which searches a page triggered. |
| indexingErrorNotificationEnabled | boolean | Whether an admin notification should be sent when an item fails to index, for the current tenant. Backed by the "indexingErrorNotificationEnabled" KSearch app setting and cached for up to 10 minutes. |
| recentQueries | List<RecentQuery> | The 50 most recent Elasticsearch queries run for the current tenant on this server, across the whole account. Each server tracks its own recent queries, so this does not cover queries handled by other servers in the cluster. |
Methods
findIndexName(AppIndexer appIndexer) · updateIndexItem(Organisation rootOrg, Branch branch, Object updatedObject, boolean deleted, Set<String> props, BulkActionContainer bulkActionContainer, boolean doSecondary) · deleteItem(String localIndexName, String id, String type, String parentId) · reindexRange(Organisation rootOrg, Branch branch, Serializable range, String physName, RangedReindexableAppIndexer i, boolean incremental) · reindex(Organisation rootOrg, Branch branch, List<String> localIndexNames, boolean incremental) · getAppIndexers() · getMapOfIndexes(Organisation rootOrg, Branch branch) · createIndex(String localName, List<AppIndexer> indexers, Branch branch, Organisation rootOrg) · getFieldMeta(AppIndexer i) · getFieldMeta(AppIndexer i, Branch branch) · newESMappingsBuilder() · numItems(AppIndexer i, Organisation organisation, Branch b) · numItems(String localName, Organisation organisation) · findDiscrepancyContentIds(AppIndexer i) · findDiscrepancies() · findDiscrepancies(AppIndexer i) · findDiscrepancies(Organisation organisation) · search(String json, String indexes) · search(String json, Integer pageSize, String indexes) · search(String json, List<String> indexes) · search(String json, Integer pageSize, List<String> indexes) · deleteByQuery(String index, String queryJson) · getRecentQueries() · getCurrentRequestQueries() · findDoc(String docId, AppIndexer ai) · search(List<AppIndexer> indexers, Branch branch, Consumer<KSearchRequestBuilder> bb) · search(String name, Organisation organisation, String q, int limit) · newOmniSearchRequest(String phrase) · getAppIndexer(String searchId) · isIndexingErrorNotificationEnabled() · omniSearch(String searchPhrase, Integer pageSize, Integer pageNum) · omniSearch(OmniSearchRequest searchRequest, Integer pageSize, Integer pageNum) · omniSearchAggs(OmniSearchRequest searchRequest) · mappingsJson(String name) · invalidateIndex(String indexName) · invalidateCache(String physicalIndexName) · getClusterHealth()
findIndexName(AppIndexer appIndexer)
Returns: String
Returns the local (tenant-independent) index name that the given AppIndexer uses for the current tenant, for example "content" or "products". This is the short name used elsewhere in this class's API, before the tenant ID suffix is appended to form the physical Elasticsearch index name.
| Parameter | Description |
|---|---|
appIndexer | the indexer to look up the local index name for |
updateIndexItem(Organisation rootOrg, Branch branch, Object updatedObject, boolean deleted, Set<String> props, BulkActionContainer bulkActionContainer, boolean doSecondary)
Returns: boolean
Updates or removes the given object in every AppIndexer's index for the tenant, letting each indexer decide whether it needs to update related items too. Callers that batch changes should pass a shared BulkActionContainer so the underlying Elasticsearch writes are sent together; when doSecondary is false, an indexer that would otherwise trigger follow-up (secondary) re-indexing instead just reports that secondary processing is required, so the caller can defer it.
| Parameter | Description |
|---|---|
rootOrg | the tenant whose indexes are updated |
branch | the website version being indexed, or null for the account's own indexes |
updatedObject | the object that changed |
deleted | true if the object was deleted and should be removed from the indexes, false if it should be added or updated |
props | the names of the properties that changed, used by indexers that only care about specific fields, or null |
bulkActionContainer | collects the index writes so they can be sent as one Elasticsearch bulk request, or null to write immediately |
doSecondary | true to let an indexer perform secondary (related-item) re-indexing immediately rather than deferring it |
deleteItem(String localIndexName, String id, String type, String parentId)
Returns: void
Deletes a single document from the current tenant's physical Elasticsearch index for the given local index name.
| Parameter | Description |
|---|---|
localIndexName | the local (tenant-independent) index name, for example "content" |
id | the ID of the document to delete |
type | the Elasticsearch document type of the item |
parentId | the ID of the parent document, for parent/child mapped items, or null |
reindexRange(Organisation rootOrg, Branch branch, Serializable range, String physName, RangedReindexableAppIndexer i, boolean incremental)
Returns: void
Re-indexes a bounded range of items from a single RangedReindexableAppIndexer into an already-created physical index. When incremental is true, an item is skipped if a document for it already exists in the target index, so this can be used to top up an index without re-writing everything that is already there.
| Parameter | Description |
|---|---|
rootOrg | the tenant whose items are being indexed |
branch | the website version being indexed, or null for the account's own indexes |
range | the subset of items to walk, in a form understood by the indexer, or null to index the entire item set |
physName | the physical Elasticsearch index name to write documents into |
i | the indexer that supplies the range of items and turns each one into indexable content |
incremental | true to skip items that already have a document in the target index, false to index every matched item |
reindex(Organisation rootOrg, Branch branch, List<String> localIndexNames, boolean incremental)
Returns: void
Queues a background re-index of the named indexes, one task per index. An incremental run only indexes documents the target cluster does not already hold, which is what closes the gap after an account's search traffic is switched to a different Elasticsearch cluster mid-migration.
| Parameter | Description |
|---|---|
rootOrg | the account to re-index |
branch | the website version to index, or null for the account's own indexes |
localIndexNames | the local index names to re-index |
incremental | true to add only missing documents, false to rebuild each index from scratch |
getAppIndexers()
Returns: Map<String,AppIndexer>
All AppIndexer instances registered by active apps for the current tenant, keyed by indexer ID. Each entry represents one searchable content type, such as content items, products, or forum posts.
getMapOfIndexes(Organisation rootOrg, Branch branch)
Returns: Map<String,List<AppIndexer>>
Groups the tenant's active AppIndexers by the local index name they share, so all the indexers that write to the same physical Elasticsearch index can be processed together, for example when creating or checking that index's mappings.
| Parameter | Description |
|---|---|
rootOrg | the tenant whose indexers are grouped |
branch | the website version to consider, or null for the account's own indexers |
createIndex(String localName, List<AppIndexer> indexers, Branch branch, Organisation rootOrg)
Returns: String
Creates a new physical Elasticsearch index with a unique, timestamped name and sets up the type mappings for every indexer that shares the given local index name. Used when (re)building an index from scratch, before the alias is reassigned to point at the new physical index.
| Parameter | Description |
|---|---|
localName | the local index name that the created physical index will serve |
indexers | the candidate indexers to draw mappings from; only those matching localName contribute mappings |
branch | the website version being indexed, or null for the account's own indexes |
rootOrg | the tenant the index is created for |
getFieldMeta(AppIndexer i)
Returns: Map<String,ESFieldMeta>
Field metadata for the given indexer's mappings, for the account's own indexes rather than a specific website branch.
| Parameter | Description |
|---|---|
i | the indexer to get field metadata for |
getFieldMeta(AppIndexer i, Branch branch)
Returns: Map<String,ESFieldMeta>
Field metadata for the given indexer's mappings, describing each mapped field's type and options. Used to drive things like search filter UIs and custom field configuration screens without needing to inspect raw Elasticsearch mappings.
| Parameter | Description |
|---|---|
i | the indexer to get field metadata for |
branch | the website version to get mappings for, or null for the account's own indexes |
newESMappingsBuilder()
Returns: ESMappingsBuilder
Creates a new, empty ESMappingsBuilder that a caller can use to build a custom set of Elasticsearch field mappings, for example when defining a custom search index outside the fields an AppIndexer already provides.
numItems(AppIndexer i, Organisation organisation, Branch b)
Returns: long
The number of documents of the given indexer's type held in its Elasticsearch index for the given tenant and branch.
| Parameter | Description |
|---|---|
i | the indexer whose document count is counted |
organisation | the tenant whose index is counted |
b | the website version to count, or null for the account's own index |
numItems(String localName, Organisation organisation)
Returns: Long
The total number of documents of every type held in the physical Elasticsearch index for the given local index name.
| Parameter | Description |
|---|---|
localName | the local index name to count, for example "content" |
organisation | the tenant whose index is counted |
findDiscrepancyContentIds(AppIndexer i)
Returns: Map<String,Object>
Compares the primary database and the search index for a StatisticsAppIndexer's content type in the current tenant, and returns every content ID that is missing from one side but present in the other.
| Parameter | Description |
|---|---|
i | the indexer to check, which must implement StatisticsAppIndexer and be active for the current tenant |
findDiscrepancies()
Returns: Map<String,EsStatistics>
Checks every StatisticsAppIndexer active for the current tenant for a difference between its primary database count and its Elasticsearch document count.
findDiscrepancies(AppIndexer i)
Returns: EsStatistics
Compares the primary database count and the Elasticsearch document count for a single StatisticsAppIndexer's content type in the current tenant.
| Parameter | Description |
|---|---|
i | the indexer to check, which must implement StatisticsAppIndexer and be active for the current tenant |
findDiscrepancies(Organisation organisation)
Returns: Map<String,EsStatistics>
Checks every StatisticsAppIndexer active for the given tenant for a difference between its primary database count and its Elasticsearch document count.
| Parameter | Description |
|---|---|
organisation | the tenant to check |
search(String json, String indexes)
Returns: KSearchResponse
Runs a raw Elasticsearch query, given as a JSON query body, against the current tenant's physical indexes for the given local index names, with no page size. Use this for aggregation queries, which read their result from getAggregations. KSearchResponse.useHits throws on a response from this overload - to iterate hits, use the overload that takes a page size.
| Parameter | Description |
|---|---|
json | the Elasticsearch query, as a JSON-encoded query body |
indexes | the local index names to search, for example "content" |
search(String json, Integer pageSize, String indexes)
Returns: KSearchResponse
Runs a raw Elasticsearch query, given as a JSON query body, against the current tenant's physical indexes for the given local index names, reading results a page at a time. This is the overload to use with KSearchResponse.useHits, which requires a page size and throws without one.
| Parameter | Description |
|---|---|
json | the Elasticsearch query, as a JSON-encoded query body |
pageSize | the number of results to read per page, or null for the default page size |
indexes | the local index names to search, for example "content" |
search(String json, List<String> indexes)
Returns: KSearchResponse
Runs a raw Elasticsearch query, given as a JSON query body, against the current tenant's physical indexes for the given local index names.
| Parameter | Description |
|---|---|
json | the Elasticsearch query, as a JSON-encoded query body |
indexes | the local index names to search, for example "content" |
search(String json, Integer pageSize, List<String> indexes)
Returns: KSearchResponse
Runs a raw Elasticsearch query, given as a JSON query body, against the current tenant's physical indexes for the given local index names, capping the number of results returned. This is the underlying implementation the other search(json, ...) overloads delegate to.
| Parameter | Description |
|---|---|
json | the Elasticsearch query, as a JSON-encoded query body |
pageSize | the maximum number of results to return, or null for the default page size |
indexes | the local index names to search, for example "content" |
deleteByQuery(String index, String queryJson)
Returns: void
Deletes every document in the current tenant's physical index for the given local index name that matches the given Elasticsearch query.
| Parameter | Description |
|---|---|
index | the local index name to delete from, for example "content" |
queryJson | the Elasticsearch query, as a JSON-encoded query body, selecting the documents to delete |
getRecentQueries()
Returns: List<RecentQuery>
The 50 most recent Elasticsearch queries run for the current tenant on this server, across the whole account. Each server tracks its own recent queries, so this does not cover queries handled by other servers in the cluster.
getCurrentRequestQueries()
Returns: List<RecentQuery>
The up to 50 most recent Elasticsearch queries run while handling the current HTTP request, useful for diagnosing which searches a page triggered.
findDoc(String docId, AppIndexer ai)
Returns: JSONObject
Looks up a single document by ID from the given indexer's Elasticsearch index for the current tenant.
| Parameter | Description |
|---|---|
docId | the unique ID of the document to look up |
ai | the indexer whose index is queried |
search(List<AppIndexer> indexers, Branch branch, Consumer<KSearchRequestBuilder> bb)
Returns: KSearchResponse
Runs a query across the physical indexes for the given indexers, skipping any indexer whose index does not yet exist. The query itself is configured by the supplied callback against a KSearchRequestBuilder.
| Parameter | Description |
|---|---|
indexers | the indexers whose indexes are searched |
branch | the website version to search, or null for the account's own indexes |
bb | a callback that configures the query, size, aggregations and other search options on the request builder |
search(String name, Organisation organisation, String q, int limit)
Returns: KSearchResponse
Runs a simple keyword search against a single local index for the given tenant, matching the query text across every field, or returning every document if no query text is given.
| Parameter | Description |
|---|---|
name | the local index name to search, for example "content" |
organisation | the tenant whose index is searched |
q | the search text to match across all fields, or null to match every document |
limit | the maximum number of results to return |
newOmniSearchRequest(String phrase)
Returns: OmniSearchRequest
Creates a new OmniSearchRequest for the current tenant, ready to be configured further and passed to omniSearch. The phrase is trimmed and lower-cased, and treated as no search phrase at all if it is blank.
| Parameter | Description |
|---|---|
phrase | the search phrase to match, or null or blank for no phrase |
getAppIndexer(String searchId)
Returns: AppIndexer
Looks up a single registered AppIndexer for the current tenant by its ID.
| Parameter | Description |
|---|---|
searchId | the indexer's ID, as used as a key in getAppIndexers |
isIndexingErrorNotificationEnabled()
Returns: boolean
Whether an admin notification should be sent when an item fails to index, for the current tenant. Backed by the "indexingErrorNotificationEnabled" KSearch app setting and cached for up to 10 minutes.
omniSearch(String searchPhrase, Integer pageSize, Integer pageNum)
Returns: OmniResult
Runs the cross-application omni search for the current tenant using a plain search phrase, a shorthand for building an OmniSearchRequest and calling the other omniSearch overload.
| Parameter | Description |
|---|---|
searchPhrase | the search phrase to match, or null or blank for no phrase |
pageSize | the number of results per page, or null for the default page size |
pageNum | the page number to return, or null for the first page |
omniSearch(OmniSearchRequest searchRequest, Integer pageSize, Integer pageNum)
Returns: OmniResult
Runs a configured OmniSearchRequest across every active application's contributed index, paging the combined results.
| Parameter | Description |
|---|---|
searchRequest | the request to run, typically built with newOmniSearchRequest and then narrowed down |
pageSize | the number of results per page, or null for the default page size |
pageNum | the page number to return, or null for the first page |
omniSearchAggs(OmniSearchRequest searchRequest)
Returns: KSearchResponse
Runs a configured OmniSearchRequest's query with zero results and item type, category and tags aggregations, so a caller can build filter facets for the omni search UI without fetching any documents.
| Parameter | Description |
|---|---|
searchRequest | the request to aggregate, typically built with newOmniSearchRequest and then narrowed down |
mappingsJson(String name)
Returns: Map<String,Map<String,Object>>
The Elasticsearch type mappings for the given local index name, for the current tenant, as parsed JSON structures keyed by mapping type.
| Parameter | Description |
|---|---|
name | the local index name to look up mappings for, for example "content" |
invalidateIndex(String indexName)
Returns: void
Invalidates the cached existence and mapping state for the current tenant's physical index behind the given local index name, on this server, and notifies every other server in the cluster to do the same.
| Parameter | Description |
|---|---|
indexName | the local index name to invalidate, for example "content" |
invalidateCache(String physicalIndexName)
Returns: void
Invalidates the cached existence and mapping state for the given physical Elasticsearch index name, on this server, and notifies every other server in the cluster to do the same.
| Parameter | Description |
|---|---|
physicalIndexName | the physical Elasticsearch index name to invalidate |
getClusterHealth()
Returns: Map<String,Object>
The current Elasticsearch cluster health status, as reported by the default Elasticsearch backend.