diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx index dce245e0ccb24..746f6e231abb0 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_input.tsx @@ -110,6 +110,7 @@ export class QueryBarInputUI extends Component { public inputRef: HTMLInputElement | null = null; private persistedLog: PersistedLog | undefined; + private abortController: AbortController | undefined; private services = this.props.kibana.services; private componentIsUnmounting = false; @@ -167,12 +168,22 @@ export class QueryBarInputUI extends Component { return; } - const suggestions: AutocompleteSuggestion[] = await getAutocompleteSuggestions({ - query: queryString, - selectionStart, - selectionEnd, - }); - return [...suggestions, ...recentSearchSuggestions]; + try { + if (this.abortController) this.abortController.abort(); + this.abortController = new AbortController(); + const suggestions: AutocompleteSuggestion[] = await getAutocompleteSuggestions({ + query: queryString, + selectionStart, + selectionEnd, + signal: this.abortController.signal, + }); + return [...suggestions, ...recentSearchSuggestions]; + } catch (e) { + // TODO: Waiting on https://github.com/elastic/kibana/issues/51406 for a properly typed error + // Ignore aborted requests + if (e.message === 'The user aborted a request.') return; + throw e; + } }; private getRecentSearchSuggestions = (query: string) => { diff --git a/src/plugins/data/public/autocomplete_provider/types.ts b/src/plugins/data/public/autocomplete_provider/types.ts index 3d34b1bc4a2d2..389057f94144d 100644 --- a/src/plugins/data/public/autocomplete_provider/types.ts +++ b/src/plugins/data/public/autocomplete_provider/types.ts @@ -40,6 +40,7 @@ export type GetSuggestions = (args: { query: string; selectionStart: number; selectionEnd: number; + signal?: AbortSignal; }) => Promise; /** @public **/ diff --git a/src/plugins/data/public/suggestions_provider/value_suggestions.ts b/src/plugins/data/public/suggestions_provider/value_suggestions.ts index 3bc1b45d87395..bff03f4cc1440 100644 --- a/src/plugins/data/public/suggestions_provider/value_suggestions.ts +++ b/src/plugins/data/public/suggestions_provider/value_suggestions.ts @@ -28,23 +28,36 @@ export function getSuggestionsProvider( http: HttpServiceBase ): IGetSuggestions { const requestSuggestions = memoize( - (index: string, field: IFieldType, query: string, boolFilter: any = []) => { + ( + index: string, + field: IFieldType, + query: string, + boolFilter: any = [], + signal?: AbortSignal + ) => { return http.fetch(`/api/kibana/suggestions/values/${index}`, { method: 'POST', body: JSON.stringify({ query, field: field.name, boolFilter }), + signal, }); }, resolver ); - return async (index: string, field: IFieldType, query: string, boolFilter?: any) => { + return async ( + index: string, + field: IFieldType, + query: string, + boolFilter?: any, + signal?: AbortSignal + ) => { const shouldSuggestValues = uiSettings.get('filterEditor:suggestValues'); if (field.type === 'boolean') { return [true, false]; } else if (!shouldSuggestValues || !field.aggregatable || field.type !== 'string') { return []; } - return await requestSuggestions(index, field, query, boolFilter); + return await requestSuggestions(index, field, query, boolFilter, signal); }; } diff --git a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/index.js b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/index.js index 8a82194470ace..ce688f9d6fb2a 100644 --- a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/index.js +++ b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/index.js @@ -22,7 +22,7 @@ export const kueryProvider = ({ config, indexPatterns, boolFilter }) => { return provider({ config, indexPatterns, boolFilter }); }); - return function getSuggestions({ query, selectionStart, selectionEnd }) { + return function getSuggestions({ query, selectionStart, selectionEnd, signal }) { const cursoredQuery = `${query.substr(0, selectionStart)}${cursorSymbol}${query.substr(selectionEnd)}`; let cursorNode; @@ -34,7 +34,7 @@ export const kueryProvider = ({ config, indexPatterns, boolFilter }) => { const { suggestionTypes = [] } = cursorNode; const suggestionsByType = suggestionTypes.map(type => { - return getSuggestionsByType[type](cursorNode); + return getSuggestionsByType[type](cursorNode, signal); }); return Promise.all(suggestionsByType) .then(suggestionsByType => dedup(flatten(suggestionsByType))); diff --git a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/value.js b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/value.js index 52bf347a94075..88e2fe812faa6 100644 --- a/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/value.js +++ b/x-pack/legacy/plugins/kuery_autocomplete/public/autocomplete_providers/value.js @@ -27,14 +27,14 @@ export function getSuggestionsProvider({ indexPatterns, boolFilter }) { suffix, fieldName, nestedPath, - }) { + }, signal) { const fullFieldName = nestedPath ? `${nestedPath}.${fieldName}` : fieldName; const fields = allFields.filter(field => field.name === fullFieldName); const query = `${prefix}${suffix}`.trim(); const { getSuggestions } = npStart.plugins.data; const suggestionsByField = fields.map(field => { - return getSuggestions(field.indexPatternTitle, field, query, boolFilter).then(data => { + return getSuggestions(field.indexPatternTitle, field, query, boolFilter, signal).then(data => { const quotedValues = data.map(value => typeof value === 'string' ? `"${escapeQuotes(value)}"` : `${value}`); return wrapAsSuggestions(start, end, query, quotedValues); });