Skip to content

Commit

Permalink
[data.search] Allow search response to follow new hits format (#88115) (
Browse files Browse the repository at this point in the history
#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>
  • Loading branch information
lukasolson and kibanamachine committed Feb 2, 2021
1 parent 55f4b36 commit c951f4e
Show file tree
Hide file tree
Showing 25 changed files with 213 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) &gt; [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`<!-- -->.

<b>Signature:</b>

```typescript
legacyHitsTotal?: boolean;
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ISearchOptions
| [abortSignal](./kibana-plugin-plugins-data-public.isearchoptions.abortsignal.md) | <code>AbortSignal</code> | An <code>AbortSignal</code> that allows the caller of <code>search</code> to abort a search request. |
| [isRestore](./kibana-plugin-plugins-data-public.isearchoptions.isrestore.md) | <code>boolean</code> | 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) | <code>boolean</code> | Whether the session is already saved (i.e. sent to background) |
| [legacyHitsTotal](./kibana-plugin-plugins-data-public.isearchoptions.legacyhitstotal.md) | <code>boolean</code> | Request the legacy format for the total number of hits. If sending <code>rest_total_hits_as_int</code> to something other than <code>true</code>, this should be set to <code>false</code>. |
| [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | <code>string</code> | A session ID, grouping multiple search requests into a single session. |
| [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | <code>string</code> | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. |

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md)

## IScopedSessionService interface

<b>Signature:</b>

```typescript
export interface IScopedSessionService
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md) | <code>&lt;Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse&gt;(strategy: ISearchStrategy&lt;Request, Response&gt;, ...args: Parameters&lt;ISearchStrategy&lt;Request, Response&gt;['search']&gt;) =&gt; Observable&lt;Response&gt;</code> | |

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IScopedSessionService](./kibana-plugin-plugins-data-server.iscopedsessionservice.md) &gt; [search](./kibana-plugin-plugins-data-server.iscopedsessionservice.search.md)

## IScopedSessionService.search property

<b>Signature:</b>

```typescript
search: <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(strategy: ISearchStrategy<Request, Response>, ...args: Parameters<ISearchStrategy<Request, Response>['search']>) => Observable<Response>;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) &gt; [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`<!-- -->.

<b>Signature:</b>

```typescript
legacyHitsTotal?: boolean;
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface ISearchOptions
| [abortSignal](./kibana-plugin-plugins-data-server.isearchoptions.abortsignal.md) | <code>AbortSignal</code> | An <code>AbortSignal</code> that allows the caller of <code>search</code> to abort a search request. |
| [isRestore](./kibana-plugin-plugins-data-server.isearchoptions.isrestore.md) | <code>boolean</code> | 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) | <code>boolean</code> | Whether the session is already saved (i.e. sent to background) |
| [legacyHitsTotal](./kibana-plugin-plugins-data-server.isearchoptions.legacyhitstotal.md) | <code>boolean</code> | Request the legacy format for the total number of hits. If sending <code>rest_total_hits_as_int</code> to something other than <code>true</code>, this should be set to <code>false</code>. |
| [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | <code>string</code> | A session ID, grouping multiple search requests into a single session. |
| [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | <code>string</code> | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. |

Original file line number Diff line number Diff line change
Expand Up @@ -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) | |
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/data/common/search/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1649,6 +1649,7 @@ export interface ISearchOptions {
abortSignal?: AbortSignal;
isRestore?: boolean;
isStored?: boolean;
legacyHitsTotal?: boolean;
sessionId?: string;
strategy?: string;
}
Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export {
SearchUsage,
SessionService,
ISessionService,
IScopedSessionService,
DataApiRequestHandlerContext,
DataRequestHandlerContext,
} from './search';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -29,7 +29,7 @@ export const esSearchStrategyProvider = (
* @throws `KbnServerError`
* @returns `Observable<IEsSearchResponse<any>>`
*/
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) {
Expand All @@ -46,7 +46,8 @@ export const esSearchStrategyProvider = (
};
const promise = esClient.asCurrentUser.search<SearchResponse<unknown>>(params);
const { body } = await shimAbortSignal(promise, abortSignal);
return toKibanaSearchResponse(body);
const response = shimHitsTotal(body, options);
return toKibanaSearchResponse(response);
} catch (e) {
throw getKbnServerError(e);
}
Expand Down
77 changes: 76 additions & 1 deletion src/plugins/data/server/search/es_search/response_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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',
},
},
});
});
});
});
18 changes: 18 additions & 0 deletions src/plugins/data/server/search/es_search/response_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,3 +32,20 @@ export function toKibanaSearchResponse(rawResponse: SearchResponse<unknown>) {
...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<unknown>,
{ 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 };
}
1 change: 0 additions & 1 deletion src/plugins/data/server/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
11 changes: 1 addition & 10 deletions src/plugins/data/server/search/routes/bsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -15,7 +15,6 @@ import {
ISearchClient,
ISearchOptions,
} from '../../../common/search';
import { shimHitsTotal } from './shim_hits_total';

type GetScopedProider = (coreStart: CoreStart) => (request: KibanaRequest) => ISearchClient;

Expand All @@ -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
Expand Down
3 changes: 1 addition & 2 deletions src/plugins/data/server/search/routes/call_msearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 0 additions & 1 deletion src/plugins/data/server/search/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@
export * from './call_msearch';
export * from './msearch';
export * from './search';
export * from './shim_hits_total';
20 changes: 10 additions & 10 deletions src/plugins/data/server/search/routes/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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()),
Expand All @@ -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$);

Expand All @@ -47,6 +53,7 @@ export function registerSearchRoute(router: DataPluginRouter): void {
{
abortSignal,
strategy,
legacyHitsTotal,
sessionId,
isStored,
isRestore,
Expand All @@ -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);
}
Expand Down
Loading

0 comments on commit c951f4e

Please sign in to comment.