From 163eca90be4475cb07feb1bf6ac7f65078117cda Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 28 Jul 2021 02:17:22 -0400 Subject: [PATCH] [data.search.autocomplete] Move autocomplete method to UI settings (#106331) (#106950) * [data.search.autocomplete] Move autocomplete method to UI settings * Use select rather than boolean * Add ftue tour * Make a select rather than text box * Only show when focused and first time page is loaded * Add docs link * Reverse order of sections * Update docs/concepts/index.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Update docs/concepts/index.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * Docs updates * setting * telemetry * Add links to docs * Fix translations * Fix failing test * Fix test * Fix tests * Revert changes to querybar service * Fix discover query Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: Liza K Co-authored-by: Lukas Olson Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: Liza K --- docs/concepts/index.asciidoc | 8 ++ ...-plugin-core-public.doclinksstart.links.md | 1 + ...kibana-plugin-core-public.doclinksstart.md | 2 +- ...-plugin-plugins-data-public.ui_settings.md | 1 + ...-plugin-plugins-data-server.ui_settings.md | 1 + docs/management/advanced-options.asciidoc | 12 +- .../public/doc_links/doc_links_service.ts | 2 + src/core/public/public.api.md | 1 + src/plugins/data/common/constants.ts | 1 + src/plugins/data/config.ts | 3 - .../value_suggestion_provider.test.ts | 72 +++++++++++- .../providers/value_suggestion_provider.ts | 4 +- src/plugins/data/public/public.api.md | 1 + .../autocomplete_ftue_popover.test.tsx | 86 +++++++++++++++ .../autocomplete_ftue_popover.tsx | 104 ++++++++++++++++++ .../query_string_input/query_bar_top_row.tsx | 38 ++++--- .../autocomplete/value_suggestions_route.ts | 5 +- src/plugins/data/server/server.api.md | 1 + src/plugins/data/server/ui_settings.ts | 35 +++++- .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../apps/dashboard/dashboard_state.ts | 1 + test/functional/apps/discover/_discover.ts | 1 + .../schema/xpack_plugins.json | 6 +- .../translations/translations/ja-JP.json | 2 +- .../translations/translations/zh-CN.json | 2 +- 27 files changed, 363 insertions(+), 38 deletions(-) create mode 100644 src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.test.tsx create mode 100644 src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.tsx diff --git a/docs/concepts/index.asciidoc b/docs/concepts/index.asciidoc index ae6e48556f684..20d7103f021cd 100644 --- a/docs/concepts/index.asciidoc +++ b/docs/concepts/index.asciidoc @@ -115,6 +115,14 @@ Following are some example KQL queries. For more detailed examples, refer to << | `machine.os:win*` |=== +[float] +[[autocomplete-suggestions]] +==== Suggestions for autocomplete + +Beginning in 7.14, {kib} uses the {ref}/search-terms-enum.html[terms enum API] for autocomplete. {kib} returns results faster, but suggestions are approximate, sorted alphabetically, and can be outside the selected time range, even if `autocomplete:useTimeFilter` is enabled (as the terms enum API applies time filtering on an index-level, rather than document-level). + +Previously, {kib} used the {ref}/search-aggregations-bucket-terms-aggregation.html[terms aggregation API], which is slower, but suggestions included all values that matched your query, and optionally, your time range, and were sorted by popularity. To revert to using the terms aggregation API, go to <>, and set `autocomplete:valueSuggestionMethod` to `terms_agg`. + [float] ==== Additional filters with AND diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 3650fe970d8fc..cadb34ae63b86 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -141,6 +141,7 @@ readonly links: { readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; + readonly autocompleteChanges: string; }; readonly date: { readonly dateMath: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 4f66cc9a2c10f..aded69733b58b 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
} | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md index 9a0c37c8edd38..bdfc881878efd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.ui_settings.md @@ -38,5 +38,6 @@ UI_SETTINGS: { readonly FILTERS_PINNED_BY_DEFAULT: "filters:pinnedByDefault"; readonly FILTERS_EDITOR_SUGGEST_VALUES: "filterEditor:suggestValues"; readonly AUTOCOMPLETE_USE_TIMERANGE: "autocomplete:useTimeRange"; + readonly AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: "autocomplete:valueSuggestionMethod"; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md index c2edc64f292d2..387dac22e1c83 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.ui_settings.md @@ -38,5 +38,6 @@ UI_SETTINGS: { readonly FILTERS_PINNED_BY_DEFAULT: "filters:pinnedByDefault"; readonly FILTERS_EDITOR_SUGGEST_VALUES: "filterEditor:suggestValues"; readonly AUTOCOMPLETE_USE_TIMERANGE: "autocomplete:useTimeRange"; + readonly AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: "autocomplete:valueSuggestionMethod"; } ``` diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 55c3e3518495b..ac4356e12be49 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -88,8 +88,16 @@ Sets the file size limit when importing files. The default value is `100MB`. The highest supported value for this setting is `1GB`. [[filtereditor-suggestvalues]]`filterEditor:suggestValues`:: -Set this property to `false` to prevent the filter editor from suggesting values -for fields. +Set this property to `false` to prevent the filter editor and KQL autocomplete +from suggesting values for fields. + +[[autocomplete-valuesuggestionmethod]]`autocomplete:valueSuggestionMethod`:: +When set to `terms_enum`, autocomplete uses the terms enum API for value suggestions. Kibana returns results faster, but suggestions are approximate, sorted alphabetically, and can be outside the selected time range. +When set to `terms_agg`, Kibana uses a terms aggregation for value suggestions, which is slower, but suggestions include all values that optionally match your time range and are sorted by popularity. +<>. + +[[autocomplete-usetimerange]]`autocomplete:useTimeRange`:: +Disable this property to get autocomplete suggestions from your full dataset, rather than from the current time range. <>. [[filters-pinnedbydefault]]`filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 831df4d81c8e1..3f21df4ff0a50 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -214,6 +214,7 @@ export class DocLinksService { luceneQuerySyntax: `${ELASTICSEARCH_DOCS}query-dsl-query-string-query.html#query-string-syntax`, percolate: `${ELASTICSEARCH_DOCS}query-dsl-percolate-query.html`, queryDsl: `${ELASTICSEARCH_DOCS}query-dsl.html`, + autocompleteChanges: `${KIBANA_DOCS}kibana-concepts-analysts.html#autocomplete-suggestions`, }, search: { sessions: `${KIBANA_DOCS}search-sessions.html`, @@ -579,6 +580,7 @@ export interface DocLinksStart { readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; + readonly autocompleteChanges: string; }; readonly date: { readonly dateMath: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 673cb2e7ab557..8e0087cbeeed0 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -622,6 +622,7 @@ export interface DocLinksStart { readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; + readonly autocompleteChanges: string; }; readonly date: { readonly dateMath: string; diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index 645de0f9fed17..89ccdfcc0479e 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -45,4 +45,5 @@ export const UI_SETTINGS = { FILTERS_PINNED_BY_DEFAULT: 'filters:pinnedByDefault', FILTERS_EDITOR_SUGGEST_VALUES: 'filterEditor:suggestValues', AUTOCOMPLETE_USE_TIMERANGE: 'autocomplete:useTimeRange', + AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: 'autocomplete:valueSuggestionMethod', } as const; diff --git a/src/plugins/data/config.ts b/src/plugins/data/config.ts index a2b5a568b70ef..2512846554106 100644 --- a/src/plugins/data/config.ts +++ b/src/plugins/data/config.ts @@ -15,9 +15,6 @@ export const configSchema = schema.object({ }), valueSuggestions: schema.object({ enabled: schema.boolean({ defaultValue: true }), - method: schema.oneOf([schema.literal('terms_enum'), schema.literal('terms_agg')], { - defaultValue: 'terms_enum', - }), tiers: schema.arrayOf( schema.oneOf([ schema.literal('data_content'), diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts index a7b1bd2c7839d..d01064b4651f7 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts +++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts @@ -10,14 +10,17 @@ import { stubIndexPattern, stubFields } from '../../stubs'; import { TimefilterSetup } from '../../query'; import { setupValueSuggestionProvider, ValueSuggestionsGetFn } from './value_suggestion_provider'; import { IUiSettingsClient, CoreSetup } from 'kibana/public'; +import { UI_SETTINGS } from '../../../common'; describe('FieldSuggestions', () => { let getValueSuggestions: ValueSuggestionsGetFn; let http: any; - let shouldSuggestValues: boolean; + let uiConfig: Record = {}; + const uiSettings = { + get: (key: string) => uiConfig[key], + } as IUiSettingsClient; beforeEach(() => { - const uiSettings = { get: (key: string) => shouldSuggestValues } as IUiSettingsClient; http = { fetch: jest.fn().mockResolvedValue([]) }; getValueSuggestions = setupValueSuggestionProvider({ http, uiSettings } as CoreSetup, { @@ -40,6 +43,8 @@ describe('FieldSuggestions', () => { }); describe('with value suggestions disabled', () => { + uiConfig = { [UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES]: false }; + it('should return an empty array', async () => { const suggestions = await getValueSuggestions({ indexPattern: stubIndexPattern, @@ -53,7 +58,7 @@ describe('FieldSuggestions', () => { }); describe('with value suggestions enabled', () => { - shouldSuggestValues = true; + uiConfig = { [UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES]: true }; it('should return true/false for boolean fields', async () => { const [field] = stubFields.filter(({ type }) => type === 'boolean'); @@ -226,5 +231,66 @@ describe('FieldSuggestions', () => { expect(JSON.parse(callParams.body).filters).toHaveLength(1); expect(http.fetch).toHaveBeenCalled(); }); + + it('should use terms_enum', async () => { + uiConfig = { + [UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES]: true, + [UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD]: 'terms_enum', + }; + const [field] = stubFields.filter( + ({ type, aggregatable }) => type === 'string' && aggregatable + ); + + await getValueSuggestions({ + indexPattern: stubIndexPattern, + field, + query: '', + useTimeRange: true, + }); + const callParams = http.fetch.mock.calls[0][1]; + + expect(JSON.parse(callParams.body)).toHaveProperty('method', 'terms_enum'); + }); + + it('should use terms_agg', async () => { + uiConfig = { + [UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES]: true, + [UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD]: 'terms_agg', + }; + const [field] = stubFields.filter( + ({ type, aggregatable }) => type === 'string' && aggregatable + ); + + await getValueSuggestions({ + indexPattern: stubIndexPattern, + field, + query: '', + useTimeRange: true, + }); + const callParams = http.fetch.mock.calls[0][1]; + + expect(JSON.parse(callParams.body)).toHaveProperty('method', 'terms_agg'); + }); + + it('should use method passed in', async () => { + uiConfig = { + [UI_SETTINGS.FILTERS_EDITOR_SUGGEST_VALUES]: true, + [UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD]: 'terms_agg', + }; + const [field] = stubFields.filter( + ({ type, aggregatable }) => type === 'string' && aggregatable + ); + + await getValueSuggestions({ + indexPattern: stubIndexPattern, + field, + query: '', + useTimeRange: true, + method: 'terms_agg', + }); + const callParams = http.fetch.mock.calls[0][1]; + + expect(JSON.parse(callParams.body)).toHaveProperty('method', 'terms_agg'); + }); }); }); diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts index 05dc38b8e5ac5..7bbdae497644a 100644 --- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts +++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts @@ -67,7 +67,9 @@ export const setupValueSuggestionProvider = ( query: string, filters: any = [], signal?: AbortSignal, - method?: ValueSuggestionsMethod + method: ValueSuggestionsMethod = core.uiSettings.get( + UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD + ) ) => { usageCollector?.trackRequest(); return core.http diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 5a5872f3c7c20..8016f5c64a4c2 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2673,6 +2673,7 @@ export const UI_SETTINGS: { readonly FILTERS_PINNED_BY_DEFAULT: "filters:pinnedByDefault"; readonly FILTERS_EDITOR_SUGGEST_VALUES: "filterEditor:suggestValues"; readonly AUTOCOMPLETE_USE_TIMERANGE: "autocomplete:useTimeRange"; + readonly AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: "autocomplete:valueSuggestionMethod"; }; // Warning: (ae-missing-release-tag) "waitUntilNextSessionCompletes$" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.test.tsx b/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.test.tsx new file mode 100644 index 0000000000000..6f71a0dc7679d --- /dev/null +++ b/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { ReactElement } from 'react'; +import { mountWithIntl as mount } from '@kbn/test/jest'; +import { AutocompleteFtuePopover } from './autocomplete_ftue_popover'; +import { EuiTourStep } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { coreMock } from '../../../../../core/public/mocks'; +import { KibanaContextProvider } from '../../../../kibana_react/public'; +import { IStorageWrapper } from '../../../../kibana_utils/public'; +const startMock = coreMock.createStart(); + +describe('AutocompleteFtuePopover', () => { + function wrapInContext(props: { + isVisible?: boolean; + storage: IStorageWrapper; + children: ReactElement; + }) { + const services = { docLinks: startMock.docLinks }; + return ( + + + + ); + } + + const createMockStorage = () => ({ + get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + clear: jest.fn(), + }); + + it('should hide popover if local storage flag is set', () => { + const children = ; + const storage = createMockStorage(); + storage.get.mockReturnValue(true); + const instance = mount(wrapInContext({ storage, children })); + expect(instance.find(EuiTourStep).prop('isStepOpen')).toBe(false); + }); + + it('should render popover if local storage flag is not set', () => { + const children = ; + const instance = mount( + wrapInContext({ + storage: createMockStorage(), + isVisible: true, + children, + }) + ); + expect(instance.find(EuiTourStep).prop('isStepOpen')).toBe(true); + }); + + it('should hide popover if it is closed', async () => { + const props = { + children: , + showAutocompleteFtuePopover: true, + storage: createMockStorage(), + }; + const instance = mount(wrapInContext(props)); + act(() => { + instance.find(EuiTourStep).prop('closePopover')!(); + }); + expect(instance.find(EuiTourStep).prop('isStepOpen')).toBe(false); + }); + + it('should set local storage flag and hide on closing with button', () => { + const props = { + children: , + showAutocompleteFtuePopover: true, + storage: createMockStorage(), + }; + const instance = mount(wrapInContext(props)); + act(() => { + instance.find(EuiTourStep).prop('footerAction')!.props.onClick(); + }); + expect(props.storage.set).toHaveBeenCalledWith(expect.any(String), true); + expect(instance.find(EuiTourStep).prop('isStepOpen')).toBe(false); + }); +}); diff --git a/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.tsx b/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.tsx new file mode 100644 index 0000000000000..8f42e9f715369 --- /dev/null +++ b/src/plugins/data/public/ui/query_string_input/autocomplete_ftue_popover.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ReactElement, useEffect, useState } from 'react'; +import React from 'react'; +import { EuiButtonEmpty, EuiLink, EuiText, EuiTourStep } from '@elastic/eui'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useKibana } from '../../../../kibana_react/public'; + +const AUTOCOMPLETE_FTUE_POPOVER_STORAGE_KEY = 'data.autocompleteFtuePopover'; + +export function AutocompleteFtuePopover({ + isVisible, + storage, + children, +}: { + isVisible?: boolean; + storage: IStorageWrapper; + children: ReactElement; +}) { + const kibana = useKibana(); + const autocompleteChangesLink = kibana.services.docLinks!.links.query.autocompleteChanges; + + const [autocompleteFtuePopoverDismissed, setAutocompleteFtuePopoverDismissed] = useState(() => + Boolean(storage.get(AUTOCOMPLETE_FTUE_POPOVER_STORAGE_KEY)) + ); + const [autocompleteFtuePopoverVisible, setAutocompleteFtuePopoverVisible] = useState(false); + + useEffect(() => { + if (!autocompleteFtuePopoverDismissed && isVisible) { + setAutocompleteFtuePopoverVisible(true); + } + }, [autocompleteFtuePopoverDismissed, isVisible]); + + return ( + {}} + closePopover={() => { + setAutocompleteFtuePopoverVisible(false); + setAutocompleteFtuePopoverDismissed(true); + }} + content={ + + + {i18n.translate('data.autocompleteFtuePopover.learnMoreLink', { + defaultMessage: 'Learn more.', + })} + + ), + }} + /> + + } + minWidth={300} + anchorPosition="downCenter" + zIndex={4002} + anchorClassName="eui-displayBlock" + step={1} + stepsTotal={1} + isStepOpen={autocompleteFtuePopoverVisible} + subtitle={''} + title={i18n.translate('data.autocompleteFtuePopover.title', { + defaultMessage: 'Autocomplete is now faster!', + })} + footerAction={ + { + storage.set(AUTOCOMPLETE_FTUE_POPOVER_STORAGE_KEY, true); + setAutocompleteFtuePopoverDismissed(true); + setAutocompleteFtuePopoverVisible(false); + }} + > + {i18n.translate('data.autocompleteFtuePopover.dismissAction', { + defaultMessage: "Don't show again", + })} + + } + > +
{ + setAutocompleteFtuePopoverVisible(false); + }} + > + {children} +
+
+ ); +} diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index 88ccf6fe8a105..abb0de19698d7 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -26,6 +26,7 @@ import QueryStringInputUI from './query_string_input'; import { UI_SETTINGS } from '../../../common'; import { PersistedLog, getQueryLog } from '../../query'; import { NoDataPopover } from './no_data_popover'; +import { AutocompleteFtuePopover } from './autocomplete_ftue_popover'; const QueryStringInput = withKibana(QueryStringInputUI); @@ -169,25 +170,28 @@ export default function QueryBarTopRow(props: QueryBarTopRowProps) { function renderQueryInput() { if (!shouldRenderQueryInput()) return; + return ( - + + + ); } diff --git a/src/plugins/data/server/autocomplete/value_suggestions_route.ts b/src/plugins/data/server/autocomplete/value_suggestions_route.ts index ceddab4d90064..de76183da3be3 100644 --- a/src/plugins/data/server/autocomplete/value_suggestions_route.ts +++ b/src/plugins/data/server/autocomplete/value_suggestions_route.ts @@ -48,10 +48,7 @@ export function registerValueSuggestionsRoute(router: IRouter, config$: Observab const abortSignal = getRequestAbortedSignal(request.events.aborted$); try { - const fn = - (method ?? config.autocomplete.valueSuggestions.method) === 'terms_enum' - ? termsEnumSuggestions - : termsAggSuggestions; + const fn = method === 'terms_agg' ? termsAggSuggestions : termsEnumSuggestions; const body = await fn( config, context.core.savedObjects.client, diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 62bb802760d23..26983495b6d73 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -1474,6 +1474,7 @@ export const UI_SETTINGS: { readonly FILTERS_PINNED_BY_DEFAULT: "filters:pinnedByDefault"; readonly FILTERS_EDITOR_SUGGEST_VALUES: "filterEditor:suggestValues"; readonly AUTOCOMPLETE_USE_TIMERANGE: "autocomplete:useTimeRange"; + readonly AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: "autocomplete:valueSuggestionMethod"; }; // Warning: (ae-missing-release-tag) "usageProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts index e0d475e7124fd..0723c4b2d85c2 100644 --- a/src/plugins/data/server/ui_settings.ts +++ b/src/plugins/data/server/ui_settings.ts @@ -703,6 +703,30 @@ export function getUiSettings(): Record> { }), schema: schema.boolean(), }, + [UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD]: { + name: i18n.translate('data.advancedSettings.autocompleteValueSuggestionMethod', { + defaultMessage: 'Autocomplete value suggestion method', + }), + type: 'select', + value: 'terms_enum', + description: i18n.translate('data.advancedSettings.autocompleteValueSuggestionMethodText', { + defaultMessage: + 'The method used for querying suggestions for values in KQL autocomplete. Select terms_enum to use the ' + + 'Elasticsearch terms enum API for improved autocomplete suggestion performance. Select terms_agg to use an ' + + 'Elasticsearch terms aggregation. {learnMoreLink}', + values: { + learnMoreLink: + '' + + i18n.translate('data.advancedSettings.autocompleteValueSuggestionMethodLink', { + defaultMessage: 'Learn more.', + }) + + '', + }, + }), + options: ['terms_enum', 'terms_agg'], + category: ['autocomplete'], + schema: schema.string(), + }, [UI_SETTINGS.AUTOCOMPLETE_USE_TIMERANGE]: { name: i18n.translate('data.advancedSettings.autocompleteIgnoreTimerange', { defaultMessage: 'Use time range', @@ -711,8 +735,17 @@ export function getUiSettings(): Record> { value: true, description: i18n.translate('data.advancedSettings.autocompleteIgnoreTimerangeText', { defaultMessage: - 'Disable this property to get autocomplete suggestions from your full dataset, rather than from the current time range.', + 'Disable this property to get autocomplete suggestions from your full dataset, rather than from the current time range. {learnMoreLink}', + values: { + learnMoreLink: + '' + + i18n.translate('data.advancedSettings.autocompleteValueSuggestionMethodLearnMoreLink', { + defaultMessage: 'Learn more.', + }) + + '', + }, }), + category: ['autocomplete'], schema: schema.boolean(), }, [UI_SETTINGS.SEARCH_TIMEOUT]: { diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index e63b3e26fcbcd..6690e4fa57796 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -388,6 +388,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'autocomplete:valueSuggestionMethod': { + type: 'keyword', + _meta: { description: 'Non-default value of setting.' }, + }, 'search:timeout': { type: 'long', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 24df80f5e25a0..5d519c32ec4b9 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -25,6 +25,7 @@ export interface UsageStats { */ 'bfetch:disableCompression': boolean; 'autocomplete:useTimeRange': boolean; + 'autocomplete:valueSuggestionMethod': string; 'search:timeout': number; 'visualization:visualize:legacyChartsLibrary': boolean; 'visualization:visualize:legacyPieChartsLibrary': boolean; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index bf0b745f3ffad..952155ed1ffe6 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7723,6 +7723,12 @@ "description": "Non-default value of setting." } }, + "autocomplete:valueSuggestionMethod": { + "type": "keyword", + "_meta": { + "description": "Non-default value of setting." + } + }, "search:timeout": { "type": "long", "_meta": { diff --git a/test/functional/apps/dashboard/dashboard_state.ts b/test/functional/apps/dashboard/dashboard_state.ts index 6c259f5a71efa..ea2031f370eba 100644 --- a/test/functional/apps/dashboard/dashboard_state.ts +++ b/test/functional/apps/dashboard/dashboard_state.ts @@ -279,6 +279,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { let originalPieSliceStyle = ''; before(async () => { + await queryBar.clearQuery(); await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME); await enableNewChartLibraryDebug(); originalPieSliceStyle = await pieChart.getPieSliceStyle(`80,000`); diff --git a/test/functional/apps/discover/_discover.ts b/test/functional/apps/discover/_discover.ts index e567ec1b8d3d8..f837a96587c2c 100644 --- a/test/functional/apps/discover/_discover.ts +++ b/test/functional/apps/discover/_discover.ts @@ -176,6 +176,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); // reset to persisted state + await queryBar.clearQuery(); await PageObjects.discover.clickResetSavedSearchButton(); const expectedHitCount = '14,004'; await retry.try(async function () { diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 270117eed8499..bbe0ad8014ae7 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -2283,8 +2283,7 @@ "error_fix_action": { "type": "long", "_meta": { - "description": - "Number of times the user used the fix action of an error displayed in the workspace." + "description": "Number of times the user used the fix action of an error displayed in the workspace." } }, "open_formula_popover": { @@ -2531,8 +2530,7 @@ "error_fix_action": { "type": "long", "_meta": { - "description": - "Number of times the user used the fix action of an error displayed in the workspace." + "description": "Number of times the user used the fix action of an error displayed in the workspace." } }, "open_formula_popover": { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 25b27b3a2bf08..362b5c416bf19 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -696,7 +696,7 @@ "dashboard.unsavedChangesBadge": "保存されていない変更", "dashboard.urlWasRemovedInSixZeroWarningMessage": "URL「dashboard/create」は6.0で廃止されました。ブックマークを更新してください。", "data.advancedSettings.autocompleteIgnoreTimerange": "時間範囲を使用", - "data.advancedSettings.autocompleteIgnoreTimerangeText": "このプロパティを無効にすると、現在の時間範囲からではなく、データセットからオートコンプリートの候補を取得します。", + "data.advancedSettings.autocompleteIgnoreTimerangeText": "このプロパティを無効にすると、現在の時間範囲からではなく、データセットからオートコンプリートの候補を取得します。 {learnMoreLink}", "data.advancedSettings.courier.batchSearchesText": "Kibana は新しい非同期検索とインフラストラクチャを使用します。\n レガシー同期動作にフォールバックする場合は、このオプションを有効にします", "data.advancedSettings.courier.batchSearchesTextDeprecation": "この設定はサポートが終了し、Kibana 8.0 では削除されます。", "data.advancedSettings.courier.batchSearchesTitle": "同期検索を使用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 84a90eb81938f..1a99ec15fb008 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -699,7 +699,7 @@ "dashboard.unsavedChangesBadge": "未保存的更改", "dashboard.urlWasRemovedInSixZeroWarningMessage": "6.0 中已移除 url“dashboard/create”。请更新您的书签。", "data.advancedSettings.autocompleteIgnoreTimerange": "使用时间范围", - "data.advancedSettings.autocompleteIgnoreTimerangeText": "禁用此属性可从您的完全数据集中获取自动完成建议,而非从当前时间范围。", + "data.advancedSettings.autocompleteIgnoreTimerangeText": "禁用此属性可从您的完全数据集中获取自动完成建议,而非从当前时间范围。 {learnMoreLink}", "data.advancedSettings.courier.batchSearchesText": "Kibana 使用新的异步搜索和基础架构。\n 如果想回退到旧式同步操作,请启用此选项", "data.advancedSettings.courier.batchSearchesTextDeprecation": "此设置已过时,将在 Kibana 8.0 中移除。", "data.advancedSettings.courier.batchSearchesTitle": "使用同步搜索",