diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts index 9867920c6e..c1c8ed9c86 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.test.ts @@ -32,6 +32,9 @@ class ElasticSearchSearchEngineForTranslatorTests extends ElasticSearchSearchEng getTranslator() { return this.translator; } + getIndexTemplate() { + return this.indexTemplate; + } } const mock = new Mock(); @@ -50,6 +53,23 @@ jest.mock('./ElasticSearchSearchEngineIndexer', () => ({ .mockImplementation(() => indexerMock), })); +const customIndexTemplate = { + name: 'custom-index-template', + body: { + index_patterns: ['*'], + template: { + settings: { + number_of_shards: 1, + }, + mappings: { + _source: { + enabled: false, + }, + }, + }, + }, +}; + describe('ElasticSearchSearchEngine', () => { let testSearchEngine: ElasticSearchSearchEngine; let inspectableSearchEngine: ElasticSearchSearchEngineForTranslatorTests; @@ -72,6 +92,20 @@ describe('ElasticSearchSearchEngine', () => { client = testSearchEngine['elasticSearchClient']; }); + describe('custom index template', () => { + it('should not have custom template set as default', async () => { + expect(inspectableSearchEngine.getIndexTemplate()).toBeUndefined(); + }); + + it('should set custom index template', async () => { + await inspectableSearchEngine.setIndexTemplate(customIndexTemplate); + + expect(inspectableSearchEngine.getIndexTemplate()).toMatchObject( + customIndexTemplate, + ); + }); + }); + describe('queryTranslator', () => { beforeAll(() => { mock.clearAll(); @@ -760,6 +794,10 @@ describe('ElasticSearchSearchEngine', () => { }); describe('indexer', () => { + beforeEach(async () => { + await testSearchEngine.setIndexTemplate(customIndexTemplate); + }); + it('should get indexer', async () => { const indexer = await testSearchEngine.getIndexer('test-index'); @@ -773,12 +811,38 @@ describe('ElasticSearchSearchEngine', () => { elasticSearchClient: client, }), ); + expect(indexerMock.on).toHaveBeenCalledWith( + 'finish', + expect.any(Function), + ); expect(indexerMock.on).toHaveBeenCalledWith( 'error', expect.any(Function), ); }); + describe('onFinish', () => { + let callback: Function; + + beforeEach(async () => { + mock.clearAll(); + await testSearchEngine.getIndexer('test-index'); + callback = indexerMock.on.mock.calls[1][1]; + }); + + it('should set provided index template on finish', async () => { + const indexTemplateSpy = jest.fn().mockReturnValue(customIndexTemplate); + mock.add( + { method: 'PUT', path: '/_index_template/custom-index-template' }, + indexTemplateSpy, + ); + + await callback(); + + expect(indexTemplateSpy).toHaveBeenCalled(); + }); + }); + describe('onError', () => { let errorHandler: Function; const error = new Error('some error'); diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index 6541d2ed80..025a9fe1fc 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -36,6 +36,24 @@ import { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineInd export type { ElasticSearchClientOptions }; +/** + * Elasticsearch specific index template + * @public + */ +export type ElasticSearchCustomIndexTemplate = { + name: string; + body: ElasticSearchIndexTemplateBody; +}; + +/** + * Elasticsearch specific index template body + * @public + */ +export type ElasticSearchIndexTemplateBody = { + index_patterns: string[]; + template: Record; +}; + /** * Search query that the elasticsearch engine understands. * @public @@ -114,6 +132,7 @@ function isBlank(str: string) { export class ElasticSearchSearchEngine implements SearchEngine { private readonly elasticSearchClient: Client; private readonly highlightOptions: ElasticSearchHighlightConfig; + protected indexTemplate?: ElasticSearchCustomIndexTemplate; constructor( private readonly elasticSearchClientOptions: ElasticSearchClientOptions, @@ -236,8 +255,13 @@ export class ElasticSearchSearchEngine implements SearchEngine { this.translator = translator; } + setIndexTemplate(template: ElasticSearchCustomIndexTemplate) { + this.indexTemplate = template; + } + async getIndexer(type: string) { const alias = this.constructSearchAlias(type); + const indexer = new ElasticSearchSearchEngineIndexer({ type, indexPrefix: this.indexPrefix, @@ -266,6 +290,20 @@ export class ElasticSearchSearchEngine implements SearchEngine { } }); + indexer.on('finish', async () => { + // Set custom index template if set + if (this.indexTemplate) { + try { + await this.elasticSearchClient.indices.putIndexTemplate( + this.indexTemplate, + ); + this.logger.info('Custom index template set'); + } catch (error) { + this.logger.error(`Unable to set custom index template: ${error}`); + } + } + }); + return indexer; }