Skip to content

Commit

Permalink
[data.search.autocomplete] Move autocomplete method to UI settings (#…
Browse files Browse the repository at this point in the history
…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 <[email protected]>

* Update docs/concepts/index.asciidoc

Co-authored-by: gchaps <[email protected]>

* 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 <[email protected]>
Co-authored-by: Liza K <[email protected]>

Co-authored-by: Lukas Olson <[email protected]>
Co-authored-by: gchaps <[email protected]>
Co-authored-by: Liza K <[email protected]>
  • Loading branch information
4 people authored Jul 28, 2021
1 parent 395428e commit 163eca9
Show file tree
Hide file tree
Showing 27 changed files with 363 additions and 38 deletions.
8 changes: 8 additions & 0 deletions docs/concepts/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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 <<advanced-options, Advanced Settings>>, and set `autocomplete:valueSuggestionMethod` to `terms_agg`.

[float]
==== Additional filters with AND

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ readonly links: {
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
```
12 changes: 10 additions & 2 deletions docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<<kibana-concepts-searching-your-data, Learn more>>.

[[autocomplete-usetimerange]]`autocomplete:useTimeRange`::
Disable this property to get autocomplete suggestions from your full dataset, rather than from the current time range. <<kibana-concepts-searching-your-data, Learn more>>.

[[filters-pinnedbydefault]]`filters:pinnedByDefault`::
Set this property to `true` to make filters have a global state (be pinned) by
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down Expand Up @@ -579,6 +580,7 @@ export interface DocLinksStart {
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
Expand Down
1 change: 1 addition & 0 deletions src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ export interface DocLinksStart {
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
3 changes: 0 additions & 3 deletions src/plugins/data/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any> = {};
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, {
Expand All @@ -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,
Expand All @@ -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');
Expand Down Expand Up @@ -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');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ export const setupValueSuggestionProvider = (
query: string,
filters: any = [],
signal?: AbortSignal,
method?: ValueSuggestionsMethod
method: ValueSuggestionsMethod = core.uiSettings.get<ValueSuggestionsMethod>(
UI_SETTINGS.AUTOCOMPLETE_VALUE_SUGGESTION_METHOD
)
) => {
usageCollector?.trackRequest();
return core.http
Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<KibanaContextProvider services={services}>
<AutocompleteFtuePopover {...props} />
</KibanaContextProvider>
);
}

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 = <span />;
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 = <span />;
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: <span />,
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: <span />,
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);
});
});
Loading

0 comments on commit 163eca9

Please sign in to comment.