From c951f4ed38caaf9d9cfee01e444960fcfd2a22d0 Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Mon, 1 Feb 2021 19:06:57 -0700 Subject: [PATCH] [data.search] Allow search response to follow new hits format (#88115) (#89951) * [data.search] Allow search response to follow new hits format * Update docs * Fix types * Fix types * Doc updates * Remove declare module * Remove declare module Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...a-public.isearchoptions.legacyhitstotal.md | 13 ++++ ...ugin-plugins-data-public.isearchoptions.md | 1 + ...ugins-data-server.iscopedsessionservice.md | 18 +++++ ...ata-server.iscopedsessionservice.search.md | 11 +++ ...a-server.isearchoptions.legacyhitstotal.md | 13 ++++ ...ugin-plugins-data-server.isearchoptions.md | 1 + .../kibana-plugin-plugins-data-server.md | 1 + src/plugins/data/common/search/types.ts | 7 ++ src/plugins/data/public/public.api.md | 1 + src/plugins/data/server/index.ts | 1 + .../search/es_search/es_search_strategy.ts | 7 +- .../search/es_search/response_utils.test.ts | 77 ++++++++++++++++++- .../server/search/es_search/response_utils.ts | 18 +++++ src/plugins/data/server/search/index.ts | 1 - .../data/server/search/routes/bsearch.ts | 11 +-- .../data/server/search/routes/call_msearch.ts | 3 +- .../data/server/search/routes/index.ts | 1 - .../data/server/search/routes/search.ts | 20 ++--- .../search/routes/shim_hits_total.test.ts | 58 -------------- .../server/search/routes/shim_hits_total.ts | 22 ------ .../data/server/search/search_service.ts | 6 +- src/plugins/data/server/server.api.md | 45 ++++++----- .../server/search/es_search_strategy.ts | 6 +- .../security_solution/index.ts | 2 +- .../server/search_strategy/timeline/index.ts | 2 +- 25 files changed, 213 insertions(+), 133 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.search.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md delete mode 100644 src/plugins/data/server/search/routes/shim_hits_total.test.ts delete mode 100644 src/plugins/data/server/search/routes/shim_hits_total.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md new file mode 100644 index 00000000000000..937e20a7a95795 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [legacyHitsTotal](./kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md) + +## ISearchOptions.legacyHitsTotal property + +Request the legacy format for the total number of hits. If sending `rest_total_hits_as_int` to something other than `true`, this should be set to `false`. + +Signature: + +```typescript +legacyHitsTotal?: boolean; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md index 5acd837495dac4..fc2767cd0231f9 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md @@ -17,6 +17,7 @@ export interface ISearchOptions | [abortSignal](./kibana-plugin-plugins-data-public.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | | [isRestore](./kibana-plugin-plugins-data-public.isearchoptions.isrestore.md) | boolean | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) | | [isStored](./kibana-plugin-plugins-data-public.isearchoptions.isstored.md) | boolean | Whether the session is already saved (i.e. sent to background) | +| [legacyHitsTotal](./kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md) | boolean | Request the legacy format for the total number of hits. If sending rest_total_hits_as_int to something other than true, this should be set to false. | | [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.md new file mode 100644 index 00000000000000..eaac671b9a1826 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) + +## IScopedSessionService interface + +Signature: + +```typescript +export interface IScopedSessionService +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md) | <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>) => Observable<Response> | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.search.md new file mode 100644 index 00000000000000..d58a9fd9f37619 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iscopedsessionservice.search.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) > [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md) + +## IScopedSessionService.search property + +Signature: + +```typescript +search: (strategy: ISearchStrategy, ...args: Parameters['search']>) => Observable; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md new file mode 100644 index 00000000000000..59b8b2c6b446f6 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) > [legacyHitsTotal](./kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md) + +## ISearchOptions.legacyHitsTotal property + +Request the legacy format for the total number of hits. If sending `rest_total_hits_as_int` to something other than `true`, this should be set to `false`. + +Signature: + +```typescript +legacyHitsTotal?: boolean; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md index 85847e1c61d25b..9de351b2b90194 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md @@ -17,6 +17,7 @@ export interface ISearchOptions | [abortSignal](./kibana-plugin-plugins-data-server.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | | [isRestore](./kibana-plugin-plugins-data-server.isearchoptions.isrestore.md) | boolean | Whether the session is restored (i.e. search requests should re-use the stored search IDs, rather than starting from scratch) | | [isStored](./kibana-plugin-plugins-data-server.isearchoptions.isstored.md) | boolean | Whether the session is already saved (i.e. sent to background) | +| [legacyHitsTotal](./kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md) | boolean | Request the legacy format for the total number of hits. If sending rest_total_hits_as_int to something other than true, this should be set to false. | | [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 84c7875c26ce89..27a386a714fc1a 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -53,6 +53,7 @@ | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | | [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Interface for an index pattern saved object | +| [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) | | | [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | | | [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | | diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index c1293f44154583..38e963591f25ce 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -84,11 +84,18 @@ export interface ISearchOptions { * An `AbortSignal` that allows the caller of `search` to abort a search request. */ abortSignal?: AbortSignal; + /** * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. */ strategy?: string; + /** + * Request the legacy format for the total number of hits. If sending `rest_total_hits_as_int` to + * something other than `true`, this should be set to `false`. + */ + legacyHitsTotal?: boolean; + /** * A session ID, grouping multiple search requests into a single session. */ diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 8feed5c86fc78d..694ca24ac3a98a 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1649,6 +1649,7 @@ export interface ISearchOptions { abortSignal?: AbortSignal; isRestore?: boolean; isStored?: boolean; + legacyHitsTotal?: boolean; sessionId?: string; strategy?: string; } diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 27af11674d061e..370ff180fa5627 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -235,6 +235,7 @@ export { SearchUsage, SessionService, ISessionService, + IScopedSessionService, DataApiRequestHandlerContext, DataRequestHandlerContext, } from './search'; 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 c176a50627b92e..2d9b16ac8b00bf 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 @@ -13,7 +13,7 @@ import type { Logger, SharedGlobalConfig } from 'kibana/server'; import type { ISearchStrategy } from '../types'; import type { SearchUsage } from '../collectors'; import { getDefaultSearchParams, getShardTimeout, shimAbortSignal } from './request_utils'; -import { toKibanaSearchResponse } from './response_utils'; +import { shimHitsTotal, toKibanaSearchResponse } from './response_utils'; import { searchUsageObserver } from '../collectors/usage'; import { getKbnServerError, KbnServerError } from '../../../../kibana_utils/server'; @@ -29,7 +29,7 @@ export const esSearchStrategyProvider = ( * @throws `KbnServerError` * @returns `Observable>` */ - search: (request, { abortSignal }, { esClient, uiSettingsClient }) => { + search: (request, { abortSignal, ...options }, { esClient, uiSettingsClient }) => { // Only default index pattern type is supported here. // See data_enhanced for other type support. if (request.indexType) { @@ -46,7 +46,8 @@ export const esSearchStrategyProvider = ( }; const promise = esClient.asCurrentUser.search>(params); const { body } = await shimAbortSignal(promise, abortSignal); - return toKibanaSearchResponse(body); + const response = shimHitsTotal(body, options); + return toKibanaSearchResponse(response); } catch (e) { throw getKbnServerError(e); } diff --git a/src/plugins/data/server/search/es_search/response_utils.test.ts b/src/plugins/data/server/search/es_search/response_utils.test.ts index 8c973b92c7ffe7..7cb5705ecf8ef2 100644 --- a/src/plugins/data/server/search/es_search/response_utils.test.ts +++ b/src/plugins/data/server/search/es_search/response_utils.test.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { getTotalLoaded, toKibanaSearchResponse } from './response_utils'; +import { getTotalLoaded, toKibanaSearchResponse, shimHitsTotal } from './response_utils'; import { SearchResponse } from 'elasticsearch'; describe('response utils', () => { @@ -55,4 +55,79 @@ describe('response utils', () => { }); }); }); + + describe('shimHitsTotal', () => { + test('returns the total if it is already numeric', () => { + const result = shimHitsTotal({ + hits: { + total: 5, + }, + } as any); + expect(result).toEqual({ + hits: { + total: 5, + }, + }); + }); + + test('returns the total if it is inside `value`', () => { + const result = shimHitsTotal({ + hits: { + total: { + value: 5, + }, + }, + } as any); + expect(result).toEqual({ + hits: { + total: 5, + }, + }); + }); + + test('returns other properties from the response', () => { + const result = shimHitsTotal({ + _shards: {}, + hits: { + hits: [], + total: { + value: 5, + }, + }, + } as any); + expect(result).toEqual({ + _shards: {}, + hits: { + hits: [], + total: 5, + }, + }); + }); + + test('returns the response as-is if `legacyHitsTotal` is `false`', () => { + const result = shimHitsTotal( + { + _shards: {}, + hits: { + hits: [], + total: { + value: 5, + relation: 'eq', + }, + }, + } as any, + { legacyHitsTotal: false } + ); + expect(result).toEqual({ + _shards: {}, + hits: { + hits: [], + total: { + value: 5, + relation: 'eq', + }, + }, + }); + }); + }); }); diff --git a/src/plugins/data/server/search/es_search/response_utils.ts b/src/plugins/data/server/search/es_search/response_utils.ts index d4fa14866fd97d..3417f24cf420ac 100644 --- a/src/plugins/data/server/search/es_search/response_utils.ts +++ b/src/plugins/data/server/search/es_search/response_utils.ts @@ -7,6 +7,7 @@ */ import { SearchResponse } from 'elasticsearch'; +import { ISearchOptions } from '../../../common'; /** * Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is @@ -31,3 +32,20 @@ export function toKibanaSearchResponse(rawResponse: SearchResponse) { ...getTotalLoaded(rawResponse), }; } + +/** + * Temporary workaround until https://github.com/elastic/kibana/issues/26356 is addressed. + * Since we are setting `track_total_hits` in the request, `hits.total` will be an object + * containing the `value`. + * + * @internal + */ +export function shimHitsTotal( + response: SearchResponse, + { legacyHitsTotal = true }: ISearchOptions = {} +) { + if (!legacyHitsTotal) return response; + const total = (response.hits?.total as any)?.value ?? response.hits?.total; + const hits = { ...response.hits, total }; + return { ...response, hits }; +} diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index a3334241100655..301b0989b51839 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -10,5 +10,4 @@ export * from './types'; export * from './es_search'; export { usageProvider, SearchUsage, searchUsageObserver } from './collectors'; export * from './aggs'; -export { shimHitsTotal } from './routes'; export * from './session'; diff --git a/src/plugins/data/server/search/routes/bsearch.ts b/src/plugins/data/server/search/routes/bsearch.ts index e30b7bdaa84022..ba96726b787c02 100644 --- a/src/plugins/data/server/search/routes/bsearch.ts +++ b/src/plugins/data/server/search/routes/bsearch.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { catchError, first, map } from 'rxjs/operators'; +import { catchError, first } from 'rxjs/operators'; import { CoreStart, KibanaRequest } from 'src/core/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { @@ -15,7 +15,6 @@ import { ISearchClient, ISearchOptions, } from '../../../common/search'; -import { shimHitsTotal } from './shim_hits_total'; type GetScopedProider = (coreStart: CoreStart) => (request: KibanaRequest) => ISearchClient; @@ -40,14 +39,6 @@ export function registerBsearchRoute( .search(requestData, options) .pipe( first(), - map((response) => { - return { - ...response, - ...{ - rawResponse: shimHitsTotal(response.rawResponse), - }, - }; - }), catchError((err) => { // Re-throw as object, to get attributes passed to the client // eslint-disable-next-line no-throw-literal diff --git a/src/plugins/data/server/search/routes/call_msearch.ts b/src/plugins/data/server/search/routes/call_msearch.ts index fc30e2f29c3ef2..e6ff5f454079b2 100644 --- a/src/plugins/data/server/search/routes/call_msearch.ts +++ b/src/plugins/data/server/search/routes/call_msearch.ts @@ -12,9 +12,8 @@ import { SearchResponse } from 'elasticsearch'; import { IUiSettingsClient, IScopedClusterClient, SharedGlobalConfig } from 'src/core/server'; import type { MsearchRequestBody, MsearchResponse } from '../../../common/search/search_source'; -import { shimHitsTotal } from './shim_hits_total'; import { getKbnServerError } from '../../../../kibana_utils/server'; -import { getShardTimeout, getDefaultSearchParams, shimAbortSignal } from '..'; +import { getShardTimeout, getDefaultSearchParams, shimAbortSignal, shimHitsTotal } from '..'; /** @internal */ export function convertRequestBody( diff --git a/src/plugins/data/server/search/routes/index.ts b/src/plugins/data/server/search/routes/index.ts index ea20240f6ae19b..25e0353fb4a27a 100644 --- a/src/plugins/data/server/search/routes/index.ts +++ b/src/plugins/data/server/search/routes/index.ts @@ -9,4 +9,3 @@ export * from './call_msearch'; export * from './msearch'; export * from './search'; -export * from './shim_hits_total'; diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 6d2da4c1e63ddb..e556e3ca49ec29 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -9,7 +9,6 @@ import { first } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; import { getRequestAbortedSignal } from '../../lib'; -import { shimHitsTotal } from './shim_hits_total'; import { reportServerError } from '../../../../kibana_utils/server'; import type { DataPluginRouter } from '../types'; @@ -27,6 +26,7 @@ export function registerSearchRoute(router: DataPluginRouter): void { body: schema.object( { + legacyHitsTotal: schema.maybe(schema.boolean()), sessionId: schema.maybe(schema.string()), isStored: schema.maybe(schema.boolean()), isRestore: schema.maybe(schema.boolean()), @@ -36,7 +36,13 @@ export function registerSearchRoute(router: DataPluginRouter): void { }, }, async (context, request, res) => { - const { sessionId, isStored, isRestore, ...searchRequest } = request.body; + const { + legacyHitsTotal = true, + sessionId, + isStored, + isRestore, + ...searchRequest + } = request.body; const { strategy, id } = request.params; const abortSignal = getRequestAbortedSignal(request.events.aborted$); @@ -47,6 +53,7 @@ export function registerSearchRoute(router: DataPluginRouter): void { { abortSignal, strategy, + legacyHitsTotal, sessionId, isStored, isRestore, @@ -55,14 +62,7 @@ export function registerSearchRoute(router: DataPluginRouter): void { .pipe(first()) .toPromise(); - return res.ok({ - body: { - ...response, - ...{ - rawResponse: shimHitsTotal(response.rawResponse), - }, - }, - }); + return res.ok({ body: response }); } catch (err) { return reportServerError(res, err); } diff --git a/src/plugins/data/server/search/routes/shim_hits_total.test.ts b/src/plugins/data/server/search/routes/shim_hits_total.test.ts deleted file mode 100644 index 6dcd7c3ff6c70c..00000000000000 --- a/src/plugins/data/server/search/routes/shim_hits_total.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { shimHitsTotal } from './shim_hits_total'; - -describe('shimHitsTotal', () => { - test('returns the total if it is already numeric', () => { - const result = shimHitsTotal({ - hits: { - total: 5, - }, - } as any); - expect(result).toEqual({ - hits: { - total: 5, - }, - }); - }); - - test('returns the total if it is inside `value`', () => { - const result = shimHitsTotal({ - hits: { - total: { - value: 5, - }, - }, - } as any); - expect(result).toEqual({ - hits: { - total: 5, - }, - }); - }); - - test('returns other properties from the response', () => { - const result = shimHitsTotal({ - _shards: {}, - hits: { - hits: [], - total: { - value: 5, - }, - }, - } as any); - expect(result).toEqual({ - _shards: {}, - hits: { - hits: [], - total: 5, - }, - }); - }); -}); diff --git a/src/plugins/data/server/search/routes/shim_hits_total.ts b/src/plugins/data/server/search/routes/shim_hits_total.ts deleted file mode 100644 index 4b56d6394e0dbc..00000000000000 --- a/src/plugins/data/server/search/routes/shim_hits_total.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { SearchResponse } from 'elasticsearch'; - -/** - * Temporary workaround until https://github.com/elastic/kibana/issues/26356 is addressed. - * Since we are setting `track_total_hits` in the request, `hits.total` will be an object - * containing the `value`. - * - * @internal - */ -export function shimHitsTotal(response: SearchResponse) { - const total = (response.hits?.total as any)?.value ?? response.hits?.total; - const hits = { ...response.hits, total }; - return { ...response, hits }; -} diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 63593bbe84a088..e9f0edbd4d6c47 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -34,7 +34,7 @@ import { AggsService } from './aggs'; import { FieldFormatsStart } from '../field_formats'; import { IndexPatternsServiceStart } from '../index_patterns'; -import { getCallMsearch, registerMsearchRoute, registerSearchRoute, shimHitsTotal } from './routes'; +import { getCallMsearch, registerMsearchRoute, registerSearchRoute } from './routes'; import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search'; import { DataPluginStart } from '../plugin'; import { UsageCollectionSetup } from '../../../usage_collection/server'; @@ -62,7 +62,7 @@ import { } from '../../common/search/aggs/buckets/shard_delay'; import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn'; import { ConfigSchema } from '../../config'; -import { SessionService, IScopedSessionService, ISessionService } from './session'; +import { IScopedSessionService, ISessionService, SessionService } from './session'; import { KbnServerError } from '../../../kibana_utils/server'; import { registerBsearchRoute } from './routes/bsearch'; @@ -209,7 +209,7 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: (key: string): T => uiSettingsCache[key], search: asScoped(request).search, - onResponse: (req, res) => shimHitsTotal(res), + onResponse: (req, res) => res, legacy: { callMsearch: getCallMsearch({ esClient, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index dcb18845ba0218..1c3b4eae620511 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -310,8 +310,6 @@ export const config: PluginConfigDescriptor; // // @public (undocumented) export interface DataApiRequestHandlerContext extends ISearchClient { - // Warning: (ae-forgotten-export) The symbol "IScopedSessionService" needs to be exported by the entry point index.d.ts - // // (undocumented) session: IScopedSessionService; } @@ -916,6 +914,16 @@ export class IndexPatternsService implements Plugin_3(strategy: ISearchStrategy, ...args: Parameters['search']>) => Observable; +} + // Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -923,6 +931,7 @@ export interface ISearchOptions { abortSignal?: AbortSignal; isRestore?: boolean; isStored?: boolean; + legacyHitsTotal?: boolean; sessionId?: string; strategy?: string; } @@ -1288,7 +1297,7 @@ export class SessionService implements ISessionService { export const shimAbortSignal: (promise: TransportRequestPromise, signal?: AbortSignal | undefined) => TransportRequestPromise; // @internal -export function shimHitsTotal(response: SearchResponse): { +export function shimHitsTotal(response: SearchResponse, { legacyHitsTotal }?: ISearchOptions): { hits: { total: any; max_score: number; @@ -1297,7 +1306,7 @@ export function shimHitsTotal(response: SearchResponse): { _type: string; _id: string; _score: number; - _source: any; + _source: unknown; _version?: number | undefined; _explanation?: import("elasticsearch").Explanation | undefined; fields?: any; @@ -1430,20 +1439,20 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:100:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:245:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:261:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:269:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:244:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:247:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:258:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:263:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:271:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index_patterns/index_patterns_service.ts:59:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:79:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts // src/plugins/data/server/search/types.ts:103:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts 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 index 694d9807b5a4d3..dc1fa13d32e27e 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -23,6 +23,7 @@ import { getTotalLoaded, searchUsageObserver, shimAbortSignal, + shimHitsTotal, } from '../../../../../src/plugins/data/server'; import type { IAsyncSearchOptions } from '../../common'; import { pollSearch } from '../../common'; @@ -63,7 +64,8 @@ export const enhancedEsSearchStrategyProvider = ( ? client.get({ ...params, id }) : client.submit(params); const { body } = await shimAbortSignal(promise, options.abortSignal); - return toAsyncKibanaSearchResponse(body); + const response = shimHitsTotal(body.response, options); + return toAsyncKibanaSearchResponse({ ...body, response }); }; const cancel = async () => { @@ -108,7 +110,7 @@ export const enhancedEsSearchStrategyProvider = ( const esResponse = await shimAbortSignal(promise, options?.abortSignal); const response = esResponse.body as SearchResponse; return { - rawResponse: response, + rawResponse: shimHitsTotal(response, options), ...getTotalLoaded(response), }; } catch (e) { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index 8b2cce01cf07a6..d25f1aaccc5e7f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -37,7 +37,7 @@ export const securitySolutionSearchStrategyProvider =