[Search] Unify search plugin step 1 (#95811)

Remove the defaultStrategy override
Move async search strategy to data
Move EQL search strategy to data
Move rest of common/search/session data (Moving whole search/session is blocked by security and taskManager)
This commit is contained in:
Anton Dosov 2021-04-21 14:23:49 +02:00 committed by GitHub
parent 7405a22cf3
commit bd4f461c70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
105 changed files with 601 additions and 533 deletions

View file

@ -362,7 +362,7 @@ Failure to have auth enabled in Kibana will make for a broken UI. UI-based error
|{kib-repo}blob/{branch}/x-pack/plugins/data_enhanced/README.md[dataEnhanced]
|The data_enhanced plugin is the x-pack counterpart to the OSS data plugin.
|The data_enhanced plugin is the x-pack counterpart to the src/plguins/data plugin.
|{kib-repo}blob/{branch}/x-pack/plugins/discover_enhanced/README.md[discoverEnhanced]

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [expiration\_time\_in\_millis](./kibana-plugin-plugins-data-server.asyncsearchresponse.expiration_time_in_millis.md)
## AsyncSearchResponse.expiration\_time\_in\_millis property
<b>Signature:</b>
```typescript
expiration_time_in_millis: number;
```

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [id](./kibana-plugin-plugins-data-server.asyncsearchresponse.id.md)
## AsyncSearchResponse.id property
<b>Signature:</b>
```typescript
id?: string;
```

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [is\_partial](./kibana-plugin-plugins-data-server.asyncsearchresponse.is_partial.md)
## AsyncSearchResponse.is\_partial property
<b>Signature:</b>
```typescript
is_partial: boolean;
```

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [is\_running](./kibana-plugin-plugins-data-server.asyncsearchresponse.is_running.md)
## AsyncSearchResponse.is\_running property
<b>Signature:</b>
```typescript
is_running: boolean;
```

View file

@ -0,0 +1,23 @@
<!-- 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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md)
## AsyncSearchResponse interface
<b>Signature:</b>
```typescript
export interface AsyncSearchResponse<T = unknown>
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [expiration\_time\_in\_millis](./kibana-plugin-plugins-data-server.asyncsearchresponse.expiration_time_in_millis.md) | <code>number</code> | |
| [id](./kibana-plugin-plugins-data-server.asyncsearchresponse.id.md) | <code>string</code> | |
| [is\_partial](./kibana-plugin-plugins-data-server.asyncsearchresponse.is_partial.md) | <code>boolean</code> | |
| [is\_running](./kibana-plugin-plugins-data-server.asyncsearchresponse.is_running.md) | <code>boolean</code> | |
| [response](./kibana-plugin-plugins-data-server.asyncsearchresponse.response.md) | <code>estypes.SearchResponse&lt;T&gt;</code> | |
| [start\_time\_in\_millis](./kibana-plugin-plugins-data-server.asyncsearchresponse.start_time_in_millis.md) | <code>number</code> | |

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [response](./kibana-plugin-plugins-data-server.asyncsearchresponse.response.md)
## AsyncSearchResponse.response property
<b>Signature:</b>
```typescript
response: estypes.SearchResponse<T>;
```

View file

@ -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; [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) &gt; [start\_time\_in\_millis](./kibana-plugin-plugins-data-server.asyncsearchresponse.start_time_in_millis.md)
## AsyncSearchResponse.start\_time\_in\_millis property
<b>Signature:</b>
```typescript
start_time_in_millis: number;
```

View file

@ -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; [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md) &gt; [\_shards](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse._shards.md)
## AsyncSearchStatusResponse.\_shards property
<b>Signature:</b>
```typescript
_shards: ShardsResponse;
```

View file

@ -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; [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md) &gt; [completion\_status](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.completion_status.md)
## AsyncSearchStatusResponse.completion\_status property
<b>Signature:</b>
```typescript
completion_status: number;
```

View file

@ -0,0 +1,19 @@
<!-- 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; [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md)
## AsyncSearchStatusResponse interface
<b>Signature:</b>
```typescript
export interface AsyncSearchStatusResponse extends Omit<AsyncSearchResponse, 'response'>
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [\_shards](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse._shards.md) | <code>ShardsResponse</code> | |
| [completion\_status](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.completion_status.md) | <code>number</code> | |

View file

@ -45,6 +45,8 @@
| --- | --- |
| [AggFunctionsMapping](./kibana-plugin-plugins-data-server.aggfunctionsmapping.md) | A global list of the expression function definitions for each agg type function. |
| [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | |
| [AsyncSearchResponse](./kibana-plugin-plugins-data-server.asyncsearchresponse.md) | |
| [AsyncSearchStatusResponse](./kibana-plugin-plugins-data-server.asyncsearchstatusresponse.md) | |
| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | |
| [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | |
| [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | |

View file

@ -623,8 +623,7 @@ The `SearchSource` API is a convenient way to construct and run an Elasticsearch
#### Default Search Strategy
One benefit of using the low-level search API, is partial response support in X-Pack, allowing for a better and more responsive user experience.
In OSS only the final result is returned.
One benefit of using the low-level search API, is partial response support, allowing for a better and more responsive user experience.
```.ts
import { isCompleteResponse } from '../plugins/data/public';

View file

@ -7,10 +7,13 @@
*/
export * from './aggs';
export * from './es_search';
export * from './expressions';
export * from './search_source';
export * from './tabify';
export * from './types';
export * from './utils';
export * from './session';
export * from './poll_search';
export * from './strategies/es_search';
export * from './strategies/eql_search';
export * from './strategies/ese_search';

View file

@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { pollSearch } from './poll_search';

View file

@ -1,16 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { from, Observable, timer, defer, fromEvent, EMPTY } from 'rxjs';
import { expand, map, switchMap, takeUntil, takeWhile, tap } from 'rxjs/operators';
import type { IKibanaSearchResponse } from '../../../../../src/plugins/data/common';
import type {
IAsyncSearchOptions,
IKibanaSearchResponse,
} from '../../../../../src/plugins/data/common';
import { isErrorResponse, isPartialResponse } from '../../../../../src/plugins/data/common';
import { AbortError } from '../../../../../src/plugins/kibana_utils/common';
import type { IAsyncSearchOptions } from './types';
export const pollSearch = <Response extends IKibanaSearchResponse>(
search: () => Promise<Response>,

View file

@ -1,20 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { EqlSearch } from '@elastic/elasticsearch/api/requestParams';
import { ApiResponse, TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport';
import {
ISearchOptions,
IKibanaSearchRequest,
IKibanaSearchResponse,
} from '../../../../../src/plugins/data/common';
export const ENHANCED_ES_SEARCH_STRATEGY = 'ese';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../types';
export const EQL_SEARCH_STRATEGY = 'eql';
@ -25,10 +20,3 @@ export interface EqlSearchStrategyRequest extends IKibanaSearchRequest<EqlReques
}
export type EqlSearchStrategyResponse<T = unknown> = IKibanaSearchResponse<ApiResponse<T>>;
export interface IAsyncSearchOptions extends ISearchOptions {
/**
* The number of milliseconds to wait between receiving a response and sending another request
*/
pollInterval?: number;
}

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './types';

View file

@ -7,7 +7,7 @@
*/
import type { estypes } from '@elastic/elasticsearch';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../types';
import { IKibanaSearchRequest, IKibanaSearchResponse } from '../../types';
export const ES_SEARCH_STRATEGY = 'es';

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './types';

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ISearchOptions } from '../../types';
export const ENHANCED_ES_SEARCH_STRATEGY = 'ese';
export interface IAsyncSearchOptions extends ISearchOptions {
/**
* The number of milliseconds to wait between receiving a response and sending another request
*/
pollInterval?: number;
}

View file

@ -7,8 +7,7 @@
*/
import { Observable } from 'rxjs';
import { IEsSearchRequest, IEsSearchResponse } from './es_search';
import { IndexPattern } from '..';
import { IEsSearchRequest, IEsSearchResponse, IndexPattern } from '..';
import type { RequestAdapter } from '../../../inspector/common';
export type ISearchGeneric = <

View file

@ -30,3 +30,62 @@ export const configSchema = schema.object({
});
export type ConfigSchema = TypeOf<typeof configSchema>;
export const searchSessionsConfigSchema = schema.object({
/**
* Turns the feature on \ off (incl. removing indicator and management screens)
*/
enabled: schema.boolean({ defaultValue: true }),
/**
* pageSize controls how many search session objects we load at once while monitoring
* session completion
*/
pageSize: schema.number({ defaultValue: 100 }),
/**
* trackingInterval controls how often we track search session objects progress
*/
trackingInterval: schema.duration({ defaultValue: '10s' }),
/**
* monitoringTaskTimeout controls for how long task manager waits for search session monitoring task to complete before considering it timed out,
* If tasks timeouts it receives cancel signal and next task starts in "trackingInterval" time
*/
monitoringTaskTimeout: schema.duration({ defaultValue: '5m' }),
/**
* notTouchedTimeout controls how long do we store unpersisted search session results,
* after the last search in the session has completed
*/
notTouchedTimeout: schema.duration({ defaultValue: '5m' }),
/**
* notTouchedInProgressTimeout controls how long do allow a search session to run after
* a user has navigated away without persisting
*/
notTouchedInProgressTimeout: schema.duration({ defaultValue: '1m' }),
/**
* maxUpdateRetries controls how many retries we perform while attempting to save a search session
*/
maxUpdateRetries: schema.number({ defaultValue: 3 }),
/**
* defaultExpiration controls how long search sessions are valid for, until they are expired.
*/
defaultExpiration: schema.duration({ defaultValue: '7d' }),
management: schema.object({
/**
* maxSessions controls how many saved search sessions we display per page on the management screen.
*/
maxSessions: schema.number({ defaultValue: 10000 }),
/**
* refreshInterval controls how often we refresh the management screen.
*/
refreshInterval: schema.duration({ defaultValue: '10s' }),
/**
* refreshTimeout controls how often we refresh the management screen.
*/
refreshTimeout: schema.duration({ defaultValue: '1m' }),
expiresSoonWarning: schema.duration({ defaultValue: '1d' }),
}),
});
export type SearchSessionsConfigSchema = TypeOf<typeof searchSessionsConfigSchema>;

View file

@ -2354,8 +2354,8 @@ export const SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions";
// Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "intl" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "isClearable" | "intl" | "refreshInterval" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
export const SearchBar: React.ComponentClass<Pick<Pick<SearchBarProps, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "isClearable" | "intl" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "isClearable" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & {
WrappedComponent: React.ComponentType<Pick<SearchBarProps, "query" | "placeholder" | "isLoading" | "iconType" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "isClearable" | "intl" | "nonKqlMode" | "nonKqlModeHelpText" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated"> & ReactIntl.InjectedIntlProps>;
};
// Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts

View file

@ -136,7 +136,7 @@ describe('SearchInterceptor', () => {
await searchInterceptor.search(mockRequest, { sessionId }).toPromise();
expect(fetchMock.mock.calls[0][0]).toEqual(
expect.objectContaining({
options: { sessionId, isStored: true, isRestore: true },
options: { sessionId, isStored: true, isRestore: true, strategy: 'es' },
})
);

View file

@ -14,6 +14,7 @@ import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public';
import { i18n } from '@kbn/i18n';
import { BatchedFunc, BfetchPublicSetup } from 'src/plugins/bfetch/public';
import {
ES_SEARCH_STRATEGY,
IKibanaSearchRequest,
IKibanaSearchResponse,
ISearchOptions,
@ -189,6 +190,11 @@ export class SearchInterceptor {
request: IKibanaSearchRequest,
options: ISearchOptions = {}
): Observable<IKibanaSearchResponse> {
options = {
strategy: ES_SEARCH_STRATEGY,
...options,
};
// Defer the following logic until `subscribe` is actually called
return defer(() => {
if (options.abortSignal?.aborted) {

View file

@ -235,6 +235,8 @@ export {
ISearchSessionService,
SearchRequestHandlerContext,
DataRequestHandlerContext,
AsyncSearchResponse,
AsyncSearchStatusResponse,
} from './search';
// Search namespace

View file

@ -5,9 +5,8 @@ object, and return a response object, of a given shape.
Both client side search strategies can be registered, as well as server side search strategies.
The `search` plugin includes two one concrete client side implementations -
`SYNC_SEARCH_STRATEGY` and `ES_SEARCH_STRATEGY` which uses `SYNC_SEARCH_STRATEGY`. There is also one
default server side search strategy, `ES_SEARCH_STRATEGY`.
The `search` plugin includes:
Includes the `esSearch` plugin in order to search for data from Elasticsearch using Elasticsearch
DSL.
- ES_SEARCH_STRATEGY - hitting regular es `_search` endpoint using query DSL
- (default) ESE_SEARCH_STRATEGY (Enhanced ES) - hitting `_async_search` endpoint and works with search sessions
- EQL_SEARCH_STRATEGY

View file

@ -7,7 +7,9 @@
*/
export * from './types';
export * from './es_search';
export * from './strategies/es_search';
export * from './strategies/ese_search';
export * from './strategies/eql_search';
export { usageProvider, SearchUsage, searchUsageObserver } from './collectors';
export * from './aggs';
export * from './session';

View file

@ -29,6 +29,7 @@ import {
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { expressionsPluginMock } from '../../../expressions/public/mocks';
import { createSearchSessionsClientMock } from './mocks';
import { ENHANCED_ES_SEARCH_STRATEGY } from '../../common';
describe('Search service', () => {
let plugin: SearchService;
@ -85,7 +86,7 @@ describe('Search service', () => {
describe('asScopedProvider', () => {
let mockScopedClient: IScopedSearchClient;
let searcPluginStart: ISearchStart<IEsSearchRequest, IEsSearchResponse<any>>;
let searchPluginStart: ISearchStart<IEsSearchRequest, IEsSearchResponse<any>>;
let mockStrategy: any;
let mockStrategyNoCancel: jest.Mocked<ISearchStrategy>;
let mockSessionService: ISearchSessionService<any>;
@ -112,21 +113,20 @@ describe('Search service', () => {
bfetch: bfetchPluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(),
});
pluginSetup.registerSearchStrategy('es', mockStrategy);
pluginSetup.registerSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY, mockStrategy);
pluginSetup.registerSearchStrategy('nocancel', mockStrategyNoCancel);
pluginSetup.__enhance({
defaultStrategy: 'es',
sessionService: mockSessionService,
});
searcPluginStart = plugin.start(mockCoreStart, {
searchPluginStart = plugin.start(mockCoreStart, {
fieldFormats: createFieldFormatsStartMock(),
indexPatterns: createIndexPatternsStartMock(),
});
const r: any = {};
mockScopedClient = searcPluginStart.asScoped(r);
mockScopedClient = searchPluginStart.asScoped(r);
});
describe('search', () => {
@ -269,7 +269,7 @@ describe('Search service', () => {
it('cancels a saved object and search ids', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'es');
mockMap.set('abc', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockStrategy.cancel = jest.fn();
mockSessionClient.cancel = jest.fn().mockResolvedValue(mockSavedObject);
@ -281,13 +281,13 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('abc');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('cancels a saved object with some strategies that dont support cancellation, dont throw an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'nocancel');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockStrategy.cancel = jest.fn();
mockSessionClient.cancel = jest.fn().mockResolvedValue(mockSavedObject);
@ -299,13 +299,13 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('def');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('cancels a saved object with some strategies that dont exist, dont throw an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'notsupported');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockStrategy.cancel = jest.fn();
mockSessionClient.cancel = jest.fn().mockResolvedValue(mockSavedObject);
@ -317,7 +317,7 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('def');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
});
@ -349,7 +349,7 @@ describe('Search service', () => {
it('deletes a saved object and search ids', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'es');
mockMap.set('abc', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockSessionClient.delete = jest.fn().mockResolvedValue(mockSavedObject);
mockStrategy.cancel = jest.fn();
@ -361,13 +361,13 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('abc');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('deletes a saved object with some strategies that dont support cancellation, dont throw an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'nocancel');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockSessionClient.delete = jest.fn().mockResolvedValue(mockSavedObject);
mockStrategy.cancel = jest.fn();
@ -379,13 +379,13 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('def');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('deletes a saved object with some strategies that dont exist, dont throw an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'notsupported');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockStrategy.cancel = jest.fn();
mockSessionClient.delete = jest.fn().mockResolvedValue(mockSavedObject);
@ -397,7 +397,7 @@ describe('Search service', () => {
const [searchId, options] = mockStrategy.cancel.mock.calls[0];
expect(mockStrategy.cancel).toHaveBeenCalledTimes(1);
expect(searchId).toBe('def');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
});
@ -429,7 +429,7 @@ describe('Search service', () => {
it('extends a saved object and search ids', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'es');
mockMap.set('abc', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockSessionClient.extend = jest.fn().mockResolvedValue(mockSavedObject);
mockStrategy.extend = jest.fn();
@ -441,13 +441,13 @@ describe('Search service', () => {
const [searchId, keepAlive, options] = mockStrategy.extend.mock.calls[0];
expect(searchId).toBe('abc');
expect(keepAlive).toContain('ms');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('doesnt extend the saved object with some strategies that dont support cancellation, throws an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'nocancel');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockSessionClient.extend = jest.fn().mockResolvedValue(mockSavedObject);
mockStrategy.extend = jest.fn().mockResolvedValue({});
@ -462,13 +462,13 @@ describe('Search service', () => {
const [searchId, keepAlive, options] = mockStrategy.extend.mock.calls[0];
expect(searchId).toBe('def');
expect(keepAlive).toContain('ms');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
it('doesnt extend the saved object with some strategies that dont exist, throws an error', async () => {
const mockMap = new Map<string, string>();
mockMap.set('abc', 'notsupported');
mockMap.set('def', 'es');
mockMap.set('def', ENHANCED_ES_SEARCH_STRATEGY);
mockSessionClient.getSearchIdMapping = jest.fn().mockResolvedValue(mockMap);
mockSessionClient.extend = jest.fn().mockResolvedValue(mockSavedObject);
mockStrategy.extend = jest.fn().mockResolvedValue({});
@ -483,7 +483,7 @@ describe('Search service', () => {
const [searchId, keepAlive, options] = mockStrategy.extend.mock.calls[0];
expect(searchId).toBe('def');
expect(keepAlive).toContain('ms');
expect(options).toHaveProperty('strategy', 'es');
expect(options).toHaveProperty('strategy', ENHANCED_ES_SEARCH_STRATEGY);
});
});
});

View file

@ -37,7 +37,7 @@ import { AggsService } from './aggs';
import { FieldFormatsStart } from '../field_formats';
import { IndexPatternsServiceStart } from '../index_patterns';
import { getCallMsearch, registerMsearchRoute, registerSearchRoute } from './routes';
import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './es_search';
import { ES_SEARCH_STRATEGY, esSearchStrategyProvider } from './strategies/es_search';
import { DataPluginStart, DataPluginStartDependencies } from '../plugin';
import { UsageCollectionSetup } from '../../../usage_collection/server';
import { registerUsageCollector } from './collectors/register';
@ -64,6 +64,8 @@ import {
SearchSourceService,
phraseFilterFunction,
esRawResponse,
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
} from '../../common/search';
import { getEsaggs, getEsdsl } from './expressions';
import {
@ -76,6 +78,8 @@ import { ISearchSessionService, SearchSessionService } from './session';
import { KbnServerError } from '../../../kibana_utils/server';
import { registerBsearchRoute } from './routes/bsearch';
import { getKibanaContext } from './expressions/kibana_context';
import { enhancedEsSearchStrategyProvider } from './strategies/ese_search';
import { eqlSearchStrategyProvider } from './strategies/eql_search';
type StrategyMap = Record<string, ISearchStrategy<any, any>>;
@ -101,7 +105,6 @@ export interface SearchRouteDependencies {
export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
private readonly aggsService = new AggsService();
private readonly searchSourceService = new SearchSourceService();
private defaultSearchStrategyName: string = ES_SEARCH_STRATEGY;
private searchStrategies: StrategyMap = {};
private sessionService: ISearchSessionService;
private asScoped!: ISearchStart['asScoped'];
@ -143,6 +146,17 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
)
);
this.registerSearchStrategy(
ENHANCED_ES_SEARCH_STRATEGY,
enhancedEsSearchStrategyProvider(
this.initializerContext.config.legacy.globalConfig$,
this.logger,
usage
)
);
this.registerSearchStrategy(EQL_SEARCH_STRATEGY, eqlSearchStrategyProvider(this.logger));
registerBsearchRoute(bfetch, (request: KibanaRequest) => this.asScoped(request));
core.savedObjects.registerType(searchTelemetry);
@ -181,9 +195,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
return {
__enhance: (enhancements: SearchEnhancements) => {
if (this.searchStrategies.hasOwnProperty(enhancements.defaultStrategy)) {
this.defaultSearchStrategyName = enhancements.defaultStrategy;
}
this.sessionService = enhancements.sessionService;
},
aggs,
@ -261,7 +272,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
>(
name: string = this.defaultSearchStrategyName
name: string = ENHANCED_ES_SEARCH_STRATEGY
): ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse> => {
this.logger.debug(`Get strategy ${name}`);
const strategy = this.searchStrategies[name];
@ -344,6 +355,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
strategy: strategyName,
isStored: true,
};
return this.cancel(deps, searchId, searchOptions);
})
);

View file

@ -6,7 +6,9 @@
* Side Public License, v 1.
*/
import moment from 'moment';
import { IScopedSearchSessionsClient } from './types';
import { SearchSessionsConfigSchema } from '../../../config';
export function createSearchSessionsClientMock<T = unknown>(): jest.Mocked<
IScopedSearchSessionsClient<T>
@ -22,5 +24,11 @@ export function createSearchSessionsClientMock<T = unknown>(): jest.Mocked<
cancel: jest.fn(),
extend: jest.fn(),
delete: jest.fn(),
getConfig: jest.fn(
() =>
(({
defaultExpiration: moment.duration('1', 'm'),
} as unknown) as SearchSessionsConfigSchema)
),
};
}

View file

@ -44,6 +44,9 @@ export class SearchSessionService implements ISearchSessionService {
delete: async () => {
throw new Error('delete not implemented in OSS search session service');
},
getConfig: () => {
return null;
},
});
}
}

View file

@ -15,6 +15,7 @@ import {
SavedObjectsUpdateResponse,
} from 'kibana/server';
import { IKibanaSearchRequest, ISearchOptions } from '../../../common/search';
import { SearchSessionsConfigSchema } from '../../../config';
export interface IScopedSearchSessionsClient<T = unknown> {
getId: (request: IKibanaSearchRequest, options: ISearchOptions) => Promise<string>;
@ -31,6 +32,7 @@ export interface IScopedSearchSessionsClient<T = unknown> {
cancel: (sessionId: string) => Promise<{}>;
delete: (sessionId: string) => Promise<{}>;
extend: (sessionId: string, expires: Date) => Promise<SavedObjectsUpdateResponse<T>>;
getConfig: () => SearchSessionsConfigSchema | null;
}
export interface ISearchSessionService<T = unknown> {

View file

@ -1,14 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { Logger } from 'kibana/server';
import { EqlSearchStrategyRequest } from '../../common/search/types';
import { eqlSearchStrategyProvider } from './eql_search_strategy';
import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server';
import { SearchStrategyDependencies } from '../../types';
import { EqlSearchStrategyRequest } from '../../../../common';
const getMockEqlResponse = () => ({
body: {

View file

@ -1,23 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { ApiResponse } from '@elastic/elasticsearch';
import { tap } from 'rxjs/operators';
import type { IScopedClusterClient, Logger } from 'kibana/server';
import type { ISearchStrategy } from '../../../../../src/plugins/data/server';
import type {
import {
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
IAsyncSearchOptions,
} from '../../common';
import { getDefaultSearchParams, shimAbortSignal } from '../../../../../src/plugins/data/server';
import { pollSearch } from '../../common';
import { getDefaultAsyncGetParams, getIgnoreThrottled } from './request_utils';
pollSearch,
} from '../../../../common';
import { toEqlKibanaSearchResponse } from './response_utils';
import { EqlSearchResponse } from './types';
import { ISearchStrategy } from '../../types';
import { getDefaultSearchParams, shimAbortSignal } from '../es_search';
import { getDefaultAsyncGetParams, getIgnoreThrottled } from '../ese_search/request_utils';
export const eqlSearchStrategyProvider = (
logger: Logger

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { eqlSearchStrategyProvider } from './eql_search_strategy';

View file

@ -1,27 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ApiResponse } from '@elastic/elasticsearch';
import { getTotalLoaded } from '../../../../../src/plugins/data/server';
import { AsyncSearchResponse, EqlSearchResponse } from './types';
import { EqlSearchStrategyResponse } from '../../common/search';
/**
* Get the Kibana representation of an async search response (see `IKibanaSearchResponse`).
*/
export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) {
return {
id: response.id,
rawResponse: response.response,
isPartial: response.is_partial,
isRunning: response.is_running,
...getTotalLoaded(response.response),
};
}
import { EqlSearchResponse } from './types';
import { EqlSearchStrategyResponse } from '../../../../common';
/**
* Get the Kibana representation of an EQL search response (see `IKibanaSearchResponse`).

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
export interface EqlSearchResponse<T = unknown> extends estypes.SearchResponse<T> {
id?: string;
is_partial: boolean;
is_running: boolean;
}

View file

@ -10,14 +10,14 @@ import {
elasticsearchClientMock,
MockedTransportRequestPromise,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../../core/server/elasticsearch/client/mocks';
import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks';
} from '../../../../../../core/server/elasticsearch/client/mocks';
import { pluginInitializerContextConfigMock } from '../../../../../../core/server/mocks';
import { esSearchStrategyProvider } from './es_search_strategy';
import { SearchStrategyDependencies } from '../types';
import { SearchStrategyDependencies } from '../../types';
import * as indexNotFoundException from '../../../common/search/test_data/index_not_found_exception.json';
import * as indexNotFoundException from '../../../../common/search/test_data/index_not_found_exception.json';
import { ElasticsearchClientError, ResponseError } from '@elastic/elasticsearch/lib/errors';
import { KbnServerError } from '../../../../kibana_utils/server';
import { KbnServerError } from '../../../../../kibana_utils/server';
describe('ES search strategy', () => {
const successBody = {

View file

@ -9,12 +9,12 @@
import { from, Observable } from 'rxjs';
import { first, tap } from 'rxjs/operators';
import type { Logger, SharedGlobalConfig } from 'kibana/server';
import type { ISearchStrategy } from '../types';
import type { SearchUsage } from '../collectors';
import type { ISearchStrategy } from '../../types';
import type { SearchUsage } from '../../collectors';
import { getDefaultSearchParams, getShardTimeout, shimAbortSignal } from './request_utils';
import { shimHitsTotal, toKibanaSearchResponse } from './response_utils';
import { searchUsageObserver } from '../collectors/usage';
import { getKbnServerError, KbnServerError } from '../../../../kibana_utils/server';
import { searchUsageObserver } from '../../collectors/usage';
import { getKbnServerError, KbnServerError } from '../../../../../kibana_utils/server';
export const esSearchStrategyProvider = (
config$: Observable<SharedGlobalConfig>,

View file

@ -9,4 +9,4 @@
export { esSearchStrategyProvider } from './es_search_strategy';
export * from './request_utils';
export * from './response_utils';
export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../../common';
export { ES_SEARCH_STRATEGY, IEsSearchRequest, IEsSearchResponse } from '../../../../common';

View file

@ -9,7 +9,7 @@
import type { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
import type { Search } from '@elastic/elasticsearch/api/requestParams';
import type { IUiSettingsClient, SharedGlobalConfig } from 'kibana/server';
import { UI_SETTINGS } from '../../../common';
import { UI_SETTINGS } from '../../../../common';
export function getShardTimeout(config: SharedGlobalConfig): Pick<Search, 'timeout'> {
const timeout = config.elasticsearch.shardTimeout.asMilliseconds();

View file

@ -7,7 +7,7 @@
*/
import type { estypes } from '@elastic/elasticsearch';
import { ISearchOptions } from '../../../common';
import { ISearchOptions } from '../../../../common';
/**
* Get the `total`/`loaded` for this response (see `IKibanaSearchResponse`). Note that `skipped` is

View file

@ -1,18 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { enhancedEsSearchStrategyProvider } from './es_search_strategy';
import { BehaviorSubject } from 'rxjs';
import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server/search';
import moment from 'moment';
import { KbnServerError } from '../../../../../src/plugins/kibana_utils/server';
import { KbnServerError } from '../../../../../kibana_utils/server';
import { ElasticsearchClientError, ResponseError } from '@elastic/elasticsearch/lib/errors';
import * as indexNotFoundException from '../../../../../src/plugins/data/common/search/test_data/index_not_found_exception.json';
import * as xContentParseException from '../../../../../src/plugins/data/common/search/test_data/x_content_parse_exception.json';
import * as indexNotFoundException from '../../../../common/search/test_data/index_not_found_exception.json';
import * as xContentParseException from '../../../../common/search/test_data/x_content_parse_exception.json';
import { SearchStrategyDependencies } from '../../types';
import { enhancedEsSearchStrategyProvider } from './ese_search_strategy';
import { createSearchSessionsClientMock } from '../../mocks';
const mockAsyncResponse = {
body: {
@ -61,6 +62,7 @@ describe('ES search strategy', () => {
transport: { request: mockApiCaller },
},
},
searchSessionsClient: createSearchSessionsClientMock(),
} as unknown) as SearchStrategyDependencies;
const mockLegacyConfig$ = new BehaviorSubject<any>({
elasticsearch: {
@ -72,14 +74,6 @@ describe('ES search strategy', () => {
},
});
const mockConfig: any = {
search: {
sessions: {
defaultExpiration: moment.duration('1', 'm'),
},
},
};
beforeEach(() => {
mockApiCaller.mockClear();
mockGetCaller.mockClear();
@ -88,11 +82,7 @@ describe('ES search strategy', () => {
});
it('returns a strategy with `search and `cancel`', async () => {
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
expect(typeof esSearch.search).toBe('function');
});
@ -103,11 +93,7 @@ describe('ES search strategy', () => {
mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.search({ params }, {}, mockDeps).toPromise();
@ -122,11 +108,7 @@ describe('ES search strategy', () => {
mockGetCaller.mockResolvedValueOnce(mockAsyncResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.search({ id: 'foo', params }, {}, mockDeps).toPromise();
@ -141,11 +123,7 @@ describe('ES search strategy', () => {
mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse);
const params = { index: 'foo-*', body: {} };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.search({ params }, {}, mockDeps).toPromise();
@ -159,11 +137,7 @@ describe('ES search strategy', () => {
mockApiCaller.mockResolvedValueOnce(mockRollupResponse);
const params = { index: 'foo-程', body: {} };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch
.search(
@ -188,11 +162,7 @@ describe('ES search strategy', () => {
mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.search({ params }, { sessionId: '1' }, mockDeps).toPromise();
@ -208,11 +178,7 @@ describe('ES search strategy', () => {
mockGetCaller.mockResolvedValueOnce(mockAsyncResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.search({ id: 'foo', params }, { sessionId: '1' }, mockDeps).toPromise();
@ -236,11 +202,7 @@ describe('ES search strategy', () => {
mockSubmitCaller.mockRejectedValue(errResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
let err: KbnServerError | undefined;
try {
@ -261,11 +223,7 @@ describe('ES search strategy', () => {
mockSubmitCaller.mockRejectedValue(errResponse);
const params = { index: 'logstash-*', body: { query: {} } };
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
let err: KbnServerError | undefined;
try {
@ -286,11 +244,7 @@ describe('ES search strategy', () => {
mockDeleteCaller.mockResolvedValueOnce(200);
const id = 'some_id';
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.cancel!(id, {}, mockDeps);
@ -310,11 +264,7 @@ describe('ES search strategy', () => {
mockDeleteCaller.mockRejectedValue(errResponse);
const id = 'some_id';
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
let err: KbnServerError | undefined;
try {
@ -337,11 +287,7 @@ describe('ES search strategy', () => {
const id = 'some_other_id';
const keepAlive = '1d';
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
await esSearch.extend!(id, keepAlive, {}, mockDeps);
@ -356,11 +302,7 @@ describe('ES search strategy', () => {
const id = 'some_other_id';
const keepAlive = '1d';
const esSearch = await enhancedEsSearchStrategyProvider(
mockConfig,
mockLegacyConfig$,
mockLogger
);
const esSearch = await enhancedEsSearchStrategyProvider(mockLegacyConfig$, mockLogger);
let err: KbnServerError | undefined;
try {

View file

@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { Observable } from 'rxjs';
@ -10,35 +11,31 @@ import type { IScopedClusterClient, Logger, SharedGlobalConfig } from 'kibana/se
import { catchError, first, tap } from 'rxjs/operators';
import { SearchResponse } from 'elasticsearch';
import { from } from 'rxjs';
import type { ISearchStrategy, SearchStrategyDependencies } from '../../types';
import type {
IAsyncSearchOptions,
IEsSearchRequest,
IEsSearchResponse,
ISearchOptions,
ISearchStrategy,
SearchStrategyDependencies,
SearchUsage,
} from '../../../../../src/plugins/data/server';
import {
getDefaultSearchParams,
getShardTimeout,
getTotalLoaded,
searchUsageObserver,
shimAbortSignal,
shimHitsTotal,
} from '../../../../../src/plugins/data/server';
import type { IAsyncSearchOptions } from '../../common';
import { pollSearch } from '../../common';
} from '../../../../common';
import { pollSearch } from '../../../../common';
import {
getDefaultAsyncGetParams,
getDefaultAsyncSubmitParams,
getIgnoreThrottled,
} from './request_utils';
import { toAsyncKibanaSearchResponse } from './response_utils';
import { ConfigSchema } from '../../config';
import { getKbnServerError, KbnServerError } from '../../../../../src/plugins/kibana_utils/server';
import { getKbnServerError, KbnServerError } from '../../../../../kibana_utils/server';
import { SearchUsage, searchUsageObserver } from '../../collectors';
import {
getDefaultSearchParams,
getShardTimeout,
getTotalLoaded,
shimAbortSignal,
shimHitsTotal,
} from '../es_search';
export const enhancedEsSearchStrategyProvider = (
config: ConfigSchema,
legacyConfig$: Observable<SharedGlobalConfig>,
logger: Logger,
usage?: SearchUsage
@ -54,7 +51,7 @@ export const enhancedEsSearchStrategyProvider = (
function asyncSearch(
{ id, ...request }: IEsSearchRequest,
options: IAsyncSearchOptions,
{ esClient, uiSettingsClient }: SearchStrategyDependencies
{ esClient, uiSettingsClient, searchSessionsClient }: SearchStrategyDependencies
) {
const client = esClient.asCurrentUser.asyncSearch;
@ -62,7 +59,11 @@ export const enhancedEsSearchStrategyProvider = (
const params = id
? getDefaultAsyncGetParams(options)
: {
...(await getDefaultAsyncSubmitParams(uiSettingsClient, config, options)),
...(await getDefaultAsyncSubmitParams(
uiSettingsClient,
searchSessionsClient.getConfig(),
options
)),
...request.params,
};
const promise = id ? client.get({ ...params, id }) : client.submit(params);

View file

@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { enhancedEsSearchStrategyProvider } from './ese_search_strategy';
export * from './types';

View file

@ -1,8 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { IUiSettingsClient } from 'kibana/server';
@ -11,9 +12,9 @@ import {
AsyncSearchSubmit,
Search,
} from '@elastic/elasticsearch/api/requestParams';
import { ISearchOptions, UI_SETTINGS } from '../../../../../src/plugins/data/common';
import { getDefaultSearchParams } from '../../../../../src/plugins/data/server';
import { ConfigSchema } from '../../config';
import { ISearchOptions, UI_SETTINGS } from '../../../../common';
import { getDefaultSearchParams } from '../es_search';
import { SearchSessionsConfigSchema } from '../../../../config';
/**
* @internal
@ -30,7 +31,7 @@ export async function getIgnoreThrottled(
*/
export async function getDefaultAsyncSubmitParams(
uiSettingsClient: IUiSettingsClient,
config: ConfigSchema,
searchSessionsConfig: SearchSessionsConfigSchema | null,
options: ISearchOptions
): Promise<
Pick<
@ -53,7 +54,11 @@ export async function getDefaultAsyncSubmitParams(
...(await getDefaultSearchParams(uiSettingsClient)),
...(options.sessionId
? {
keep_alive: `${config.search.sessions.defaultExpiration.asMilliseconds()}ms`,
// TODO: searchSessionsConfig could be "null" if we are running without x-pack which happens only in tests.
// This can be cleaned up when we completely stop separating basic and oss
keep_alive: searchSessionsConfig
? `${searchSessionsConfig.defaultExpiration.asMilliseconds()}ms`
: '1m',
}
: {}),
};

View file

@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { AsyncSearchResponse } from './types';
import { getTotalLoaded } from '../es_search';
/**
* Get the Kibana representation of an async search response (see `IKibanaSearchResponse`).
*/
export function toAsyncKibanaSearchResponse(response: AsyncSearchResponse) {
return {
id: response.id,
rawResponse: response.response,
isPartial: response.is_partial,
isRunning: response.is_running,
...getTotalLoaded(response.response),
};
}

View file

@ -1,12 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { estypes } from '@elastic/elasticsearch';
import { SearchResponse, ShardsResponse } from 'elasticsearch';
import { ShardsResponse } from 'elasticsearch';
export interface AsyncSearchResponse<T = unknown> {
id?: string;
@ -20,9 +21,3 @@ export interface AsyncSearchStatusResponse extends Omit<AsyncSearchResponse, 're
completion_status: number;
_shards: ShardsResponse;
}
export interface EqlSearchResponse<T = unknown> extends SearchResponse<T> {
id?: string;
is_partial: boolean;
is_running: boolean;
}

View file

@ -21,14 +21,14 @@ import {
IKibanaSearchRequest,
IKibanaSearchResponse,
ISearchClient,
IEsSearchResponse,
IEsSearchRequest,
} from '../../common/search';
import { AggsSetup, AggsStart } from './aggs';
import { SearchUsage } from './collectors';
import { IEsSearchRequest, IEsSearchResponse } from './es_search';
import { IScopedSearchSessionsClient, ISearchSessionService } from './session';
export interface SearchEnhancements {
defaultStrategy: string;
sessionService: ISearchSessionService;
}

View file

@ -68,6 +68,7 @@ import { SavedObjectsFindResponse } from 'kibana/server';
import { SavedObjectsUpdateResponse } from 'kibana/server';
import { Search } from '@elastic/elasticsearch/api/requestParams';
import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common';
import { ShardsResponse } from 'elasticsearch';
import { SharedGlobalConfig as SharedGlobalConfig_2 } from 'kibana/server';
import { ToastInputFields } from 'src/core/public/notifications';
import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport';
@ -281,6 +282,34 @@ export class AggParamType<TAggConfig extends IAggConfig = IAggConfig> extends Ba
makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig;
}
// Warning: (ae-missing-release-tag) "AsyncSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface AsyncSearchResponse<T = unknown> {
// (undocumented)
expiration_time_in_millis: number;
// (undocumented)
id?: string;
// (undocumented)
is_partial: boolean;
// (undocumented)
is_running: boolean;
// (undocumented)
response: estypes.SearchResponse<T>;
// (undocumented)
start_time_in_millis: number;
}
// Warning: (ae-missing-release-tag) "AsyncSearchStatusResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface AsyncSearchStatusResponse extends Omit<AsyncSearchResponse, 'response'> {
// (undocumented)
completion_status: number;
// (undocumented)
_shards: ShardsResponse;
}
// Warning: (ae-missing-release-tag) "BUCKET_TYPES" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -1355,6 +1384,7 @@ export class SearchSessionService implements ISearchSessionService {
extend: () => Promise<never>;
cancel: () => Promise<never>;
delete: () => Promise<never>;
getConfig: () => null;
};
}
@ -1512,18 +1542,18 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "HistogramFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:128:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:259:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:260:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" 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/plugin.ts:81: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:114:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts

View file

@ -116,7 +116,7 @@ export default function ({ getService }: FtrProviderContext) {
});
});
it('should return 400 when index type is provided in OSS', async () => {
it('should return 400 when index type is provided in "es" strategy', async () => {
const resp = await supertest.post(`/internal/bsearch`).send({
batch: [
{
@ -130,6 +130,9 @@ export default function ({ getService }: FtrProviderContext) {
},
},
},
options: {
strategy: 'es',
},
},
],
});
@ -151,11 +154,14 @@ export default function ({ getService }: FtrProviderContext) {
after(async () => {
await esArchiver.unload('../../../functional/fixtures/es_archiver/logstash_functional');
});
it('should return 400 for Painless error', async () => {
it('should return 400 "search_phase_execution_exception" for Painless error in "es" strategy', async () => {
const resp = await supertest.post(`/internal/bsearch`).send({
batch: [
{
request: painlessErrReq,
options: {
strategy: 'es',
},
},
],
});

View file

@ -1,25 +1,16 @@
# data_enhanced
The `data_enhanced` plugin is the x-pack counterpart to the OSS `data` plugin.
The `data_enhanced` plugin is the x-pack counterpart to the `src/plguins/data` plugin.
It exists to provide Elastic-licensed services, or parts of services, which
enhance existing OSS functionality from `data`.
It exists to provide services, or parts of services, which
enhance existing functionality from `src/plugins/data`.
Currently the `data_enhanced` plugin doesn't return any APIs which you can
Currently, the `data_enhanced` plugin doesn't return any APIs which you can
consume directly, however it is possible that you are indirectly relying on the
enhanced functionality that it provides via the OSS `data` plugin.
enhanced functionality that it provides via the `data` plugin from `src/`.
Here is the functionality it adds:
## KQL Autocomplete
The OSS autocomplete service provides suggestions for field names and values
based on suggestion providers which are registered to the service. This plugin
registers the autocomplete provider for KQL to the OSS service.
## Async, Rollup, and EQL Search Strategies
This plugin enhances the OSS search service with an ES search strategy that
uses async search (or rollups) behind the scenes. It also registers an EQL
search strategy.
## Search Sessions
Search sessions are handy when you want to enable a user to run something asynchronously (for example, a dashboard over a long period of time), and then quickly restore the results at a later time. The Search Service transparently fetches results from the .async-search index, instead of running each request again.

View file

@ -4,17 +4,3 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export {
SEARCH_SESSION_TYPE,
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
EqlRequestParams,
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
IAsyncSearchOptions,
pollSearch,
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
SearchSessionRequestInfo,
} from './search';

View file

@ -4,7 +4,3 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export * from './types';
export * from './poll_search';
export * from './session';

View file

@ -1,16 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// TODO https://github.com/elastic/kibana/issues/92802
export {
SEARCH_SESSION_TYPE,
SearchSessionSavedObjectAttributes,
SearchSessionFindOptions,
SearchSessionRequestInfo,
SearchSessionStatus,
SEARCH_SESSIONS_TABLE_ID,
} from '../../../../../../src/plugins/data/common/';

View file

@ -1,14 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export enum SearchSessionStatus {
IN_PROGRESS = 'in_progress',
ERROR = 'error',
COMPLETE = 'complete',
CANCELLED = 'cancelled',
EXPIRED = 'expired',
}

View file

@ -1,93 +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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { SearchSessionStatus } from './';
export const SEARCH_SESSION_TYPE = 'search-session';
export interface SearchSessionSavedObjectAttributes {
sessionId: string;
/**
* User-facing session name to be displayed in session management
*/
name?: string;
/**
* App that created the session. e.g 'discover'
*/
appId?: string;
/**
* Creation time of the session
*/
created: string;
/**
* Last touch time of the session
*/
touched: string;
/**
* Expiration time of the session. Expiration itself is managed by Elasticsearch.
*/
expires: string;
/**
* status
*/
status: SearchSessionStatus;
/**
* urlGeneratorId
*/
urlGeneratorId?: string;
/**
* The application state that was used to create the session.
* Should be used, for example, to re-load an expired search session.
*/
initialState?: Record<string, unknown>;
/**
* Application state that should be used to restore the session.
* For example, relative dates are conveted to absolute ones.
*/
restoreState?: Record<string, unknown>;
/**
* Mapping of search request hashes to their corresponsing info (async search id, etc.)
*/
idMapping: Record<string, SearchSessionRequestInfo>;
/**
* This value is true if the session was actively stored by the user. If it is false, the session may be purged by the system.
*/
persisted: boolean;
/**
* The realm type/name & username uniquely identifies the user who created this search session
*/
realmType?: string;
realmName?: string;
username?: string;
}
export interface SearchSessionRequestInfo {
/**
* ID of the async search request
*/
id: string;
/**
* Search strategy used to submit the search request
*/
strategy: string;
/**
* status
*/
status: string;
/**
* An optional error. Set if status is set to error.
*/
error?: string;
}
export interface SearchSessionFindOptions {
page?: number;
perPage?: number;
sortField?: string;
sortOrder?: string;
filter?: string;
}

View file

@ -6,64 +6,11 @@
*/
import { schema, TypeOf } from '@kbn/config-schema';
import { searchSessionsConfigSchema } from '../../../src/plugins/data/config';
export const configSchema = schema.object({
search: schema.object({
sessions: schema.object({
/**
* Turns the feature on \ off (incl. removing indicator and management screens)
*/
enabled: schema.boolean({ defaultValue: true }),
/**
* pageSize controls how many search session objects we load at once while monitoring
* session completion
*/
pageSize: schema.number({ defaultValue: 100 }),
/**
* trackingInterval controls how often we track search session objects progress
*/
trackingInterval: schema.duration({ defaultValue: '10s' }),
/**
* monitoringTaskTimeout controls for how long task manager waits for search session monitoring task to complete before considering it timed out,
* If tasks timeouts it receives cancel signal and next task starts in "trackingInterval" time
*/
monitoringTaskTimeout: schema.duration({ defaultValue: '5m' }),
/**
* notTouchedTimeout controls how long do we store unpersisted search session results,
* after the last search in the session has completed
*/
notTouchedTimeout: schema.duration({ defaultValue: '5m' }),
/**
* notTouchedInProgressTimeout controls how long do allow a search session to run after
* a user has navigated away without persisting
*/
notTouchedInProgressTimeout: schema.duration({ defaultValue: '1m' }),
/**
* maxUpdateRetries controls how many retries we perform while attempting to save a search session
*/
maxUpdateRetries: schema.number({ defaultValue: 3 }),
/**
* defaultExpiration controls how long search sessions are valid for, until they are expired.
*/
defaultExpiration: schema.duration({ defaultValue: '7d' }),
management: schema.object({
/**
* maxSessions controls how many saved search sessions we display per page on the management screen.
*/
maxSessions: schema.number({ defaultValue: 10000 }),
/**
* refreshInterval controls how often we refresh the management screen.
*/
refreshInterval: schema.duration({ defaultValue: '10s' }),
/**
* refreshTimeout controls how often we refresh the management screen.
*/
refreshTimeout: schema.duration({ defaultValue: '1m' }),
expiresSoonWarning: schema.duration({ defaultValue: '1d' }),
}),
}),
sessions: searchSessionsConfigSchema,
}),
});

View file

@ -14,4 +14,7 @@ export const plugin = (initializerContext: PluginInitializerContext<ConfigSchema
export { DataEnhancedSetup, DataEnhancedStart };
export { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '../common';
export {
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
} from '../../../../src/plugins/data/common';

View file

@ -27,10 +27,14 @@ import {
IKibanaSearchRequest,
SearchSessionState,
} from '../../../../../src/plugins/data/public';
import { AbortError } from '../../../../../src/plugins/kibana_utils/public';
import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, pollSearch } from '../../common';
import { SearchResponseCache } from './search_response_cache';
import {
ENHANCED_ES_SEARCH_STRATEGY,
IAsyncSearchOptions,
pollSearch,
} from '../../../../../src/plugins/data/common';
import { createRequestHash } from './utils';
import { SearchResponseCache } from './search_response_cache';
import { AbortError } from '../../../../../src/plugins/kibana_utils/public';
import { SearchAbortController } from './search_abort_controller';
const MAX_CACHE_ITEMS = 50;

View file

@ -8,7 +8,7 @@
import { EuiTextProps, EuiToolTipProps } from '@elastic/eui';
import { mount } from 'enzyme';
import React from 'react';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common/';
import { UISession } from '../types';
import { LocaleWrapper } from '../__mocks__';
import { getStatusText, StatusIndicator } from './status';

View file

@ -8,7 +8,7 @@
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { ReactElement } from 'react';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common';
import { dateString } from '../lib/date_string';
import { UISession } from '../types';
import { StatusDef as StatusAttributes, TableText } from './';

View file

@ -13,7 +13,7 @@ import moment from 'moment';
import React from 'react';
import { coreMock } from 'src/core/public/mocks';
import { SessionsClient } from 'src/plugins/data/public/search';
import { SearchSessionStatus } from '../../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../../src/plugins/data/common';
import { IManagementSectionsPluginsSetup, SessionsConfigSchema } from '../../';
import { SearchSessionsMgmtAPI } from '../../lib/api';
import { LocaleWrapper, mockUrls } from '../../__mocks__';

View file

@ -14,7 +14,7 @@ import useDebounce from 'react-use/lib/useDebounce';
import useInterval from 'react-use/lib/useInterval';
import { TableText } from '../';
import { IManagementSectionsPluginsSetup, SessionsConfigSchema } from '../..';
import { SEARCH_SESSIONS_TABLE_ID } from '../../../../../common/search';
import { SEARCH_SESSIONS_TABLE_ID } from '../../../../../../../../src/plugins/data/common/';
import { SearchSessionsMgmtAPI } from '../../lib/api';
import { getColumns } from '../../lib/get_columns';
import { UISession } from '../../types';

View file

@ -13,7 +13,7 @@ import { coreMock } from 'src/core/public/mocks';
import type { SavedObjectsFindResponse } from 'src/core/server';
import { SessionsClient } from 'src/plugins/data/public/search';
import type { SessionsConfigSchema } from '../';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common';
import { mockUrls } from '../__mocks__';
import { SearchSessionsMgmtAPI } from './api';

View file

@ -15,7 +15,7 @@ import {
ISessionsClient,
SearchUsageCollector,
} from '../../../../../../../src/plugins/data/public';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common';
import { ACTION } from '../components/actions';
import { PersistedSearchSessionSavedObjectAttributes, UISession } from '../types';
import { SessionsConfigSchema } from '..';

View file

@ -14,7 +14,7 @@ import { ReactElement } from 'react';
import { coreMock } from 'src/core/public/mocks';
import { SessionsClient } from 'src/plugins/data/public/search';
import { IManagementSectionsPluginsSetup, SessionsConfigSchema } from '../';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common';
import { OnActionComplete } from '../components';
import { UISession } from '../types';
import { mockUrls } from '../__mocks__';

View file

@ -22,7 +22,7 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public';
import { IManagementSectionsPluginsSetup, SessionsConfigSchema } from '../';
import { SearchSessionStatus } from '../../../../common/search';
import { SearchSessionStatus } from '../../../../../../../src/plugins/data/common';
import { TableText } from '../components';
import { OnActionComplete, PopoverActionsMenu } from '../components';
import { StatusIndicator } from '../components/status';

View file

@ -5,7 +5,10 @@
* 2.0.
*/
import { SearchSessionSavedObjectAttributes, SearchSessionStatus } from '../../../common';
import {
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
} from '../../../../../../src/plugins/data/common';
import { ACTION } from './components/actions';
export const DATE_STRING_FORMAT = 'D MMM, YYYY, HH:mm:ss';

View file

@ -10,7 +10,7 @@ import { first } from 'rxjs/operators';
import { SearchResponse } from 'elasticsearch';
import { SharedGlobalConfig, Logger } from 'kibana/server';
import { CollectorFetchContext } from '../../../../../src/plugins/usage_collection/server';
import { SEARCH_SESSION_TYPE } from '../../common';
import { SEARCH_SESSION_TYPE } from '../../../../../src/plugins/data/common';
import { ReportedUsage } from './register';
interface SessionPersistedTermsBucket {

View file

@ -20,6 +20,9 @@ export function plugin(initializerContext: PluginInitializerContext<ConfigSchema
return new EnhancedDataServerPlugin(initializerContext);
}
export { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '../common';
export {
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
} from '../../../../src/plugins/data/common';
export { EnhancedDataServerPlugin as Plugin };

View file

@ -6,15 +6,8 @@
*/
import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'kibana/server';
import { usageProvider } from '../../../../src/plugins/data/server';
import { ENHANCED_ES_SEARCH_STRATEGY, EQL_SEARCH_STRATEGY } from '../common';
import { registerSessionRoutes } from './routes';
import { searchSessionSavedObjectType } from './saved_objects';
import {
SearchSessionService,
enhancedEsSearchStrategyProvider,
eqlSearchStrategyProvider,
} from './search';
import { getUiSettings } from './ui_settings';
import type {
DataEnhancedRequestHandlerContext,
@ -23,6 +16,7 @@ import type {
} from './type';
import { ConfigSchema } from '../config';
import { registerUsageCollector } from './collectors';
import { SearchSessionService } from './search';
export class EnhancedDataServerPlugin
implements Plugin<void, void, SetupDependencies, StartDependencies> {
@ -36,31 +30,13 @@ export class EnhancedDataServerPlugin
}
public setup(core: CoreSetup<StartDependencies>, deps: SetupDependencies) {
const usage = deps.usageCollection ? usageProvider(core) : undefined;
core.uiSettings.register(getUiSettings());
core.savedObjects.registerType(searchSessionSavedObjectType);
deps.data.search.registerSearchStrategy(
ENHANCED_ES_SEARCH_STRATEGY,
enhancedEsSearchStrategyProvider(
this.config,
this.initializerContext.config.legacy.globalConfig$,
this.logger,
usage
)
);
deps.data.search.registerSearchStrategy(
EQL_SEARCH_STRATEGY,
eqlSearchStrategyProvider(this.logger)
);
this.sessionService = new SearchSessionService(this.logger, this.config, deps.security);
deps.data.__enhance({
search: {
defaultStrategy: ENHANCED_ES_SEARCH_STRATEGY,
sessionService: this.sessionService,
},
});

View file

@ -6,7 +6,7 @@
*/
import { SavedObjectsType } from 'kibana/server';
import { SEARCH_SESSION_TYPE } from '../../common';
import { SEARCH_SESSION_TYPE } from '../../../../../src/plugins/data/common';
import { searchSessionSavedObjectMigrations } from './search_session_migration';
export const searchSessionSavedObjectType: SavedObjectsType = {

View file

@ -10,8 +10,7 @@ import {
SearchSessionSavedObjectAttributesPre$7$13$0,
} from './search_session_migration';
import { SavedObject } from '../../../../../src/core/types';
import { SEARCH_SESSION_TYPE } from '../../../../../src/plugins/data/common';
import { SearchSessionStatus } from '../../common/search/session/status';
import { SEARCH_SESSION_TYPE, SearchSessionStatus } from '../../../../../src/plugins/data/common';
import { SavedObjectMigrationContext } from 'kibana/server';
const mockCompletedSessionSavedObject: SavedObject<SearchSessionSavedObjectAttributesPre$7$13$0> = {

View file

@ -9,7 +9,7 @@ import { SavedObjectMigrationMap, SavedObjectUnsanitizedDoc } from 'kibana/serve
import {
SearchSessionSavedObjectAttributes as SearchSessionSavedObjectAttributesLatest,
SearchSessionStatus,
} from '../../common';
} from '../../../../../src/plugins/data/common';
/**
* Search sessions were released in 7.12.0

View file

@ -5,6 +5,4 @@
* 2.0.
*/
export { enhancedEsSearchStrategyProvider } from './es_search_strategy';
export { eqlSearchStrategyProvider } from './eql_search_strategy';
export * from './session';

View file

@ -14,7 +14,7 @@ import {
SearchSessionSavedObjectAttributes,
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
} from '../../../common';
} from '../../../../../../src/plugins/data/common';
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
import { SearchSessionsConfig, SearchStatus } from './types';
import moment from 'moment';

View file

@ -15,14 +15,14 @@ import {
import moment from 'moment';
import { EMPTY, from, Observable } from 'rxjs';
import { catchError, concatMap } from 'rxjs/operators';
import { nodeBuilder } from '../../../../../../src/plugins/data/common';
import {
nodeBuilder,
ENHANCED_ES_SEARCH_STRATEGY,
SEARCH_SESSION_TYPE,
SearchSessionRequestInfo,
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
} from '../../../common';
} from '../../../../../../src/plugins/data/common';
import { getSearchStatus } from './get_search_status';
import { getSessionStatus } from './get_session_status';
import { SearchSessionsConfig, SearchStatus } from './types';

View file

@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n';
import { ApiResponse } from '@elastic/elasticsearch';
import { ElasticsearchClient } from 'src/core/server';
import { SearchStatus } from './types';
import { AsyncSearchStatusResponse } from '../types';
import { SearchSessionRequestInfo } from '../../../common';
import { SearchSessionRequestInfo } from '../../../../../../src/plugins/data/common';
import { AsyncSearchStatusResponse } from '../../../../../../src/plugins/data/server';
export async function getSearchStatus(
client: ElasticsearchClient,

View file

@ -7,7 +7,7 @@
import { SearchStatus } from './types';
import { getSessionStatus } from './get_session_status';
import { SearchSessionStatus } from '../../../common';
import { SearchSessionStatus } from '../../../../../../src/plugins/data/common';
describe('getSessionStatus', () => {
test("returns an in_progress status if there's nothing inside the session", () => {

View file

@ -5,7 +5,10 @@
* 2.0.
*/
import { SearchSessionSavedObjectAttributes, SearchSessionStatus } from '../../../common';
import {
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
} from '../../../../../../src/plugins/data/common/';
import { SearchStatus } from './types';
export function getSessionStatus(session: SearchSessionSavedObjectAttributes): SearchSessionStatus {

View file

@ -17,7 +17,7 @@ import {
import { checkRunningSessions } from './check_running_sessions';
import { CoreSetup, SavedObjectsClient, Logger } from '../../../../../../src/core/server';
import { ConfigSchema } from '../../../config';
import { SEARCH_SESSION_TYPE } from '../../../common';
import { SEARCH_SESSION_TYPE } from '../../../../../../src/plugins/data/common';
import { DataEnhancedStartDependencies } from '../../type';
export const SEARCH_SESSIONS_TASK_TYPE = 'search_sessions_monitor';

View file

@ -11,7 +11,6 @@ import {
SavedObjectsErrorHelpers,
} from '../../../../../../src/core/server';
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
import { SearchSessionStatus, SEARCH_SESSION_TYPE } from '../../../common';
import { SearchSessionService } from './session_service';
import { createRequestHash } from './utils';
import moment from 'moment';
@ -19,7 +18,11 @@ import { coreMock } from '../../../../../../src/core/server/mocks';
import { ConfigSchema } from '../../../config';
import { taskManagerMock } from '../../../../task_manager/server/mocks';
import { AuthenticatedUser } from '../../../../security/common/model';
import { nodeBuilder } from '../../../../../../src/plugins/data/common';
import {
nodeBuilder,
SEARCH_SESSION_TYPE,
SearchSessionStatus,
} from '../../../../../../src/plugins/data/common';
import { TaskManagerStartContract } from '../../../../task_manager/server';
const MAX_UPDATE_RETRIES = 3;

View file

@ -21,6 +21,8 @@ import {
IKibanaSearchRequest,
ISearchOptions,
nodeBuilder,
ENHANCED_ES_SEARCH_STRATEGY,
SEARCH_SESSION_TYPE,
} from '../../../../../../src/plugins/data/common';
import { esKuery, ISearchSessionService } from '../../../../../../src/plugins/data/server';
import { AuthenticatedUser, SecurityPluginSetup } from '../../../../security/server';
@ -29,12 +31,10 @@ import {
TaskManagerStartContract,
} from '../../../../task_manager/server';
import {
ENHANCED_ES_SEARCH_STRATEGY,
SearchSessionRequestInfo,
SearchSessionSavedObjectAttributes,
SearchSessionStatus,
SEARCH_SESSION_TYPE,
} from '../../../common';
} from '../../../../../../src/plugins/data/common';
import { createRequestHash } from './utils';
import { ConfigSchema } from '../../../config';
import {
@ -461,6 +461,7 @@ export class SearchSessionService
extend: this.extend.bind(this, deps, user),
cancel: this.cancel.bind(this, deps, user),
delete: this.delete.bind(this, deps, user),
getConfig: () => this.config.search.sessions,
};
};
};

View file

@ -11,7 +11,7 @@ import {
PluginStart,
shimHitsTotal,
} from '../../../../../../src/plugins/data/server';
import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../data_enhanced/common';
import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../../../src/plugins/data/common';
import {
FactoryQueryTypes,
StrategyResponseType,

View file

@ -9,7 +9,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui';
import {
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
} from '../../../../../../data_enhanced/common';
} from '../../../../../../../../src/plugins/data/common';
import { Inspect, Maybe, PaginationInputPaginated } from '../../..';
import { TimelineEdges, TimelineEventsAllRequestOptions } from '../..';
import { EqlSearchResponse } from '../../../../detection_engine/types';

View file

@ -9,8 +9,8 @@ import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/pub
import {
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
} from '../../../../../data_enhanced/common';
import { EQL_SEARCH_STRATEGY } from '../../../../../data_enhanced/public';
EQL_SEARCH_STRATEGY,
} from '../../../../../../../src/plugins/data/common';
import {
getValidationErrors,
isErrorResponse,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { EqlSearchStrategyResponse } from '../../../../../data_enhanced/common';
import { EqlSearchStrategyResponse } from '../../../../../../../src/plugins/data/common';
import { Source } from './types';
import { EqlSearchResponse } from '../../../../common/detection_engine/types';
import { Connection } from '@elastic/elasticsearch';

View file

@ -7,7 +7,7 @@
import moment from 'moment';
import { EqlSearchStrategyResponse } from '../../../../../data_enhanced/common';
import { EqlSearchStrategyResponse } from '../../../../../../../src/plugins/data/common';
import { Source } from './types';
import { EqlSearchResponse } from '../../../../common/detection_engine/types';
import { inputsModel } from '../../../common/store';

View file

@ -9,7 +9,7 @@ import moment from 'moment';
import { Unit } from '@elastic/datemath';
import { inputsModel } from '../../../common/store';
import { EqlSearchStrategyResponse } from '../../../../../data_enhanced/common';
import { EqlSearchStrategyResponse } from '../../../../../../../src/plugins/data/common';
import { InspectResponse } from '../../../types';
import { EqlPreviewResponse, Source } from './types';
import { BaseHit, EqlSearchResponse } from '../../../../common/detection_engine/types';

View file

@ -11,7 +11,7 @@ import { of, throwError } from 'rxjs';
import { delay } from 'rxjs/operators';
import * as i18n from '../translations';
import { EqlSearchStrategyResponse } from '../../../../../data_enhanced/common';
import { EqlSearchStrategyResponse } from '../../../../../../../src/plugins/data/common';
import { Source } from './types';
import { EqlSearchResponse } from '../../../../common/detection_engine/types';
import { useKibana } from '../../../common/lib/kibana';

View file

@ -16,12 +16,10 @@ import {
isCompleteResponse,
isErrorResponse,
isPartialResponse,
} from '../../../../../../../src/plugins/data/common';
import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common';
import {
EqlSearchStrategyRequest,
EqlSearchStrategyResponse,
} from '../../../../../data_enhanced/common';
} from '../../../../../../../src/plugins/data/common';
import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common';
import { formatInspect, getEqlAggsData } from './helpers';
import { EqlPreviewResponse, EqlPreviewRequest, Source } from './types';
import { hasEqlSequenceQuery } from '../../../../common/detection_engine/utils';

View file

@ -11,7 +11,7 @@ import {
PluginStart,
shimHitsTotal,
} from '../../../../../../src/plugins/data/server';
import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../data_enhanced/common';
import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../../../src/plugins/data/common';
import {
FactoryQueryTypes,
StrategyResponseType,

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { EqlSearchStrategyResponse } from '../../../../../../data_enhanced/common';
import { EqlSearchStrategyResponse } from '../../../../../../../../src/plugins/data/common';
import { EqlSearchResponse } from '../../../../../common/detection_engine/types';
export const sequenceResponse = ({

Some files were not shown because too many files have changed in this diff Show more