[Search] Server strategy example (#71679)

* Server strategy example

* Add tsconfig
Renamed is_partial to isPartial
Added isPartial and isRunning to OSS response type

* Docs + remove unused sample code

* Fix test naming of arguments

* ts

* ts fix

* Add filters and query input selector

* Update examples/search_examples/public/components/app.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Use new service

* exapmle plugin ts

* unsubscribe + use timefilter

* typo

* docs

* Add comments and use agg config

* Added agg configs
Added field selector
Added a custom input param

* Adding getEsQuery to query service (??)

* Add server side example

* docs

* caps

* list plugin in examples page

* fix typo

* Update examples/search_examples/public/application.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Update examples/search_examples/public/application.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Update examples/search_examples/public/components/app.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Update examples/search_examples/public/components/app.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Update examples/search_examples/public/components/app.tsx

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* eslint

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Liza Katz 2020-08-11 23:28:43 +03:00 committed by GitHub
parent d78644229e
commit 506e9537bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1161 additions and 106 deletions

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) &gt; [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md)
## IEsSearchResponse.isPartial property
Indicates whether the results returned are complete or partial
<b>Signature:</b>
```typescript
isPartial?: boolean;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) &gt; [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md)
## IEsSearchResponse.isRunning property
Indicates whether async search is still in flight
<b>Signature:</b>
```typescript
isRunning?: boolean;
```

View file

@ -14,5 +14,7 @@ export interface IEsSearchResponse extends IKibanaSearchResponse
| Property | Type | Description |
| --- | --- | --- |
| [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md) | <code>boolean</code> | Indicates whether the results returned are complete or partial |
| [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md) | <code>boolean</code> | Indicates whether async search is still in flight |
| [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) | <code>SearchResponse&lt;any&gt;</code> | |

View file

@ -7,5 +7,5 @@
<b>Signature:</b>
```typescript
export declare type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOptions) => Observable<IEsSearchResponse>;
export declare type ISearchGeneric = (request: IEsSearchRequest, options?: ISearchOptions) => Observable<IEsSearchResponse>;
```

View file

@ -15,4 +15,5 @@ export interface ISearchOptions
| Property | Type | Description |
| --- | --- | --- |
| [signal](./kibana-plugin-plugins-data-public.isearchoptions.signal.md) | <code>AbortSignal</code> | |
| [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | <code>string</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-public](./kibana-plugin-plugins-data-public.md) &gt; [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) &gt; [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md)
## ISearchOptions.strategy property
<b>Signature:</b>
```typescript
strategy?: string;
```

View file

@ -35,7 +35,7 @@ export declare class SearchInterceptor
| Method | Modifiers | Description |
| --- | --- | --- |
| [runSearch(request, signal)](./kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md) | | |
| [runSearch(request, signal, strategy)](./kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md) | | |
| [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given <code>search</code> method. Overrides the <code>AbortSignal</code> with one that will abort either when <code>cancelPending</code> is called, when the request times out, or when the original <code>AbortSignal</code> is aborted. Updates the <code>pendingCount</code> when the request is started/finalized. |
| [setupTimers(options)](./kibana-plugin-plugins-data-public.searchinterceptor.setuptimers.md) | | |

View file

@ -7,7 +7,7 @@
<b>Signature:</b>
```typescript
protected runSearch(request: IEsSearchRequest, signal: AbortSignal): Observable<IEsSearchResponse>;
protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable<IEsSearchResponse>;
```
## Parameters
@ -16,6 +16,7 @@ protected runSearch(request: IEsSearchRequest, signal: AbortSignal): Observable<
| --- | --- | --- |
| request | <code>IEsSearchRequest</code> | |
| signal | <code>AbortSignal</code> | |
| strategy | <code>string</code> | |
<b>Returns:</b>

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

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; [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md)
## IEsSearchRequest interface
<b>Signature:</b>
```typescript
export interface IEsSearchRequest extends IKibanaSearchRequest
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [indexType](./kibana-plugin-plugins-data-server.iessearchrequest.indextype.md) | <code>string</code> | |
| [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md) | <code>ISearchRequestParams</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; [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) &gt; [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md)
## IEsSearchRequest.params property
<b>Signature:</b>
```typescript
params?: ISearchRequestParams;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) &gt; [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md)
## IEsSearchResponse.isPartial property
Indicates whether the results returned are complete or partial
<b>Signature:</b>
```typescript
isPartial?: boolean;
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) &gt; [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) &gt; [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md)
## IEsSearchResponse.isRunning property
Indicates whether async search is still in flight
<b>Signature:</b>
```typescript
isRunning?: boolean;
```

View file

@ -0,0 +1,20 @@
<!-- 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; [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md)
## IEsSearchResponse interface
<b>Signature:</b>
```typescript
export interface IEsSearchResponse extends IKibanaSearchResponse
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md) | <code>boolean</code> | Indicates whether the results returned are complete or partial |
| [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md) | <code>boolean</code> | Indicates whether async search is still in flight |
| [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md) | <code>SearchResponse&lt;any&gt;</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; [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) &gt; [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md)
## IEsSearchResponse.rawResponse property
<b>Signature:</b>
```typescript
rawResponse: SearchResponse<any>;
```

View file

@ -36,6 +36,8 @@
| [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | |
| [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | |
| [Filter](./kibana-plugin-plugins-data-server.filter.md) | |
| [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | |
| [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) | |
| [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | |
| [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | |
| [IIndexPattern](./kibana-plugin-plugins-data-server.iindexpattern.md) | |

View file

@ -0,0 +1,9 @@
# search_examples
> An awesome Kibana plugin
---
## Development
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.

View file

@ -0,0 +1,32 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { IEsSearchResponse, IEsSearchRequest } from '../../../src/plugins/data/common';
export const PLUGIN_ID = 'searchExamples';
export const PLUGIN_NAME = 'Search Examples';
export interface IMyStrategyRequest extends IEsSearchRequest {
get_cool: boolean;
}
export interface IMyStrategyResponse extends IEsSearchResponse {
cool: string;
}
export const SERVER_SEARCH_ROUTE_PATH = '/api/examples/search';

View file

@ -0,0 +1,9 @@
{
"id": "searchExamples",
"version": "8.0.0",
"server": true,
"ui": true,
"requiredPlugins": ["navigation", "data", "developerExamples"],
"optionalPlugins": [],
"requiredBundles": []
}

View file

@ -0,0 +1,44 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { AppPluginStartDependencies } from './types';
import { SearchExamplesApp } from './components/app';
export const renderApp = (
{ notifications, savedObjects, http }: CoreStart,
{ navigation, data }: AppPluginStartDependencies,
{ appBasePath, element }: AppMountParameters
) => {
ReactDOM.render(
<SearchExamplesApp
basename={appBasePath}
notifications={notifications}
savedObjectsClient={savedObjects.client}
navigation={navigation}
data={data}
http={http}
/>,
element
);
return () => ReactDOM.unmountComponentAtNode(element);
};

View file

@ -0,0 +1,341 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React, { useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, I18nProvider } from '@kbn/i18n/react';
import { BrowserRouter as Router } from 'react-router-dom';
import {
EuiButton,
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiPageHeader,
EuiTitle,
EuiText,
EuiFlexGrid,
EuiFlexItem,
EuiCheckbox,
EuiSpacer,
EuiCode,
EuiComboBox,
EuiFormLabel,
} from '@elastic/eui';
import { CoreStart } from '../../../../src/core/public';
import { mountReactNode } from '../../../../src/core/public/utils';
import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public';
import {
PLUGIN_ID,
PLUGIN_NAME,
IMyStrategyRequest,
IMyStrategyResponse,
SERVER_SEARCH_ROUTE_PATH,
} from '../../common';
import {
DataPublicPluginStart,
IndexPatternSelect,
IndexPattern,
IndexPatternField,
} from '../../../../src/plugins/data/public';
interface SearchExamplesAppDeps {
basename: string;
notifications: CoreStart['notifications'];
http: CoreStart['http'];
savedObjectsClient: CoreStart['savedObjects']['client'];
navigation: NavigationPublicPluginStart;
data: DataPublicPluginStart;
}
function formatFieldToComboBox(field?: IndexPatternField | null) {
if (!field) return [];
return formatFieldsToComboBox([field]);
}
function formatFieldsToComboBox(fields?: IndexPatternField[]) {
if (!fields) return [];
return fields?.map((field) => {
return {
label: field.displayName || field.name,
};
});
}
export const SearchExamplesApp = ({
http,
basename,
notifications,
savedObjectsClient,
navigation,
data,
}: SearchExamplesAppDeps) => {
const [getCool, setGetCool] = useState<boolean>(false);
const [timeTook, setTimeTook] = useState<number | undefined>();
const [indexPattern, setIndexPattern] = useState<IndexPattern | null>();
const [numericFields, setNumericFields] = useState<IndexPatternField[]>();
const [selectedField, setSelectedField] = useState<IndexPatternField | null | undefined>();
// Fetch the default index pattern using the `data.indexPatterns` service, as the component is mounted.
useEffect(() => {
const setDefaultIndexPattern = async () => {
const defaultIndexPattern = await data.indexPatterns.getDefault();
setIndexPattern(defaultIndexPattern);
};
setDefaultIndexPattern();
}, [data]);
// Update the fields list every time the index pattern is modified.
useEffect(() => {
const fields = indexPattern?.fields.filter(
(field) => field.type === 'number' && field.aggregatable
);
setNumericFields(fields);
setSelectedField(fields?.length ? fields[0] : null);
}, [indexPattern]);
const doAsyncSearch = async (strategy?: string) => {
if (!indexPattern || !selectedField) return;
// Constuct the query portion of the search request
const query = data.query.getEsQuery(indexPattern);
// Constuct the aggregations portion of the search request by using the `data.search.aggs` service.
const aggs = [{ type: 'avg', params: { field: selectedField.name } }];
const aggsDsl = data.search.aggs.createAggConfigs(indexPattern, aggs).toDsl();
const request = {
params: {
index: indexPattern.title,
body: {
aggs: aggsDsl,
query,
},
},
};
if (strategy) {
// Add a custom request parameter to be consumed by `MyStrategy`.
(request as IMyStrategyRequest).get_cool = getCool;
}
// Submit the search request using the `data.search` service.
const searchSubscription$ = data.search
.search(request, {
strategy,
})
.subscribe({
next: (response) => {
if (!response.isPartial && !response.isRunning) {
setTimeTook(response.rawResponse.took);
const avgResult: number | undefined = response.rawResponse.aggregations
? response.rawResponse.aggregations[1].value
: undefined;
const message = (
<EuiText>
Searched {response.rawResponse.hits.total} documents. <br />
The average of {selectedField.name} is {avgResult ? Math.floor(avgResult) : 0}.
<br />
Is it Cool? {String((response as IMyStrategyResponse).cool)}
</EuiText>
);
notifications.toasts.addSuccess({
title: 'Query result',
text: mountReactNode(message),
});
searchSubscription$.unsubscribe();
} else if (response.isPartial && !response.isRunning) {
// TODO: Make response error status clearer
notifications.toasts.addWarning('An error has occurred');
searchSubscription$.unsubscribe();
}
},
error: () => {
notifications.toasts.addDanger('Failed to run search');
},
});
};
const onClickHandler = () => {
doAsyncSearch();
};
const onMyStrategyClickHandler = () => {
doAsyncSearch('myStrategy');
};
const onServerClickHandler = async () => {
if (!indexPattern || !selectedField) return;
try {
const response = await http.get(SERVER_SEARCH_ROUTE_PATH, {
query: {
index: indexPattern.title,
field: selectedField.name,
},
});
notifications.toasts.addSuccess(`Server returned ${JSON.stringify(response)}`);
} catch (e) {
notifications.toasts.addDanger('Failed to run search');
}
};
if (!indexPattern) return null;
return (
<Router basename={basename}>
<I18nProvider>
<>
<navigation.ui.TopNavMenu
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
indexPatterns={indexPattern ? [indexPattern] : undefined}
/>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="searchExamples.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
<EuiText>
<EuiFlexGrid columns={1}>
<EuiFlexItem>
<EuiFormLabel>Index Pattern</EuiFormLabel>
<IndexPatternSelect
savedObjectsClient={savedObjectsClient}
placeholder={i18n.translate(
'backgroundSessionExample.selectIndexPatternPlaceholder',
{
defaultMessage: 'Select index pattern',
}
)}
indexPatternId={indexPattern?.id || ''}
onChange={async (newIndexPatternId: any) => {
const newIndexPattern = await data.indexPatterns.get(newIndexPatternId);
setIndexPattern(newIndexPattern);
}}
isClearable={false}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormLabel>Numeric Fields</EuiFormLabel>
<EuiComboBox
options={formatFieldsToComboBox(numericFields)}
selectedOptions={formatFieldToComboBox(selectedField)}
singleSelection={true}
onChange={(option) => {
const field = indexPattern.getFieldByName(option[0].label);
setSelectedField(field || null);
}}
sortMatchesBy="startsWith"
/>
</EuiFlexItem>
</EuiFlexGrid>
</EuiText>
<EuiText>
<FormattedMessage
id="searchExamples.timestampText"
defaultMessage="Last query took: {time} ms"
values={{ time: timeTook || 'Unknown' }}
/>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>
Searching Elasticsearch using <EuiCode>data.search</EuiCode>
</h3>
</EuiTitle>
<EuiText>
If you want to fetch data from Elasticsearch, you can use the different services
provided by the <EuiCode>data</EuiCode> plugin. These help you get the index
pattern and search bar configuration, format them into a DSL query and send it
to Elasticsearch.
<EuiSpacer />
<EuiButton type="primary" size="s" onClick={onClickHandler}>
<FormattedMessage id="searchExamples.buttonText" defaultMessage="Get data" />
</EuiButton>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Writing a custom search strategy</h3>
</EuiTitle>
<EuiText>
If you want to do some pre or post processing on the server, you might want to
create a custom search strategy. This example uses such a strategy, passing in
custom input and receiving custom output back.
<EuiSpacer />
<EuiCheckbox
id="GetCool"
label={
<FormattedMessage
id="searchExamples.getCoolCheckbox"
defaultMessage="Get cool parameter?"
/>
}
checked={getCool}
onChange={(event) => setGetCool(event.target.checked)}
/>
<EuiButton type="primary" size="s" onClick={onMyStrategyClickHandler}>
<FormattedMessage
id="searchExamples.myStrategyButtonText"
defaultMessage="Get data via My Strategy"
/>
</EuiButton>
</EuiText>
<EuiSpacer />
<EuiTitle size="s">
<h3>Using search on the server</h3>
</EuiTitle>
<EuiText>
You can also run your search request from the server, without registering a
search strategy. This request does not take the configuration of{' '}
<EuiCode>TopNavMenu</EuiCode> into account, but you could pass those down to the
server as well.
<EuiButton type="primary" size="s" onClick={onServerClickHandler}>
<FormattedMessage
id="searchExamples.myServerButtonText"
defaultMessage="Get data on the server"
/>
</EuiButton>
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</Router>
);
};

View file

@ -0,0 +1,29 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import './index.scss';
import { SearchExamplesPlugin } from './plugin';
// This exports static code and TypeScript types,
// as well as, Kibana Platform `plugin()` initializer.
export function plugin() {
return new SearchExamplesPlugin();
}
export { SearchExamplesPluginSetup, SearchExamplesPluginStart } from './types';

View file

@ -0,0 +1,76 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
AppMountParameters,
CoreSetup,
CoreStart,
Plugin,
AppNavLinkStatus,
} from '../../../src/core/public';
import {
SearchExamplesPluginSetup,
SearchExamplesPluginStart,
AppPluginSetupDependencies,
AppPluginStartDependencies,
} from './types';
import { PLUGIN_NAME } from '../common';
export class SearchExamplesPlugin
implements
Plugin<
SearchExamplesPluginSetup,
SearchExamplesPluginStart,
AppPluginSetupDependencies,
AppPluginStartDependencies
> {
public setup(
core: CoreSetup<AppPluginStartDependencies>,
{ developerExamples }: AppPluginSetupDependencies
): SearchExamplesPluginSetup {
// Register an application into the side navigation menu
core.application.register({
id: 'searchExamples',
title: PLUGIN_NAME,
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
// Load application bundle
const { renderApp } = await import('./application');
// Get start services as specified in kibana.json
const [coreStart, depsStart] = await core.getStartServices();
// Render the application
return renderApp(coreStart, depsStart, params);
},
});
developerExamples.register({
appId: 'searchExamples',
title: 'Search Examples',
description: `Search Examples`,
});
return {};
}
public start(core: CoreStart): SearchExamplesPluginStart {
return {};
}
public stop() {}
}

View file

@ -0,0 +1,36 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
import { DeveloperExamplesSetup } from '../../developer_examples/public';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchExamplesPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchExamplesPluginStart {}
export interface AppPluginSetupDependencies {
developerExamples: DeveloperExamplesSetup;
}
export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
data: DataPublicPluginStart;
}

View file

@ -0,0 +1,27 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { PluginInitializerContext } from '../../../src/core/server';
import { SearchExamplesPlugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
return new SearchExamplesPlugin(initializerContext);
}
export { SearchExamplesPluginSetup, SearchExamplesPluginStart } from './types';

View file

@ -0,0 +1,40 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { ISearchStrategy, PluginStart } from '../../../src/plugins/data/server';
import { IMyStrategyResponse, IMyStrategyRequest } from '../common';
export const mySearchStrategyProvider = (data: PluginStart): ISearchStrategy => {
const es = data.search.getSearchStrategy('es');
return {
search: async (context, request, options): Promise<IMyStrategyResponse> => {
request.debug = true;
const esSearchRes = await es.search(context, request, options);
return {
...esSearchRes,
cool: (request as IMyStrategyRequest).get_cool ? 'YES' : 'NOPE',
};
},
cancel: async (context, id) => {
if (es.cancel) {
es.cancel(context, id);
}
},
};
};

View file

@ -0,0 +1,73 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
PluginInitializerContext,
CoreSetup,
CoreStart,
Plugin,
Logger,
} from '../../../src/core/server';
import {
SearchExamplesPluginSetup,
SearchExamplesPluginStart,
SearchExamplesPluginSetupDeps,
SearchExamplesPluginStartDeps,
} from './types';
import { mySearchStrategyProvider } from './my_strategy';
import { registerRoutes } from './routes';
export class SearchExamplesPlugin
implements
Plugin<
SearchExamplesPluginSetup,
SearchExamplesPluginStart,
SearchExamplesPluginSetupDeps,
SearchExamplesPluginStartDeps
> {
private readonly logger: Logger;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
}
public setup(
core: CoreSetup<SearchExamplesPluginStartDeps>,
deps: SearchExamplesPluginSetupDeps
) {
this.logger.debug('search_examples: Setup');
const router = core.http.createRouter();
core.getStartServices().then(([_, depsStart]) => {
const myStrategy = mySearchStrategyProvider(depsStart.data);
deps.data.search.registerSearchStrategy('myStrategy', myStrategy);
registerRoutes(router, depsStart.data);
});
return {};
}
public start(core: CoreStart) {
this.logger.debug('search_examples: Started');
return {};
}
public stop() {}
}

View file

@ -0,0 +1,19 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { registerRoutes } from './register_routes';

View file

@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { IRouter } from 'kibana/server';
import { PluginStart as DataPluginStart } from 'src/plugins/data/server';
import { registerServerSearchRoute } from './server_search_route';
export function registerRoutes(router: IRouter, data: DataPluginStart) {
registerServerSearchRoute(router, data);
}

View file

@ -0,0 +1,70 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { PluginStart as DataPluginStart, IEsSearchRequest } from 'src/plugins/data/server';
import { schema } from '@kbn/config-schema';
import { IEsSearchResponse } from 'src/plugins/data/common';
import { IRouter } from '../../../../src/core/server';
import { SERVER_SEARCH_ROUTE_PATH } from '../../common';
export function registerServerSearchRoute(router: IRouter, data: DataPluginStart) {
router.get(
{
path: SERVER_SEARCH_ROUTE_PATH,
validate: {
query: schema.object({
index: schema.maybe(schema.string()),
field: schema.maybe(schema.string()),
}),
},
},
async (context, request, response) => {
const { index, field } = request.query;
// Run a synchronous search server side, by enforcing a high keepalive and waiting for completion.
// If you wish to run the search with polling (in basic+), you'd have to poll on the search API.
// Please reach out to the @app-arch-team if you need this to be implemented.
const res = await data.search.search(
context,
{
params: {
index,
body: {
aggs: {
'1': {
avg: {
field,
},
},
},
},
waitForCompletionTimeout: '5m',
keepAlive: '5m',
},
} as IEsSearchRequest,
{}
);
return response.ok({
body: {
aggs: (res as IEsSearchResponse).rawResponse.aggregations,
},
});
}
);
}

View file

@ -0,0 +1,34 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Rename PluginStart to something better
import { PluginSetup, PluginStart } from '../../../src/plugins/data/server';
export interface SearchExamplesPluginSetupDeps {
data: PluginSetup;
}
export interface SearchExamplesPluginStartDeps {
data: PluginStart;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchExamplesPluginSetup {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SearchExamplesPluginStart {}

View file

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"common/**/*.ts",
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
"../../typings/**/*",
],
"exclude": []
}

View file

@ -31,5 +31,13 @@ export interface IEsSearchRequest extends IKibanaSearchRequest {
}
export interface IEsSearchResponse extends IKibanaSearchResponse {
/**
* Indicates whether async search is still in flight
*/
isRunning?: boolean;
/**
* Indicates whether the results returned are complete or partial
*/
isPartial?: boolean;
rawResponse: SearchResponse<any>;
}

View file

@ -770,6 +770,8 @@ export interface IEsSearchRequest extends IKibanaSearchRequest {
//
// @public (undocumented)
export interface IEsSearchResponse extends IKibanaSearchResponse {
isPartial?: boolean;
isRunning?: boolean;
// (undocumented)
rawResponse: SearchResponse_2<any>;
}
@ -1213,11 +1215,10 @@ export type InputTimeRange = TimeRange | {
// @public (undocumented)
export type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable<IKibanaSearchResponse>;
// Warning: (ae-forgotten-export) The symbol "IStrategyOptions" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ISearchGeneric" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOptions) => Observable<IEsSearchResponse>;
export type ISearchGeneric = (request: IEsSearchRequest, options?: ISearchOptions) => Observable<IEsSearchResponse>;
// Warning: (ae-missing-release-tag) "ISearchOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@ -1225,6 +1226,8 @@ export type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOpti
export interface ISearchOptions {
// (undocumented)
signal?: AbortSignal;
// (undocumented)
strategy?: string;
}
// Warning: (ae-forgotten-export) The symbol "SearchSource" needs to be exported by the entry point index.d.ts
@ -1723,7 +1726,7 @@ export class SearchInterceptor {
// (undocumented)
protected readonly requestTimeout?: number | undefined;
// (undocumented)
protected runSearch(request: IEsSearchRequest, signal: AbortSignal): Observable<IEsSearchResponse>;
protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable<IEsSearchResponse>;
search(request: IEsSearchRequest, options?: ISearchOptions): Observable<IEsSearchResponse>;
// (undocumented)
protected setupTimers(options?: ISearchOptions): {

View file

@ -44,6 +44,7 @@ const createStartContractMock = () => {
savedQueries: jest.fn() as any,
state$: new Observable(),
timefilter: timefilterServiceMock.createStartContract(),
getEsQuery: jest.fn(),
};
return startContract;

View file

@ -26,6 +26,9 @@ import { TimefilterService, TimefilterSetup } from './timefilter';
import { createSavedQueryService } from './saved_query/saved_query_service';
import { createQueryStateObservable } from './state_sync/create_global_query_observable';
import { QueryStringManager, QueryStringContract } from './query_string';
import { buildEsQuery, getEsQueryConfig } from '../../common';
import { getUiSettings } from '../services';
import { IndexPattern } from '..';
/**
* Query Service
@ -86,6 +89,16 @@ export class QueryService {
savedQueries: createSavedQueryService(savedObjectsClient),
state$: this.state$,
timefilter: this.timefilter,
getEsQuery: (indexPattern: IndexPattern) => {
const timeFilter = this.timefilter.timefilter.createFilter(indexPattern);
return buildEsQuery(
indexPattern,
this.queryStringManager.getQuery(),
[...this.filterManager.getFilters(), ...(timeFilter ? [timeFilter] : [])],
getEsQueryConfig(getUiSettings())
);
},
};
}

View file

@ -17,11 +17,12 @@
* under the License.
*/
import { trimEnd } from 'lodash';
import { BehaviorSubject, throwError, timer, Subscription, defer, from, Observable } from 'rxjs';
import { finalize, filter } from 'rxjs/operators';
import { ApplicationStart, Toast, ToastsStart, CoreStart } from 'kibana/public';
import { getCombinedSignal, AbortError } from '../../common/utils';
import { IEsSearchRequest, IEsSearchResponse } from '../../common/search';
import { IEsSearchRequest, IEsSearchResponse, ES_SEARCH_STRATEGY } from '../../common/search';
import { ISearchOptions } from './types';
import { getLongQueryNotification } from './long_query_notification';
import { SearchUsageCollector } from './collectors';
@ -92,14 +93,20 @@ export class SearchInterceptor {
protected runSearch(
request: IEsSearchRequest,
signal: AbortSignal
signal: AbortSignal,
strategy?: string
): Observable<IEsSearchResponse> {
const { id, ...searchRequest } = request;
const path = id != null ? `/internal/search/es/${id}` : '/internal/search/es';
const method = 'POST';
const path = trimEnd(`/internal/search/${strategy || ES_SEARCH_STRATEGY}/${id || ''}`, '/');
const body = JSON.stringify(id != null ? {} : searchRequest);
const response = this.deps.http.fetch({ path, method, body, signal });
return from(response);
return from(
this.deps.http.fetch({
method: 'POST',
path,
body,
signal,
})
);
}
/**
@ -120,7 +127,7 @@ export class SearchInterceptor {
const { combinedSignal, cleanup } = this.setupTimers(options);
this.pendingCount$.next(++this.pendingCount);
return this.runSearch(request, combinedSignal).pipe(
return this.runSearch(request, combinedSignal, options?.strategy).pipe(
finalize(() => {
this.pendingCount$.next(--this.pendingCount);
cleanup();

View file

@ -37,6 +37,7 @@ import { GetInternalStartServicesFn } from '../types';
export interface ISearchOptions {
signal?: AbortSignal;
strategy?: string;
}
export type ISearch = (
@ -44,14 +45,9 @@ export type ISearch = (
options?: ISearchOptions
) => Observable<IKibanaSearchResponse>;
// Service API types
export interface IStrategyOptions extends ISearchOptions {
strategy?: string;
}
export type ISearchGeneric = (
request: IEsSearchRequest,
options?: IStrategyOptions
options?: ISearchOptions
) => Observable<IEsSearchResponse>;
export interface ISearchStartLegacy {

View file

@ -161,7 +161,12 @@ import {
toAbsoluteDates,
} from '../common';
export { EsaggsExpressionFunctionDefinition, ParsedInterval } from '../common';
export {
EsaggsExpressionFunctionDefinition,
ParsedInterval,
IEsSearchRequest,
IEsSearchResponse,
} from '../common';
export {
ISearchStrategy,

View file

@ -78,7 +78,7 @@ describe('ES search strategy', () => {
});
});
it('returns total, loaded, and raw response', async () => {
it('has all response parameters', async () => {
const params = { index: 'logstash-*' };
const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger);
@ -86,7 +86,8 @@ describe('ES search strategy', () => {
params,
});
expect(response).toHaveProperty('total');
expect(response.isRunning).toBe(false);
expect(response.isPartial).toBe(false);
expect(response).toHaveProperty('loaded');
expect(response).toHaveProperty('rawResponse');
});

View file

@ -30,7 +30,7 @@ export const esSearchStrategyProvider = (
): ISearchStrategy => {
return {
search: async (context, request, options) => {
logger.info(`search ${JSON.stringify(request.params)}`);
logger.info(`search ${request.params?.index}`);
const config = await config$.pipe(first()).toPromise();
const defaultParams = getDefaultSearchParams(config);
@ -56,7 +56,12 @@ export const esSearchStrategyProvider = (
// The above query will either complete or timeout and throw an error.
// There is no progress indication on this api.
return { rawResponse, ...getTotalLoaded(rawResponse._shards) };
return {
isPartial: false,
isRunning: false,
rawResponse,
...getTotalLoaded(rawResponse._shards),
};
} catch (e) {
if (usage) usage.trackError();
throw e;

View file

@ -369,6 +369,30 @@ export function getTotalLoaded({ total, failed, successful }: ShardsResponse): {
loaded: number;
};
// Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface IEsSearchRequest extends IKibanaSearchRequest {
// (undocumented)
indexType?: string;
// Warning: (ae-forgotten-export) The symbol "ISearchRequestParams" needs to be exported by the entry point index.d.ts
//
// (undocumented)
params?: ISearchRequestParams;
}
// Warning: (ae-forgotten-export) The symbol "IKibanaSearchResponse" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "IEsSearchResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface IEsSearchResponse extends IKibanaSearchResponse {
isPartial?: boolean;
isRunning?: boolean;
// (undocumented)
rawResponse: SearchResponse<any>;
}
// Warning: (ae-missing-release-tag) "IFieldFormatsRegistry" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@ -547,8 +571,6 @@ export interface ISearchSetup {
export interface ISearchStart {
getSearchStrategy: (name: string) => ISearchStrategy;
// Warning: (ae-forgotten-export) The symbol "RequestHandlerContext" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "IKibanaSearchRequest" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "IKibanaSearchResponse" needs to be exported by the entry point index.d.ts
//
// (undocumented)
search: (context: RequestHandlerContext, request: IKibanaSearchRequest, options: ISearchOptions) => Promise<IKibanaSearchResponse>;
@ -560,9 +582,6 @@ export interface ISearchStart {
export interface ISearchStrategy {
// (undocumented)
cancel?: (context: RequestHandlerContext, id: string) => Promise<void>;
// Warning: (ae-forgotten-export) The symbol "IEsSearchRequest" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "IEsSearchResponse" needs to be exported by the entry point index.d.ts
//
// (undocumented)
search: (context: RequestHandlerContext, request: IEsSearchRequest, options?: ISearchOptions) => Promise<IEsSearchResponse>;
}
@ -817,13 +836,13 @@ export function usageProvider(core: CoreSetup_2): SearchUsage;
// src/plugins/data/server/index.ts:101:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:127:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:180:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:181:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:182:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:183:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:184:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:185:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:186:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:187:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:188:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:189:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:190:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
// src/plugins/data/server/index.ts:193:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)

View file

@ -4,9 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export {
EnhancedSearchParams,
IEnhancedEsSearchRequest,
IAsyncSearchRequest,
IAsyncSearchResponse,
} from './search';
export { EnhancedSearchParams, IEnhancedEsSearchRequest, IAsyncSearchRequest } from './search';

View file

@ -4,9 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export {
EnhancedSearchParams,
IEnhancedEsSearchRequest,
IAsyncSearchRequest,
IAsyncSearchResponse,
} from './types';
export { EnhancedSearchParams, IEnhancedEsSearchRequest, IAsyncSearchRequest } from './types';

View file

@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import {
IEsSearchRequest,
IEsSearchResponse,
ISearchRequestParams,
} from '../../../../../src/plugins/data/common';
import { IEsSearchRequest, ISearchRequestParams } from '../../../../../src/plugins/data/common';
export interface EnhancedSearchParams extends ISearchRequestParams {
ignoreThrottled: boolean;
@ -23,17 +19,6 @@ export interface IAsyncSearchRequest extends IEsSearchRequest {
params?: EnhancedSearchParams;
}
export interface IAsyncSearchResponse extends IEsSearchResponse {
/**
* Indicates whether async search is still in flight
*/
is_running?: boolean;
/**
* Indicates whether the results returned are complete or partial
*/
is_partial?: boolean;
}
export interface IEnhancedEsSearchRequest extends IEsSearchRequest {
/**
* Used to determine whether to use the _rollups_search or a regular search endpoint.

View file

@ -72,8 +72,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
rawResponse: {
took: 1,
@ -99,8 +99,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: true,
isPartial: false,
isRunning: true,
id: 1,
rawResponse: {
took: 1,
@ -110,8 +110,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 20,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
rawResponse: {
took: 1,
@ -144,8 +144,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: true,
is_running: false,
isPartial: true,
isRunning: false,
id: 1,
},
},
@ -168,8 +168,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 500,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
@ -194,16 +194,16 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: true,
isPartial: false,
isRunning: true,
id: 1,
},
},
{
time: 300,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
@ -238,8 +238,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 2000,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
@ -262,16 +262,16 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: true,
isPartial: false,
isRunning: true,
id: 1,
},
},
{
time: 2000,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
@ -302,8 +302,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: true,
isPartial: false,
isRunning: true,
id: 1,
},
},
@ -311,8 +311,8 @@ describe('EnhancedSearchInterceptor', () => {
time: 10,
value: {
error: 'oh no',
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
isError: true,
@ -346,16 +346,16 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 10,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
{
time: 20,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
},
},
@ -380,8 +380,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 250,
value: {
is_partial: true,
is_running: true,
isPartial: true,
isRunning: true,
id: 1,
rawResponse: {
took: 1,
@ -391,8 +391,8 @@ describe('EnhancedSearchInterceptor', () => {
{
time: 2000,
value: {
is_partial: false,
is_running: false,
isPartial: false,
isRunning: false,
id: 1,
rawResponse: {
took: 1,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Observable, throwError, EMPTY, timer, from } from 'rxjs';
import { throwError, EMPTY, timer, from } from 'rxjs';
import { mergeMap, expand, takeUntil, finalize, tap } from 'rxjs/operators';
import { getLongQueryNotification } from './long_query_notification';
import {
@ -14,7 +14,7 @@ import {
} from '../../../../../src/plugins/data/public';
import { AbortError, toPromise } from '../../../../../src/plugins/data/common';
import { IAsyncSearchOptions } from '.';
import { IAsyncSearchRequest, IAsyncSearchResponse } from '../../common';
import { IAsyncSearchRequest } from '../../common';
export class EnhancedSearchInterceptor extends SearchInterceptor {
/**
@ -67,7 +67,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
public search(
request: IAsyncSearchRequest,
{ pollInterval = 1000, ...options }: IAsyncSearchOptions = {}
): Observable<IAsyncSearchResponse> {
) {
let { id } = request;
request.params = {
@ -80,15 +80,15 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
this.pendingCount$.next(++this.pendingCount);
return (this.runSearch(request, combinedSignal) as Observable<IAsyncSearchResponse>).pipe(
expand((response: IAsyncSearchResponse) => {
return this.runSearch(request, combinedSignal, options?.strategy).pipe(
expand((response) => {
// If the response indicates of an error, stop polling and complete the observable
if (!response || (!response.is_running && response.is_partial)) {
if (!response || (!response.isRunning && response.isPartial)) {
return throwError(new AbortError());
}
// If the response indicates it is complete, stop polling and complete the observable
if (!response.is_running) {
if (!response.isRunning) {
return EMPTY;
}
@ -97,7 +97,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
return timer(pollInterval).pipe(
// Send future requests using just the ID from the response
mergeMap(() => {
return this.runSearch({ id }, combinedSignal) as Observable<IAsyncSearchResponse>;
return this.runSearch({ id }, combinedSignal, options?.strategy);
})
);
}),

View file

@ -128,8 +128,8 @@ async function asyncSearch(
return {
id,
is_partial,
is_running,
isPartial: is_partial,
isRunning: is_running,
rawResponse: shimHitsTotal(response),
...getTotalLoaded(response._shards),
};