From 322a9925c2dfc4a76a133077a90ca3c2ce9f66ad Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Fri, 6 Mar 2020 16:56:03 -0700 Subject: [PATCH] [Search service] Add enhanced ES search strategy (#59224) (#59563) * Add async search strategy * Add async search * Fix async strategy and add tests * Move types to separate file * Revert changes to demo search * Update demo search strategy to use async * Add async es search strategy * Return response as rawResponse * Poll after initial request * Add cancellation to search strategies * Add tests * Simplify async search strategy * Move loadingCount to search strategy * Update abort controller library * Bootstrap * Abort when the request is aborted * Add utility and update value suggestions route * Fix bad merge conflict * Update tests * Move to data_enhanced plugin * Remove bad merge * Revert switching abort controller libraries * Revert package.json in lib * Move to previous abort controller * Add support for frozen indices * Fix test to use fake timers to run debounced handlers * Revert changes to example plugin * Fix loading bar not going away when cancelling * Call getSearchStrategy instead of passing directly * Add async demo search strategy * Fix error with setting state * Update how aborting works * Fix type checks * Add test for loading count * Attempt to fix broken example test * Revert changes to test * Fix test * Update name to camelCase * Fix failing test * Don't require data_enhanced in example plugin * Actually send DELETE request * Use waitForCompletion parameter * Use default search params * Add support for rollups * Only make changes needed for frozen indices/rollups * Fix tests/types * Don't include skipped in loaded/total Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- src/plugins/data/common/index.ts | 1 + .../search/es_search/es_search_strategy.ts | 9 ++- .../es_search/get_es_preference.test.ts | 39 +++++++----- .../search/es_search/get_es_preference.ts | 14 ++--- .../data/public/search/es_search/index.ts | 21 +++++++ src/plugins/data/public/search/index.ts | 1 + src/plugins/data/server/index.ts | 12 +++- .../data/server/search/create_api.test.ts | 13 +++- src/plugins/data/server/search/create_api.ts | 4 ++ .../es_search/es_search_strategy.test.ts | 18 ------ .../search/es_search/es_search_strategy.ts | 24 +++----- .../es_search/get_default_search_params.ts | 28 +++++++++ .../data/server/search/es_search/index.ts | 4 +- src/plugins/data/server/search/index.ts | 4 +- x-pack/plugins/data_enhanced/common/index.ts | 7 +++ .../data_enhanced/common/search/index.ts | 7 +++ .../data_enhanced/common/search/types.ts | 19 ++++++ x-pack/plugins/data_enhanced/kibana.json | 2 +- x-pack/plugins/data_enhanced/public/plugin.ts | 16 ++++- .../public/search/es_search_strategy.ts | 41 +++++++++++++ .../data_enhanced/public/search/index.ts | 1 + x-pack/plugins/data_enhanced/server/index.ts | 14 +++++ x-pack/plugins/data_enhanced/server/plugin.ts | 37 ++++++++++++ .../server/search/es_search_strategy.ts | 59 +++++++++++++++++++ .../data_enhanced/server/search/index.ts | 7 +++ 25 files changed, 331 insertions(+), 71 deletions(-) create mode 100644 src/plugins/data/public/search/es_search/index.ts create mode 100644 src/plugins/data/server/search/es_search/get_default_search_params.ts create mode 100644 x-pack/plugins/data_enhanced/common/index.ts create mode 100644 x-pack/plugins/data_enhanced/common/search/index.ts create mode 100644 x-pack/plugins/data_enhanced/common/search/types.ts create mode 100644 x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts create mode 100644 x-pack/plugins/data_enhanced/server/index.ts create mode 100644 x-pack/plugins/data_enhanced/server/plugin.ts create mode 100644 x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts create mode 100644 x-pack/plugins/data_enhanced/server/search/index.ts diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts index e02045de24e8..7fa6e88b427a 100644 --- a/src/plugins/data/common/index.ts +++ b/src/plugins/data/common/index.ts @@ -24,4 +24,5 @@ export * from './index_patterns'; export * from './es_query'; export * from './utils'; export * from './types'; +export * from './search'; export * from './constants'; diff --git a/src/plugins/data/public/search/es_search/es_search_strategy.ts b/src/plugins/data/public/search/es_search/es_search_strategy.ts index 5382a59123e7..a61428c99815 100644 --- a/src/plugins/data/public/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/public/search/es_search/es_search_strategy.ts @@ -30,11 +30,10 @@ export const esSearchStrategyProvider: TSearchStrategyProvider { - if (typeof request.params.preference === 'undefined') { - const setPreference = context.core.uiSettings.get('courier:setRequestPreference'); - const customPreference = context.core.uiSettings.get('courier:customRequestPreference'); - request.params.preference = getEsPreference(setPreference, customPreference); - } + request.params = { + preference: getEsPreference(context.core.uiSettings), + ...request.params, + }; return search({ ...request, serverStrategy: ES_SEARCH_STRATEGY }, options) as Observable< IEsSearchResponse >; diff --git a/src/plugins/data/public/search/es_search/get_es_preference.test.ts b/src/plugins/data/public/search/es_search/get_es_preference.test.ts index 27e6f9b48bbd..8b8156b4519d 100644 --- a/src/plugins/data/public/search/es_search/get_es_preference.test.ts +++ b/src/plugins/data/public/search/es_search/get_es_preference.test.ts @@ -18,29 +18,40 @@ */ import { getEsPreference } from './get_es_preference'; - -jest.useFakeTimers(); +import { CoreStart } from '../../../../../core/public'; +import { coreMock } from '../../../../../core/public/mocks'; describe('Get ES preference', () => { + let mockCoreStart: MockedKeys; + + beforeEach(() => { + mockCoreStart = coreMock.createStart(); + }); + test('returns the session ID if set to sessionId', () => { - const setPreference = 'sessionId'; - const customPreference = 'foobar'; - const sessionId = 'my_session_id'; - const preference = getEsPreference(setPreference, customPreference, sessionId); - expect(preference).toBe(sessionId); + mockCoreStart.uiSettings.get.mockImplementation((key: string) => { + if (key === 'courier:setRequestPreference') return 'sessionId'; + if (key === 'courier:customRequestPreference') return 'foobar'; + }); + const preference = getEsPreference(mockCoreStart.uiSettings, 'my_session_id'); + expect(preference).toBe('my_session_id'); }); test('returns the custom preference if set to custom', () => { - const setPreference = 'custom'; - const customPreference = 'foobar'; - const preference = getEsPreference(setPreference, customPreference); - expect(preference).toBe(customPreference); + mockCoreStart.uiSettings.get.mockImplementation((key: string) => { + if (key === 'courier:setRequestPreference') return 'custom'; + if (key === 'courier:customRequestPreference') return 'foobar'; + }); + const preference = getEsPreference(mockCoreStart.uiSettings); + expect(preference).toBe('foobar'); }); test('returns undefined if set to none', () => { - const setPreference = 'none'; - const customPreference = 'foobar'; - const preference = getEsPreference(setPreference, customPreference); + mockCoreStart.uiSettings.get.mockImplementation((key: string) => { + if (key === 'courier:setRequestPreference') return 'none'; + if (key === 'courier:customRequestPreference') return 'foobar'; + }); + const preference = getEsPreference(mockCoreStart.uiSettings); expect(preference).toBe(undefined); }); }); diff --git a/src/plugins/data/public/search/es_search/get_es_preference.ts b/src/plugins/data/public/search/es_search/get_es_preference.ts index 200e5bacb7f1..3f1c2b9b3b73 100644 --- a/src/plugins/data/public/search/es_search/get_es_preference.ts +++ b/src/plugins/data/public/search/es_search/get_es_preference.ts @@ -17,13 +17,13 @@ * under the License. */ +import { IUiSettingsClient } from '../../../../../core/public'; + const defaultSessionId = `${Date.now()}`; -export function getEsPreference( - setRequestPreference: string, - customRequestPreference?: string, - sessionId: string = defaultSessionId -) { - if (setRequestPreference === 'sessionId') return `${sessionId}`; - return setRequestPreference === 'custom' ? customRequestPreference : undefined; +export function getEsPreference(uiSettings: IUiSettingsClient, sessionId = defaultSessionId) { + const setPreference = uiSettings.get('courier:setRequestPreference'); + if (setPreference === 'sessionId') return `${sessionId}`; + const customPreference = uiSettings.get('courier:customRequestPreference'); + return setPreference === 'custom' ? customPreference : undefined; } diff --git a/src/plugins/data/public/search/es_search/index.ts b/src/plugins/data/public/search/es_search/index.ts new file mode 100644 index 000000000000..41c6ec388bfa --- /dev/null +++ b/src/plugins/data/public/search/es_search/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { esSearchStrategyProvider } from './es_search_strategy'; +export { getEsPreference } from './get_es_preference'; diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 853dbd09e1f9..2a54cfe2be78 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -36,6 +36,7 @@ export { export { IEsSearchResponse, IEsSearchRequest, ES_SEARCH_STRATEGY } from '../../common/search'; export { ISyncSearchRequest, SYNC_SEARCH_STRATEGY } from './sync_search_strategy'; +export { esSearchStrategyProvider, getEsPreference } from './es_search'; export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search'; diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index b7ec02871306..18ba1130cc26 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -151,8 +151,16 @@ export { * Search */ -export { IRequestTypesMap, IResponseTypesMap } from './search'; -export * from './search'; +export { + ISearch, + ICancel, + ISearchOptions, + IRequestTypesMap, + IResponseTypesMap, + ISearchContext, + TSearchStrategyProvider, + getDefaultSearchParams, +} from './search'; /** * Types to be shared externally diff --git a/src/plugins/data/server/search/create_api.test.ts b/src/plugins/data/server/search/create_api.test.ts index 99e48056ef85..0cf68b7e020c 100644 --- a/src/plugins/data/server/search/create_api.test.ts +++ b/src/plugins/data/server/search/create_api.test.ts @@ -23,8 +23,6 @@ import { TSearchStrategiesMap } from './i_search_strategy'; import { IRouteHandlerSearchContext } from './i_route_handler_search_context'; import { DEFAULT_SEARCH_STRATEGY } from '../../common/search'; -// let mockCoreSetup: MockedKeys; - const mockDefaultSearch = jest.fn(() => Promise.resolve({ total: 100, loaded: 0 })); const mockDefaultSearchStrategyProvider = jest.fn(() => Promise.resolve({ @@ -59,4 +57,15 @@ describe('createApi', () => { `"No strategy found for noneByThisName"` ); }); + + it('logs the response if `debug` is set to `true`', async () => { + const spy = jest.spyOn(console, 'log'); + await api.search({ params: {} }); + + expect(spy).not.toBeCalled(); + + await api.search({ debug: true, params: {} }); + + expect(spy).toBeCalled(); + }); }); diff --git a/src/plugins/data/server/search/create_api.ts b/src/plugins/data/server/search/create_api.ts index 798a4b82caae..00665b21f2ba 100644 --- a/src/plugins/data/server/search/create_api.ts +++ b/src/plugins/data/server/search/create_api.ts @@ -31,6 +31,10 @@ export function createApi({ }) { const api: IRouteHandlerSearchContext = { search: async (request, options, strategyName) => { + if (request.debug) { + // eslint-disable-next-line + console.log(JSON.stringify(request, null, 2)); + } const name = strategyName ?? DEFAULT_SEARCH_STRATEGY; const strategyProvider = searchStrategies[name]; if (!strategyProvider) { diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts index 99ccb4dcbeba..c4b8119f9e09 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -51,24 +51,6 @@ describe('ES search strategy', () => { expect(typeof esSearch.search).toBe('function'); }); - it('logs the response if `debug` is set to `true`', async () => { - const spy = jest.spyOn(console, 'log'); - const esSearch = esSearchStrategyProvider( - { - core: mockCoreSetup, - config$: mockConfig$, - }, - mockApiCaller, - mockSearch - ); - - expect(spy).not.toBeCalled(); - - await esSearch.search({ params: {}, debug: true }); - - expect(spy).toBeCalled(); - }); - it('calls the API caller with the params with defaults', async () => { const params = { index: 'logstash-*' }; const esSearch = esSearchStrategyProvider( diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 20bc964effc0..26055a3ae41f 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -21,7 +21,7 @@ import { APICaller } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; import { ES_SEARCH_STRATEGY } from '../../../common/search'; import { ISearchStrategy, TSearchStrategyProvider } from '../i_search_strategy'; -import { ISearchContext } from '..'; +import { getDefaultSearchParams, ISearchContext } from '..'; export const esSearchStrategyProvider: TSearchStrategyProvider = ( context: ISearchContext, @@ -30,28 +30,18 @@ export const esSearchStrategyProvider: TSearchStrategyProvider { const config = await context.config$.pipe(first()).toPromise(); + const defaultParams = getDefaultSearchParams(config); const params = { - timeout: `${config.elasticsearch.shardTimeout.asMilliseconds()}ms`, - ignoreUnavailable: true, // Don't fail if the index/indices don't exist - restTotalHitsAsInt: true, // Get the number of hits as an int rather than a range + ...defaultParams, ...request.params, }; - if (request.debug) { - // eslint-disable-next-line - console.log(JSON.stringify(params, null, 2)); - } - const esSearchResponse = (await caller('search', params, options)) as SearchResponse; + const rawResponse = (await caller('search', params, options)) as SearchResponse; // The above query will either complete or timeout and throw an error. // There is no progress indication on this api. - return { - total: esSearchResponse._shards.total, - loaded: - esSearchResponse._shards.failed + - esSearchResponse._shards.skipped + - esSearchResponse._shards.successful, - rawResponse: esSearchResponse, - }; + const { total, failed, successful } = rawResponse._shards; + const loaded = failed + successful; + return { total, loaded, rawResponse }; }, }; }; diff --git a/src/plugins/data/server/search/es_search/get_default_search_params.ts b/src/plugins/data/server/search/es_search/get_default_search_params.ts new file mode 100644 index 000000000000..b2341ccc0f3c --- /dev/null +++ b/src/plugins/data/server/search/es_search/get_default_search_params.ts @@ -0,0 +1,28 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SharedGlobalConfig } from '../../../../../core/server'; + +export function getDefaultSearchParams(config: SharedGlobalConfig) { + return { + timeout: `${config.elasticsearch.shardTimeout.asMilliseconds()}ms`, + ignoreUnavailable: true, // Don't fail if the index/indices don't exist + restTotalHitsAsInt: true, // Get the number of hits as an int rather than a range + }; +} diff --git a/src/plugins/data/server/search/es_search/index.ts b/src/plugins/data/server/search/es_search/index.ts index e5dcb0c97d7c..5a8b3bc94c67 100644 --- a/src/plugins/data/server/search/es_search/index.ts +++ b/src/plugins/data/server/search/es_search/index.ts @@ -17,6 +17,6 @@ * under the License. */ -export { esSearchStrategyProvider } from './es_search_strategy'; - export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../../common/search'; +export { esSearchStrategyProvider } from './es_search_strategy'; +export { getDefaultSearchParams } from './get_default_search_params'; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index 298a665fd5b2..385e96ee803b 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -21,8 +21,10 @@ export { ISearchSetup } from './i_search_setup'; export { ISearchContext } from './i_search_context'; -export { IRequestTypesMap, IResponseTypesMap } from './i_search'; +export { ISearch, ICancel, ISearchOptions, IRequestTypesMap, IResponseTypesMap } from './i_search'; export { TStrategyTypes } from './strategy_types'; export { TSearchStrategyProvider } from './i_search_strategy'; + +export { getDefaultSearchParams } from './es_search'; diff --git a/x-pack/plugins/data_enhanced/common/index.ts b/x-pack/plugins/data_enhanced/common/index.ts new file mode 100644 index 000000000000..0d5e353b0e83 --- /dev/null +++ b/x-pack/plugins/data_enhanced/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { EnhancedSearchParams, IEnhancedEsSearchRequest } from './search'; diff --git a/x-pack/plugins/data_enhanced/common/search/index.ts b/x-pack/plugins/data_enhanced/common/search/index.ts new file mode 100644 index 000000000000..3fe4fd029b94 --- /dev/null +++ b/x-pack/plugins/data_enhanced/common/search/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { EnhancedSearchParams, IEnhancedEsSearchRequest } from './types'; diff --git a/x-pack/plugins/data_enhanced/common/search/types.ts b/x-pack/plugins/data_enhanced/common/search/types.ts new file mode 100644 index 000000000000..59ce9f0b36f2 --- /dev/null +++ b/x-pack/plugins/data_enhanced/common/search/types.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchParams } from 'elasticsearch'; +import { IEsSearchRequest } from '../../../../../src/plugins/data/common'; + +export interface EnhancedSearchParams extends SearchParams { + ignoreThrottled: boolean; +} + +export interface IEnhancedEsSearchRequest extends IEsSearchRequest { + /** + * Used to determine whether to use the _rollups_search or a regular search endpoint. + */ + isRollup?: boolean; +} diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index ebda41f46ebf..b2d5f42d9e46 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -8,6 +8,6 @@ "requiredPlugins": [ "data" ], - "server": false, + "server": true, "ui": true } diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index 4fe27d400f45..6316d87c5051 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -5,10 +5,18 @@ */ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart, + ES_SEARCH_STRATEGY, +} from '../../../../src/plugins/data/public'; import { setAutocompleteService } from './services'; import { setupKqlQuerySuggestionProvider, KUERY_LANGUAGE_NAME } from './autocomplete'; -import { ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider } from './search'; +import { + ASYNC_SEARCH_STRATEGY, + asyncSearchStrategyProvider, + enhancedEsSearchStrategyProvider, +} from './search'; export interface DataEnhancedSetupDependencies { data: DataPublicPluginSetup; @@ -29,6 +37,10 @@ export class DataEnhancedPlugin implements Plugin { setupKqlQuerySuggestionProvider(core) ); data.search.registerSearchStrategyProvider(ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider); + data.search.registerSearchStrategyProvider( + ES_SEARCH_STRATEGY, + enhancedEsSearchStrategyProvider + ); } public start(core: CoreStart, plugins: DataEnhancedStartDependencies) { diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts new file mode 100644 index 000000000000..25c6a789cca9 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Observable } from 'rxjs'; +import { ES_SEARCH_STRATEGY, IEsSearchResponse } from '../../../../../src/plugins/data/common'; +import { + TSearchStrategyProvider, + ISearchContext, + ISearch, + SYNC_SEARCH_STRATEGY, + getEsPreference, +} from '../../../../../src/plugins/data/public'; +import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common'; + +export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext +) => { + const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY); + const { search: syncSearch } = syncStrategyProvider(context); + + const search: ISearch = ( + request: IEnhancedEsSearchRequest, + options + ) => { + const params: EnhancedSearchParams = { + ignoreThrottled: !context.core.uiSettings.get('search:includeFrozen'), + preference: getEsPreference(context.core.uiSettings), + ...request.params, + }; + request.params = params; + + return syncSearch({ ...request, serverStrategy: ES_SEARCH_STRATEGY }, options) as Observable< + IEsSearchResponse + >; + }; + + return { search }; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/index.ts b/x-pack/plugins/data_enhanced/public/search/index.ts index a7729aeea564..e39c1b6a1dd6 100644 --- a/x-pack/plugins/data_enhanced/public/search/index.ts +++ b/x-pack/plugins/data_enhanced/public/search/index.ts @@ -5,4 +5,5 @@ */ export { ASYNC_SEARCH_STRATEGY, asyncSearchStrategyProvider } from './async_search_strategy'; +export { enhancedEsSearchStrategyProvider } from './es_search_strategy'; export { IAsyncSearchRequest, IAsyncSearchOptions } from './types'; diff --git a/x-pack/plugins/data_enhanced/server/index.ts b/x-pack/plugins/data_enhanced/server/index.ts new file mode 100644 index 000000000000..fbe1ecc10d63 --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from 'kibana/server'; +import { EnhancedDataServerPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new EnhancedDataServerPlugin(initializerContext); +} + +export { EnhancedDataServerPlugin as Plugin }; diff --git a/x-pack/plugins/data_enhanced/server/plugin.ts b/x-pack/plugins/data_enhanced/server/plugin.ts new file mode 100644 index 000000000000..a27a73431574 --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/plugin.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + PluginInitializerContext, + CoreSetup, + CoreStart, + Plugin, +} from '../../../../src/core/server'; +import { ES_SEARCH_STRATEGY } from '../../../../src/plugins/data/common'; +import { PluginSetup as DataPluginSetup } from '../../../../src/plugins/data/server'; +import { enhancedEsSearchStrategyProvider } from './search'; + +interface SetupDependencies { + data: DataPluginSetup; +} + +export class EnhancedDataServerPlugin implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, deps: SetupDependencies) { + deps.data.search.registerSearchStrategyProvider( + this.initializerContext.opaqueId, + ES_SEARCH_STRATEGY, + enhancedEsSearchStrategyProvider + ); + } + + public start(core: CoreStart) {} + + public stop() {} +} + +export { EnhancedDataServerPlugin as Plugin }; diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts new file mode 100644 index 000000000000..6e12ffb6404c --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { first } from 'rxjs/operators'; +import { mapKeys, snakeCase } from 'lodash'; +import { SearchResponse } from 'elasticsearch'; +import { APICaller } from '../../../../../src/core/server'; +import { ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common'; +import { + ISearchContext, + TSearchStrategyProvider, + ISearch, + ISearchOptions, + getDefaultSearchParams, +} from '../../../../../src/plugins/data/server'; +import { IEnhancedEsSearchRequest } from '../../common'; + +export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = ( + context: ISearchContext, + caller: APICaller +) => { + const search: ISearch = async ( + request: IEnhancedEsSearchRequest, + options + ) => { + const config = await context.config$.pipe(first()).toPromise(); + const defaultParams = getDefaultSearchParams(config); + const params = { ...defaultParams, ...request.params }; + + const rawResponse = (await (request.isRollup + ? rollupSearch(caller, { ...request, params }, options) + : caller('search', params, options))) as SearchResponse; + + const { total, failed, successful } = rawResponse._shards; + const loaded = failed + successful; + return { total, loaded, rawResponse }; + }; + + return { search }; +}; + +function rollupSearch( + caller: APICaller, + request: IEnhancedEsSearchRequest, + options?: ISearchOptions +) { + const method = 'POST'; + const path = `${request.params.index}/_rollup_search`; + const { body, ...params } = request.params; + const query = toSnakeCase(params); + return caller('transport.request', { method, path, body, query }, options); +} + +function toSnakeCase(obj: Record) { + return mapKeys(obj, (value, key) => snakeCase(key)); +} diff --git a/x-pack/plugins/data_enhanced/server/search/index.ts b/x-pack/plugins/data_enhanced/server/search/index.ts new file mode 100644 index 000000000000..f914326f30d3 --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { enhancedEsSearchStrategyProvider } from './es_search_strategy';