diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md
new file mode 100644
index 0000000000000..00a56c6fe9c31
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) > [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md)
+
+## IEsSearchResponse.isPartial property
+
+Indicates whether the results returned are complete or partial
+
+Signature:
+
+```typescript
+isPartial?: boolean;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md
new file mode 100644
index 0000000000000..56fb1a7519811
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-public.iessearchresponse.md) > [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md)
+
+## IEsSearchResponse.isRunning property
+
+Indicates whether async search is still in flight
+
+Signature:
+
+```typescript
+isRunning?: boolean;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md
index ea7e2aef00d6e..041d79de3282e 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iessearchresponse.md
@@ -14,5 +14,7 @@ export interface IEsSearchResponse extends IKibanaSearchResponse
| Property | Type | Description |
| --- | --- | --- |
+| [isPartial](./kibana-plugin-plugins-data-public.iessearchresponse.ispartial.md) | boolean
| Indicates whether the results returned are complete or partial |
+| [isRunning](./kibana-plugin-plugins-data-public.iessearchresponse.isrunning.md) | boolean
| Indicates whether async search is still in flight |
| [rawResponse](./kibana-plugin-plugins-data-public.iessearchresponse.rawresponse.md) | SearchResponse<any>
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md
index cdf19cd15a298..3bd6a398c8df5 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchgeneric.md
@@ -7,5 +7,5 @@
Signature:
```typescript
-export declare type ISearchGeneric = (request: IEsSearchRequest, options?: IStrategyOptions) => Observable;
+export declare type ISearchGeneric = (request: IEsSearchRequest, options?: ISearchOptions) => Observable;
```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
index 05b7252606289..3eb38dc7d52e0 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md
@@ -15,4 +15,5 @@ export interface ISearchOptions
| Property | Type | Description |
| --- | --- | --- |
| [signal](./kibana-plugin-plugins-data-public.isearchoptions.signal.md) | AbortSignal
| |
+| [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | string
| |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.strategy.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.strategy.md
new file mode 100644
index 0000000000000..df7e050691a8f
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.strategy.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md)
+
+## ISearchOptions.strategy property
+
+Signature:
+
+```typescript
+strategy?: string;
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md
index 30e980b5ffc54..b3b7da05326d0 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md
@@ -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 search
method. Overrides the AbortSignal
with one that will abort either when cancelPending
is called, when the request times out, or when the original AbortSignal
is aborted. Updates the pendingCount
when the request is started/finalized. |
| [setupTimers(options)](./kibana-plugin-plugins-data-public.searchinterceptor.setuptimers.md) | | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md
index 3601a00c48cf3..ad1d1dcb59d7b 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.runsearch.md
@@ -7,7 +7,7 @@
Signature:
```typescript
-protected runSearch(request: IEsSearchRequest, signal: AbortSignal): Observable;
+protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable;
```
## Parameters
@@ -16,6 +16,7 @@ protected runSearch(request: IEsSearchRequest, signal: AbortSignal): Observable<
| --- | --- | --- |
| request | IEsSearchRequest
| |
| signal | AbortSignal
| |
+| strategy | string
| |
Returns:
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.indextype.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.indextype.md
new file mode 100644
index 0000000000000..aaf4e55ee007b
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.indextype.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) > [indexType](./kibana-plugin-plugins-data-server.iessearchrequest.indextype.md)
+
+## IEsSearchRequest.indexType property
+
+Signature:
+
+```typescript
+indexType?: string;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md
new file mode 100644
index 0000000000000..0dfa23eb64c1b
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md)
+
+## IEsSearchRequest interface
+
+Signature:
+
+```typescript
+export interface IEsSearchRequest extends IKibanaSearchRequest
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [indexType](./kibana-plugin-plugins-data-server.iessearchrequest.indextype.md) | string
| |
+| [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md) | ISearchRequestParams
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md
new file mode 100644
index 0000000000000..d65281973c951
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchrequest.params.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) > [params](./kibana-plugin-plugins-data-server.iessearchrequest.params.md)
+
+## IEsSearchRequest.params property
+
+Signature:
+
+```typescript
+params?: ISearchRequestParams;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md
new file mode 100644
index 0000000000000..fbddfc1cd9fc4
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md)
+
+## IEsSearchResponse.isPartial property
+
+Indicates whether the results returned are complete or partial
+
+Signature:
+
+```typescript
+isPartial?: boolean;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md
new file mode 100644
index 0000000000000..01f3982957d5c
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md)
+
+## IEsSearchResponse.isRunning property
+
+Indicates whether async search is still in flight
+
+Signature:
+
+```typescript
+isRunning?: boolean;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md
new file mode 100644
index 0000000000000..0407dce5fe418
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md)
+
+## IEsSearchResponse interface
+
+Signature:
+
+```typescript
+export interface IEsSearchResponse extends IKibanaSearchResponse
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [isPartial](./kibana-plugin-plugins-data-server.iessearchresponse.ispartial.md) | boolean
| Indicates whether the results returned are complete or partial |
+| [isRunning](./kibana-plugin-plugins-data-server.iessearchresponse.isrunning.md) | boolean
| Indicates whether async search is still in flight |
+| [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md) | SearchResponse<any>
| |
+
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md
new file mode 100644
index 0000000000000..0ee1691d0f697
--- /dev/null
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IEsSearchResponse](./kibana-plugin-plugins-data-server.iessearchresponse.md) > [rawResponse](./kibana-plugin-plugins-data-server.iessearchresponse.rawresponse.md)
+
+## IEsSearchResponse.rawResponse property
+
+Signature:
+
+```typescript
+rawResponse: SearchResponse;
+```
diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
index 1bcd575803f88..f472064c87755 100644
--- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
+++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md
@@ -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) | |
diff --git a/docs/user/visualize.asciidoc b/docs/user/visualize.asciidoc
index 6919b5a8772ef..dc116962f9e96 100644
--- a/docs/user/visualize.asciidoc
+++ b/docs/user/visualize.asciidoc
@@ -132,6 +132,7 @@ include::{kib-repo-dir}/visualize/lens.asciidoc[]
include::{kib-repo-dir}/visualize/most-frequent.asciidoc[]
include::{kib-repo-dir}/visualize/tsvb.asciidoc[]
+
include::{kib-repo-dir}/visualize/timelion.asciidoc[]
include::{kib-repo-dir}/visualize/tilemap.asciidoc[]
diff --git a/docs/visualize/images/timelion-add-to-dashboard.png b/docs/visualize/images/timelion-add-to-dashboard.png
new file mode 100644
index 0000000000000..a0ccc0af735a8
Binary files /dev/null and b/docs/visualize/images/timelion-add-to-dashboard.png differ
diff --git a/docs/visualize/images/timelion-app.png b/docs/visualize/images/timelion-app.png
new file mode 100644
index 0000000000000..236f9f2d41808
Binary files /dev/null and b/docs/visualize/images/timelion-app.png differ
diff --git a/docs/visualize/images/timelion-copy-expression.png b/docs/visualize/images/timelion-copy-expression.png
new file mode 100644
index 0000000000000..376bf7919166e
Binary files /dev/null and b/docs/visualize/images/timelion-copy-expression.png differ
diff --git a/docs/visualize/images/timelion-create-new-dashboard.png b/docs/visualize/images/timelion-create-new-dashboard.png
new file mode 100644
index 0000000000000..4049b6d77cca6
Binary files /dev/null and b/docs/visualize/images/timelion-create-new-dashboard.png differ
diff --git a/docs/visualize/images/timelion-dashboard.png b/docs/visualize/images/timelion-dashboard.png
new file mode 100644
index 0000000000000..e217dca98d079
Binary files /dev/null and b/docs/visualize/images/timelion-dashboard.png differ
diff --git a/docs/visualize/images/timelion-vis-paste-expression.png b/docs/visualize/images/timelion-vis-paste-expression.png
new file mode 100644
index 0000000000000..86e175e40815b
Binary files /dev/null and b/docs/visualize/images/timelion-vis-paste-expression.png differ
diff --git a/docs/visualize/timelion.asciidoc b/docs/visualize/timelion.asciidoc
index 9e41cce561454..4869664fab0a4 100644
--- a/docs/visualize/timelion.asciidoc
+++ b/docs/visualize/timelion.asciidoc
@@ -504,3 +504,44 @@ image::images/timelion-conditional04.png[]
{nbsp}
For additional information on Timelion conditional capabilities, go to https://www.elastic.co/blog/timeseries-if-then-else-with-timelion[I have but one .condition()].
+
+[float]
+[[timelion-deprecation]]
+=== Timelion App deprecation
+
+Deprecated since 7.0, the Timelion app will be removed in 8.0. If you have any Timelion worksheets, you must migrate them to a dashboard.
+
+NOTE: Only the Timelion app is deprecated. {kib} continues to support Timelion visualizations on dashboards, in Visualize, and in Canvas.
+
+[float]
+[[timelion-app-to-vis]]
+==== Create a dashboard from a Timelion worksheet
+
+To replace a Timelion worksheet with a dashboard, follow the same process for adding a visualization.
+In addition, you must migrate the Timelion graphs to Visualize.
+
+. Open the menu, click **Dashboard**, then click **Create dashboard**.
+
+. On the dashboard, click **Create New**, then select the Timelion visualization.
++
+[role="screenshot"]
+image::images/timelion-create-new-dashboard.png[]
++
+The only thing you need is the Timelion expression for each graph.
+
+. Open the Timelion app on a new tab, select the chart you want to copy, and copy its expression.
++
+[role="screenshot"]
+image::images/timelion-copy-expression.png[]
+
+. Return to the other tab and paste the copied expression to the *Timelion Expression* field and click **Update**.
++
+[role="screenshot"]
+image::images/timelion-vis-paste-expression.png[]
+
+. Save the new visualization, give it a name, and click **Save and Return**.
++
+Your Timelion visualization will appear on the dashboard. Repeat this for all your charts on each worksheet.
++
+[role="screenshot"]
+image::images/timelion-dashboard.png[]
diff --git a/examples/search_examples/README.md b/examples/search_examples/README.md
new file mode 100644
index 0000000000000..bcc17bf7f3333
--- /dev/null
+++ b/examples/search_examples/README.md
@@ -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.
diff --git a/examples/search_examples/common/index.ts b/examples/search_examples/common/index.ts
new file mode 100644
index 0000000000000..e1e7f6389ae83
--- /dev/null
+++ b/examples/search_examples/common/index.ts
@@ -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';
diff --git a/examples/search_examples/kibana.json b/examples/search_examples/kibana.json
new file mode 100644
index 0000000000000..7e392b8417360
--- /dev/null
+++ b/examples/search_examples/kibana.json
@@ -0,0 +1,9 @@
+{
+ "id": "searchExamples",
+ "version": "8.0.0",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["navigation", "data", "developerExamples"],
+ "optionalPlugins": [],
+ "requiredBundles": []
+}
diff --git a/examples/search_examples/public/application.tsx b/examples/search_examples/public/application.tsx
new file mode 100644
index 0000000000000..81e4ee8514c11
--- /dev/null
+++ b/examples/search_examples/public/application.tsx
@@ -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(
+ ,
+ element
+ );
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+};
diff --git a/examples/search_examples/public/components/app.tsx b/examples/search_examples/public/components/app.tsx
new file mode 100644
index 0000000000000..31cc5e420da11
--- /dev/null
+++ b/examples/search_examples/public/components/app.tsx
@@ -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(false);
+ const [timeTook, setTimeTook] = useState();
+ const [indexPattern, setIndexPattern] = useState();
+ const [numericFields, setNumericFields] = useState();
+ const [selectedField, setSelectedField] = useState();
+
+ // 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 = (
+
+ Searched {response.rawResponse.hits.total} documents.
+ The average of {selectedField.name} is {avgResult ? Math.floor(avgResult) : 0}.
+
+ Is it Cool? {String((response as IMyStrategyResponse).cool)}
+
+ );
+ 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 (
+
+
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Index Pattern
+ {
+ const newIndexPattern = await data.indexPatterns.get(newIndexPatternId);
+ setIndexPattern(newIndexPattern);
+ }}
+ isClearable={false}
+ />
+
+
+ Numeric Fields
+ {
+ const field = indexPattern.getFieldByName(option[0].label);
+ setSelectedField(field || null);
+ }}
+ sortMatchesBy="startsWith"
+ />
+
+
+
+
+
+
+
+
+
+ Searching Elasticsearch using data.search
+
+
+
+ If you want to fetch data from Elasticsearch, you can use the different services
+ provided by the data plugin. These help you get the index
+ pattern and search bar configuration, format them into a DSL query and send it
+ to Elasticsearch.
+
+
+
+
+
+
+
+ Writing a custom search strategy
+
+
+ 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.
+
+
+ }
+ checked={getCool}
+ onChange={(event) => setGetCool(event.target.checked)}
+ />
+
+
+
+
+
+
+ Using search on the server
+
+
+ You can also run your search request from the server, without registering a
+ search strategy. This request does not take the configuration of{' '}
+ TopNavMenu into account, but you could pass those down to the
+ server as well.
+
+
+
+
+
+
+
+
+ >
+
+
+ );
+};
diff --git a/examples/search_examples/public/index.scss b/examples/search_examples/public/index.scss
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/examples/search_examples/public/index.ts b/examples/search_examples/public/index.ts
new file mode 100644
index 0000000000000..cd3208aa0c088
--- /dev/null
+++ b/examples/search_examples/public/index.ts
@@ -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';
diff --git a/examples/search_examples/public/plugin.ts b/examples/search_examples/public/plugin.ts
new file mode 100644
index 0000000000000..436304e9d5ff6
--- /dev/null
+++ b/examples/search_examples/public/plugin.ts
@@ -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,
+ { 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() {}
+}
diff --git a/examples/search_examples/public/types.ts b/examples/search_examples/public/types.ts
new file mode 100644
index 0000000000000..0670cc5907449
--- /dev/null
+++ b/examples/search_examples/public/types.ts
@@ -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;
+}
diff --git a/examples/search_examples/server/index.ts b/examples/search_examples/server/index.ts
new file mode 100644
index 0000000000000..06d41490af57a
--- /dev/null
+++ b/examples/search_examples/server/index.ts
@@ -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';
diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts
new file mode 100644
index 0000000000000..a1116ddbd759b
--- /dev/null
+++ b/examples/search_examples/server/my_strategy.ts
@@ -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 => {
+ 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);
+ }
+ },
+ };
+};
diff --git a/examples/search_examples/server/plugin.ts b/examples/search_examples/server/plugin.ts
new file mode 100644
index 0000000000000..deef5a53b2b73
--- /dev/null
+++ b/examples/search_examples/server/plugin.ts
@@ -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,
+ 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() {}
+}
diff --git a/examples/search_examples/server/routes/index.ts b/examples/search_examples/server/routes/index.ts
new file mode 100644
index 0000000000000..ea575cf371bb7
--- /dev/null
+++ b/examples/search_examples/server/routes/index.ts
@@ -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';
diff --git a/examples/search_examples/server/routes/register_routes.ts b/examples/search_examples/server/routes/register_routes.ts
new file mode 100644
index 0000000000000..ac3ea28d89d26
--- /dev/null
+++ b/examples/search_examples/server/routes/register_routes.ts
@@ -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);
+}
diff --git a/examples/search_examples/server/routes/server_search_route.ts b/examples/search_examples/server/routes/server_search_route.ts
new file mode 100644
index 0000000000000..6eb21cf34b4a3
--- /dev/null
+++ b/examples/search_examples/server/routes/server_search_route.ts
@@ -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,
+ },
+ });
+ }
+ );
+}
diff --git a/examples/search_examples/server/types.ts b/examples/search_examples/server/types.ts
new file mode 100644
index 0000000000000..da0f24a3012bb
--- /dev/null
+++ b/examples/search_examples/server/types.ts
@@ -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 {}
diff --git a/examples/search_examples/tsconfig.json b/examples/search_examples/tsconfig.json
new file mode 100644
index 0000000000000..8a3ced743d0fa
--- /dev/null
+++ b/examples/search_examples/tsconfig.json
@@ -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": []
+}
diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts
index db2e31706e95c..6fc0923768703 100644
--- a/src/plugins/data/common/search/es_search/types.ts
+++ b/src/plugins/data/common/search/es_search/types.ts
@@ -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;
}
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 216ed018957e0..6225d74fb1b31 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -770,6 +770,8 @@ export interface IEsSearchRequest extends IKibanaSearchRequest {
//
// @public (undocumented)
export interface IEsSearchResponse extends IKibanaSearchResponse {
+ isPartial?: boolean;
+ isRunning?: boolean;
// (undocumented)
rawResponse: SearchResponse_2;
}
@@ -1213,11 +1215,10 @@ export type InputTimeRange = TimeRange | {
// @public (undocumented)
export type ISearch = (request: IKibanaSearchRequest, options?: ISearchOptions) => Observable;
-// 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;
+export type ISearchGeneric = (request: IEsSearchRequest, options?: ISearchOptions) => Observable;
// 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;
+ protected runSearch(request: IEsSearchRequest, signal: AbortSignal, strategy?: string): Observable;
search(request: IEsSearchRequest, options?: ISearchOptions): Observable;
// (undocumented)
protected setupTimers(options?: ISearchOptions): {
diff --git a/src/plugins/data/public/query/mocks.ts b/src/plugins/data/public/query/mocks.ts
index 8c15d9d6d0152..53c177de0fa39 100644
--- a/src/plugins/data/public/query/mocks.ts
+++ b/src/plugins/data/public/query/mocks.ts
@@ -44,6 +44,7 @@ const createStartContractMock = () => {
savedQueries: jest.fn() as any,
state$: new Observable(),
timefilter: timefilterServiceMock.createStartContract(),
+ getEsQuery: jest.fn(),
};
return startContract;
diff --git a/src/plugins/data/public/query/query_service.ts b/src/plugins/data/public/query/query_service.ts
index da514c0e24ea4..fe7fdcbb1d113 100644
--- a/src/plugins/data/public/query/query_service.ts
+++ b/src/plugins/data/public/query/query_service.ts
@@ -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())
+ );
+ },
};
}
diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts
index 677ad0ccea677..d6fcde8e986f3 100644
--- a/src/plugins/data/public/search/search_interceptor.ts
+++ b/src/plugins/data/public/search/search_interceptor.ts
@@ -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 {
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();
diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts
index ec74275f35c04..f80a13d048a68 100644
--- a/src/plugins/data/public/search/types.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -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;
-// Service API types
-export interface IStrategyOptions extends ISearchOptions {
- strategy?: string;
-}
-
export type ISearchGeneric = (
request: IEsSearchRequest,
- options?: IStrategyOptions
+ options?: ISearchOptions
) => Observable;
export interface ISearchStartLegacy {
diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts
index 1f3d7fbcb9f0f..73ed88850d787 100644
--- a/src/plugins/data/server/index.ts
+++ b/src/plugins/data/server/index.ts
@@ -161,7 +161,12 @@ import {
toAbsoluteDates,
} from '../common';
-export { EsaggsExpressionFunctionDefinition, ParsedInterval } from '../common';
+export {
+ EsaggsExpressionFunctionDefinition,
+ ParsedInterval,
+ IEsSearchRequest,
+ IEsSearchResponse,
+} from '../common';
export {
ISearchStrategy,
diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
index bc59bdee6a40a..2888f9d9d20b5 100644
--- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
+++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts
@@ -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');
});
diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts
index 78ead6df1a44e..234c30376d6db 100644
--- a/src/plugins/data/server/search/es_search/es_search_strategy.ts
+++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts
@@ -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;
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 013034c79d3f3..37d569a4bf9fe 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -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;
+}
+
// 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;
@@ -560,9 +582,6 @@ export interface ISearchStart {
export interface ISearchStrategy {
// (undocumented)
cancel?: (context: RequestHandlerContext, id: string) => Promise;
- // 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;
}
@@ -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)
diff --git a/src/plugins/vis_type_vega/public/components/vega_actions_menu.tsx b/src/plugins/vis_type_vega/public/components/vega_actions_menu.tsx
index f10954df432c2..33fa1ceefd3d5 100644
--- a/src/plugins/vis_type_vega/public/components/vega_actions_menu.tsx
+++ b/src/plugins/vis_type_vega/public/components/vega_actions_menu.tsx
@@ -70,7 +70,7 @@ function VegaActionsMenu({ formatHJson, formatJson }: VegaActionsMenuProps) {
return (