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

PropertyReturnsDescription
appIndexersMap<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.
clusterHealthMap<String,Object>The current Elasticsearch cluster health status, as reported by the default Elasticsearch backend.
currentRequestQueriesList<RecentQuery>The up to 50 most recent Elasticsearch queries run while handling the current HTTP request, useful for diagnosing which searches a page triggered.
indexingErrorNotificationEnabledbooleanWhether 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.
recentQueriesList<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.

ParameterDescription
appIndexerthe 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.

ParameterDescription
rootOrgthe tenant whose indexes are updated
branchthe website version being indexed, or null for the account's own indexes
updatedObjectthe object that changed
deletedtrue if the object was deleted and should be removed from the indexes, false if it should be added or updated
propsthe names of the properties that changed, used by indexers that only care about specific fields, or null
bulkActionContainercollects the index writes so they can be sent as one Elasticsearch bulk request, or null to write immediately
doSecondarytrue 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.

ParameterDescription
localIndexNamethe local (tenant-independent) index name, for example "content"
idthe ID of the document to delete
typethe Elasticsearch document type of the item
parentIdthe 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.

ParameterDescription
rootOrgthe tenant whose items are being indexed
branchthe website version being indexed, or null for the account's own indexes
rangethe subset of items to walk, in a form understood by the indexer, or null to index the entire item set
physNamethe physical Elasticsearch index name to write documents into
ithe indexer that supplies the range of items and turns each one into indexable content
incrementaltrue 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.

ParameterDescription
rootOrgthe account to re-index
branchthe website version to index, or null for the account's own indexes
localIndexNamesthe local index names to re-index
incrementaltrue 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.

ParameterDescription
rootOrgthe tenant whose indexers are grouped
branchthe 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.

ParameterDescription
localNamethe local index name that the created physical index will serve
indexersthe candidate indexers to draw mappings from; only those matching localName contribute mappings
branchthe website version being indexed, or null for the account's own indexes
rootOrgthe 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.

ParameterDescription
ithe 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.

ParameterDescription
ithe indexer to get field metadata for
branchthe 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.

ParameterDescription
ithe indexer whose document count is counted
organisationthe tenant whose index is counted
bthe 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.

ParameterDescription
localNamethe local index name to count, for example "content"
organisationthe 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.

ParameterDescription
ithe 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.

ParameterDescription
ithe 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.

ParameterDescription
organisationthe tenant to check

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.

ParameterDescription
jsonthe Elasticsearch query, as a JSON-encoded query body
indexesthe 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.

ParameterDescription
jsonthe Elasticsearch query, as a JSON-encoded query body
pageSizethe number of results to read per page, or null for the default page size
indexesthe 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.

ParameterDescription
jsonthe Elasticsearch query, as a JSON-encoded query body
indexesthe 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.

ParameterDescription
jsonthe Elasticsearch query, as a JSON-encoded query body
pageSizethe maximum number of results to return, or null for the default page size
indexesthe 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.

ParameterDescription
indexthe local index name to delete from, for example "content"
queryJsonthe 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.

ParameterDescription
docIdthe unique ID of the document to look up
aithe 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.

ParameterDescription
indexersthe indexers whose indexes are searched
branchthe website version to search, or null for the account's own indexes
bba 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.

ParameterDescription
namethe local index name to search, for example "content"
organisationthe tenant whose index is searched
qthe search text to match across all fields, or null to match every document
limitthe 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.

ParameterDescription
phrasethe 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.

ParameterDescription
searchIdthe 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.

ParameterDescription
searchPhrasethe search phrase to match, or null or blank for no phrase
pageSizethe number of results per page, or null for the default page size
pageNumthe 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.

ParameterDescription
searchRequestthe request to run, typically built with newOmniSearchRequest and then narrowed down
pageSizethe number of results per page, or null for the default page size
pageNumthe 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.

ParameterDescription
searchRequestthe 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.

ParameterDescription
namethe 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.

ParameterDescription
indexNamethe 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.

ParameterDescription
physicalIndexNamethe physical Elasticsearch index name to invalidate

getClusterHealth()

Returns: Map<String,Object>

The current Elasticsearch cluster health status, as reported by the default Elasticsearch backend.

To get full access to the Kademi Hub existing customers can login here, or new customers can register here.