From 808a89c6abe460a64ddf9233d7c58e28731cdf14 Mon Sep 17 00:00:00 2001 From: Davis McPhee Date: Sat, 18 May 2024 00:36:20 -0300 Subject: [PATCH] [Discover] Rely on `dataSource` for ES|QL mode (#183108) ## Summary This PR updates Discover to rely on `dataSource` for determining if ES|QL mode is enabled instead of relying on `query` directly. This creates a clearer separation between the modes and moves Discover toward supporting multiple data source types. It also includes a number of cleanups around ES|QL mode that make the intent of the code clearer and removes tech debt / code duplication. Changes included in the PR: - Add a new `useIsEsqlMode` hook that relies on `dataSource` for checking if Discover is in ES|QL mode. - Remove "raw record type" concept in Discover and replace it with ES|QL `dataSource` checks. - Remove references to `isPlainRecord` and replace them with ES|QL `dataSource` checks. - Remove `isTextBasedQuery` utility function and replace it with ES|QL `dataSource` checks or `isOfAggregateQueryType` where casting is necessary. - Replace references to `isOfAggregateQueryType` with ES|QL `dataSource` checks except where casting is necessary. - Replace other references to "text based" with "ES|QL" for clarity and consistency. Closes #181963. ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../src/utils/is_legacy_table_enabled.ts | 6 +- .../common/utils/sorting/get_default_sort.ts | 8 +- .../discover/common/utils/sorting/get_sort.ts | 28 +++--- .../sorting/get_sort_for_search_source.ts | 2 +- .../document_explorer_update_callout.test.tsx | 10 +- .../components/layout/discover_documents.tsx | 46 ++++----- .../layout/discover_histogram_layout.test.tsx | 11 +-- .../layout/discover_histogram_layout.tsx | 15 ++- .../layout/discover_layout.test.tsx | 5 - .../components/layout/discover_layout.tsx | 91 +++++++++--------- .../layout/discover_main_content.test.tsx | 17 ++-- .../layout/discover_main_content.tsx | 17 ++-- .../selected_vs_available_callout.test.tsx | 56 ++++++----- .../layout/selected_vs_available_callout.tsx | 21 ++-- .../layout/use_discover_histogram.test.tsx | 16 ++-- .../layout/use_discover_histogram.ts | 74 +++++++-------- .../layout/use_fetch_more_records.test.tsx | 95 ++++++++++--------- .../layout/use_fetch_more_records.ts | 7 +- .../components/no_results/no_results.test.tsx | 24 +++-- .../no_results_suggestions.tsx | 16 +--- .../discover_sidebar_responsive.test.tsx | 32 +++---- .../sidebar/discover_sidebar_responsive.tsx | 19 ++-- .../components/sidebar/lib/get_field_list.ts | 8 +- .../sidebar/lib/sidebar_reducer.test.ts | 20 ++-- .../components/sidebar/lib/sidebar_reducer.ts | 14 +-- .../components/top_nav/discover_topnav.tsx | 29 +++--- .../top_nav/get_top_nav_links.test.ts | 4 +- .../components/top_nav/get_top_nav_links.tsx | 10 +- .../components/top_nav/on_save_search.tsx | 14 ++- .../top_nav/open_alerts_popover.test.tsx | 4 +- .../top_nav/open_alerts_popover.tsx | 18 ++-- .../components/top_nav/use_discover_topnav.ts | 10 +- .../main/data_fetching/fetch_all.test.ts | 67 +++++-------- .../main/data_fetching/fetch_all.ts | 58 +++++------ .../{fetch_text_based.ts => fetch_esql.ts} | 26 ++--- .../application/main/discover_main_app.tsx | 5 +- .../application/main/discover_main_route.tsx | 9 +- .../main/hooks/use_adhoc_data_views.ts | 8 +- ...nguage.test.tsx => use_esql_mode.test.tsx} | 41 ++------ ...sed_query_language.ts => use_esql_mode.ts} | 16 ++-- ...ehavior_subject.ts => use_is_esql_mode.ts} | 16 ++-- .../hooks/use_saved_search_messages.test.ts | 8 +- .../main/hooks/use_saved_search_messages.ts | 58 ++--------- .../discover_data_state_container.test.ts | 15 +-- .../discover_data_state_container.ts | 39 +++----- .../utils/build_state_subscribe.ts | 24 ++--- .../utils/cleanup_url_state.ts | 16 ++-- ...ang.test.ts => get_esql_data_view.test.ts} | 12 +-- ...ed_query_lang.ts => get_esql_data_view.ts} | 2 +- .../utils/get_state_defaults.test.ts | 14 +-- .../utils/get_state_defaults.ts | 18 ++-- .../utils/get_switch_data_view_app_state.ts | 6 +- .../utils/load_saved_search.ts | 15 +-- .../utils/resolve_data_view.ts | 12 +-- .../utils/update_saved_search.ts | 11 ++- .../main/utils/get_raw_record_type.test.ts | 23 ----- .../main/utils/get_raw_record_type.ts | 18 ---- .../main/utils/get_valid_view_mode.test.ts | 14 +-- .../main/utils/get_valid_view_mode.ts | 10 +- .../main/utils/is_text_based_query.test.ts | 17 ---- .../main/utils/is_text_based_query.ts | 18 ---- .../discover/public/application/types.ts | 4 +- .../discover_grid_flyout.test.tsx | 2 +- .../discover_grid_flyout.tsx | 16 ++-- .../discover_tour/discover_tour.test.tsx | 6 +- .../discover_tour/discover_tour_provider.tsx | 14 +-- .../doc_table/components/table_row.test.tsx | 4 +- .../doc_table/components/table_row.tsx | 8 +- .../components/table_row_details.tsx | 12 +-- .../doc_table/create_doc_table_embeddable.tsx | 2 +- .../doc_table/doc_table_embeddable.tsx | 2 +- .../doc_table/doc_table_wrapper.tsx | 10 +- .../view_mode_toggle.test.tsx | 8 +- .../view_mode_toggle/view_mode_toggle.tsx | 10 +- .../embeddable/saved_search_embeddable.tsx | 36 +++---- .../saved_search_embeddable_component.tsx | 7 +- .../public/utils/get_sharing_data.test.ts | 2 +- .../discover/public/utils/get_sharing_data.ts | 4 +- .../discover/public/utils/sorting/get_sort.ts | 6 +- .../components/doc_viewer_source/source.tsx | 2 +- .../unified_doc_viewer/public/plugin.tsx | 2 +- .../apps/discover/group3/_lens_vis.ts | 2 +- .../apps/discover/group3/_panels_toggle.ts | 6 +- .../apps/discover/group6/_sidebar.ts | 6 +- .../discover/group6/_sidebar_field_stats.ts | 2 +- .../discover/group6/_time_field_column.ts | 22 ++--- .../apps/discover/group6/_view_mode_toggle.ts | 4 +- .../translations/translations/fr-FR.json | 6 +- .../translations/translations/ja-JP.json | 6 +- .../translations/translations/zh-CN.json | 6 +- .../apps/discover/visualize_field.ts | 6 +- 91 files changed, 667 insertions(+), 899 deletions(-) rename src/plugins/discover/public/application/main/data_fetching/{fetch_text_based.ts => fetch_esql.ts} (80%) rename src/plugins/discover/public/application/main/hooks/{use_test_based_query_language.test.tsx => use_esql_mode.test.tsx} (87%) rename src/plugins/discover/public/application/main/hooks/{use_text_based_query_language.ts => use_esql_mode.ts} (90%) rename src/plugins/discover/public/application/main/hooks/{use_behavior_subject.ts => use_is_esql_mode.ts} (50%) rename src/plugins/discover/public/application/main/state_management/utils/{get_data_view_by_text_based_query_lang.test.ts => get_esql_data_view.test.ts} (81%) rename src/plugins/discover/public/application/main/state_management/utils/{get_data_view_by_text_based_query_lang.ts => get_esql_data_view.ts} (96%) delete mode 100644 src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts delete mode 100644 src/plugins/discover/public/application/main/utils/get_raw_record_type.ts delete mode 100644 src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts delete mode 100644 src/plugins/discover/public/application/main/utils/is_text_based_query.ts diff --git a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts b/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts index 7e575cf80dbfb..8aabd92201664 100644 --- a/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts +++ b/packages/kbn-discover-utils/src/utils/is_legacy_table_enabled.ts @@ -11,12 +11,12 @@ import { DOC_TABLE_LEGACY } from '../constants'; export function isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode, + isEsqlMode, }: { uiSettings: IUiSettingsClient; - isTextBasedQueryMode: boolean; + isEsqlMode: boolean; }): boolean { - if (isTextBasedQueryMode) { + if (isEsqlMode) { return false; // only show the new data grid } diff --git a/src/plugins/discover/common/utils/sorting/get_default_sort.ts b/src/plugins/discover/common/utils/sorting/get_default_sort.ts index 5e4e38f2d82b9..8a0fb63323803 100644 --- a/src/plugins/discover/common/utils/sorting/get_default_sort.ts +++ b/src/plugins/discover/common/utils/sorting/get_default_sort.ts @@ -12,21 +12,21 @@ import { isSortable } from './get_sort'; /** * use in case the user didn't manually sort. - * the default sort is returned depending on the data view or non for text based queries + * the default sort is returned depending on the data view or non for ES|QL queries */ export function getDefaultSort( dataView: DataView | undefined, defaultSortOrder: string = 'desc', hidingTimeColumn: boolean = false, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { - if (isTextBasedQueryMode) { + if (isEsqlMode) { return []; } if ( dataView?.timeFieldName && - isSortable(dataView.timeFieldName, dataView, isTextBasedQueryMode) && + isSortable(dataView.timeFieldName, dataView, isEsqlMode) && !hidingTimeColumn ) { return [[dataView.timeFieldName, defaultSortOrder]]; diff --git a/src/plugins/discover/common/utils/sorting/get_sort.ts b/src/plugins/discover/common/utils/sorting/get_sort.ts index 82a2f801906cd..f0a9c7b5b2265 100644 --- a/src/plugins/discover/common/utils/sorting/get_sort.ts +++ b/src/plugins/discover/common/utils/sorting/get_sort.ts @@ -14,14 +14,10 @@ export type SortPairObj = Record; export type SortPair = SortOrder | SortPairObj; export type SortInput = SortPair | SortPair[]; -export function isSortable( - fieldName: string, - dataView: DataView, - isTextBasedQueryMode: boolean -): boolean { - if (isTextBasedQueryMode) { - // in-memory sorting is used for text-based queries - // would be great to have a way to determine if a text-based column is sortable +export function isSortable(fieldName: string, dataView: DataView, isEsqlMode: boolean): boolean { + if (isEsqlMode) { + // in-memory sorting is used for ES|QL queries + // would be great to have a way to determine if a ES|QL column is sortable return fieldName !== '_source'; } const field = dataView.getFieldByName(fieldName); @@ -31,18 +27,18 @@ export function isSortable( function createSortObject( sortPair: SortInput, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortPairObj | undefined { if ( Array.isArray(sortPair) && sortPair.length === 2 && - isSortable(String(sortPair[0]), dataView, isTextBasedQueryMode) + isSortable(String(sortPair[0]), dataView, isEsqlMode) ) { const [field, direction] = sortPair as SortOrder; return { [field]: direction }; } else if ( isPlainObject(sortPair) && - isSortable(Object.keys(sortPair)[0], dataView, isTextBasedQueryMode) + isSortable(Object.keys(sortPair)[0], dataView, isEsqlMode) ) { return sortPair as SortPairObj; } @@ -59,13 +55,13 @@ export function isLegacySort(sort: SortPair[] | SortPair): sort is SortPair { * @param {array} sort two dimensional array [[fieldToSort, directionToSort]] * or an array of objects [{fieldToSort: directionToSort}] * @param {object} dataView used for determining default sort - * @param {boolean} isTextBasedQueryMode + * @param {boolean} isEsqlMode * @returns Array<{object}> an array of sort objects */ export function getSort( sort: SortPair[] | SortPair, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortPairObj[] { if (Array.isArray(sort)) { if (isLegacySort(sort)) { @@ -73,7 +69,7 @@ export function getSort( return [{ [sort[0]]: sort[1] }]; } return sort - .map((sortPair: SortPair) => createSortObject(sortPair, dataView, isTextBasedQueryMode)) + .map((sortPair: SortPair) => createSortObject(sortPair, dataView, isEsqlMode)) .filter((sortPairObj) => typeof sortPairObj === 'object') as SortPairObj[]; } return []; @@ -86,9 +82,9 @@ export function getSort( export function getSortArray( sort: SortInput, dataView: DataView, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { - return getSort(sort, dataView, isTextBasedQueryMode).reduce((acc: SortOrder[], sortPair) => { + return getSort(sort, dataView, isEsqlMode).reduce((acc: SortOrder[], sortPair) => { const entries = Object.entries(sortPair); if (entries && entries[0]) { acc.push(entries[0]); diff --git a/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts b/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts index d63c75a4f0150..e19372e9f505e 100644 --- a/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts +++ b/src/plugins/discover/common/utils/sorting/get_sort_for_search_source.ts @@ -46,7 +46,7 @@ export function getSortForSearchSource({ } const { timeFieldName } = dataView; - const sortPairs = getSort(sort, dataView, false); // text based request is not using search source + const sortPairs = getSort(sort, dataView, false); // ES|QL request is not using search source const sortForSearchSource = sortPairs.map((sortPair: Record) => { if (timeFieldName && sortPair[timeFieldName]) { diff --git a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx b/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx index 3b46dbe1b8bca..d5d46870ef33f 100644 --- a/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx +++ b/src/plugins/discover/public/application/main/components/document_explorer_callout/document_explorer_update_callout.test.tsx @@ -17,6 +17,8 @@ import { LocalStorageMock } from '../../../../__mocks__/local_storage_mock'; import { DiscoverServices } from '../../../../build_services'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { DiscoverTourProvider } from '../../../../components/discover_tour'; +import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; const defaultServices = { ...discoverServiceMock, @@ -62,9 +64,11 @@ describe('Document Explorer Update callout', () => { it('should start a tour when the button is clicked', () => { const result = mountWithIntl( - - - + + + + + ); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index b5f50f2ca9c14..f382f61275b67 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -46,7 +46,6 @@ import { useInternalStateSelector } from '../../state_management/discover_intern import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FetchStatus } from '../../../types'; -import { RecordRawType } from '../../state_management/discover_data_state_container'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { useDataState } from '../../hooks/use_data_state'; import { DocTableInfinite } from '../../../../components/doc_table/doc_table_infinite'; @@ -56,7 +55,6 @@ import { DISCOVER_TOUR_STEP_ANCHOR_IDS, DiscoverTourProvider, } from '../../../../components/discover_tour'; -import { getRawRecordType } from '../../utils/get_raw_record_type'; import { getMaxAllowedSampleSize, getAllowedSampleSize, @@ -68,6 +66,7 @@ import { SelectedVSAvailableCallout } from './selected_vs_available_callout'; import { useDiscoverCustomization } from '../../../../customizations'; import { onResizeGridColumn } from '../../../../utils/on_resize_grid_column'; import { useContextualGridCustomisations } from '../../hooks/grid_customisations'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const containerStyles = css` position: relative; @@ -123,12 +122,12 @@ function DiscoverDocumentsComponent({ ]; }); const expandedDoc = useInternalStateSelector((state) => state.expandedDoc); - const isTextBasedQuery = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]); + const isEsqlMode = useIsEsqlMode(); const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]); const hideAnnouncements = useMemo(() => uiSettings.get(HIDE_ANNOUNCEMENTS), [uiSettings]); const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), - [uiSettings, isTextBasedQuery] + () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), + [uiSettings, isEsqlMode] ); const documentState = useDataState(documents$); const isDataLoading = @@ -136,7 +135,7 @@ function DiscoverDocumentsComponent({ documentState.fetchStatus === FetchStatus.PARTIAL; // This is needed to prevent EuiDataGrid pushing onSort because the data view has been switched. - // It's just necessary for non-text-based query lang requests since they don't have a partial result state, that's + // It's just necessary for non ES|QL requests since they don't have a partial result state, that's // considered as loading state in the Component. // 1. When switching the data view, the sorting in the URL is reset to the default sorting of the selected data view. // 2. The new sort param is already available in this component and propagated to the EuiDataGrid. @@ -145,13 +144,12 @@ function DiscoverDocumentsComponent({ // 5. this is propagated to Discover's URL and causes an unwanted change of state to an unsorted state // This solution switches to the loading state in this component when the URL index doesn't match the dataView.id const isDataViewLoading = - useInternalStateSelector((state) => state.isDataViewLoading) && !isTextBasedQuery; + useInternalStateSelector((state) => state.isDataViewLoading) && !isEsqlMode; const isEmptyDataResult = - isTextBasedQuery || !documentState.result || documentState.result.length === 0; + isEsqlMode || !documentState.result || documentState.result.length === 0; const rows = useMemo(() => documentState.result || [], [documentState.result]); const { isMoreDataLoading, totalHits, onFetchMoreRecords } = useFetchMoreRecords({ - isTextBasedQuery, stateContainer, }); @@ -227,10 +225,10 @@ function DiscoverDocumentsComponent({ const columnsMeta: DataTableColumnsMeta | undefined = useMemo( () => - documentState.textBasedQueryColumns - ? getTextBasedColumnsMeta(documentState.textBasedQueryColumns) + documentState.esqlQueryColumns + ? getTextBasedColumnsMeta(documentState.esqlQueryColumns) : undefined, - [documentState.textBasedQueryColumns] + [documentState.esqlQueryColumns] ); const renderDocumentView = useCallback( @@ -269,19 +267,13 @@ function DiscoverDocumentsComponent({ () => ( <> ), - [ - isTextBasedQuery, - currentColumns, - documents?.textBasedQueryColumns, - documentState.interceptedWarnings, - ] + [currentColumns, documents?.esqlQueryColumns, documentState.interceptedWarnings] ); const gridAnnouncementCallout = useMemo(() => { @@ -289,12 +281,12 @@ function DiscoverDocumentsComponent({ return null; } - return !isTextBasedQuery ? ( - + return !isEsqlMode ? ( + ) : null; - }, [hideAnnouncements, isLegacy, isTextBasedQuery]); + }, [hideAnnouncements, isLegacy, isEsqlMode]); const loadingIndicator = useMemo( () => @@ -364,12 +356,12 @@ function DiscoverDocumentsComponent({ isLoading={isDataLoading} searchDescription={savedSearch.description} sharedItemTitle={savedSearch.title} - isPlainRecord={isTextBasedQuery} + isEsqlMode={isEsqlMode} onAddColumn={onAddColumn} onFilter={onAddFilter as DocViewFilterFn} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} - onSort={!isTextBasedQuery ? onSort : undefined} + onSort={!isEsqlMode ? onSort : undefined} useNewFieldsApi={useNewFieldsApi} dataTestSubj="discoverDocTable" /> @@ -415,12 +407,12 @@ function DiscoverDocumentsComponent({ rowHeightState={rowHeight} onUpdateRowHeight={onUpdateRowHeight} isSortEnabled={true} - isPlainRecord={isTextBasedQuery} + isPlainRecord={isEsqlMode} rowsPerPageState={rowsPerPage ?? getDefaultRowsPerPage(services.uiSettings)} onUpdateRowsPerPage={onUpdateRowsPerPage} maxAllowedSampleSize={getMaxAllowedSampleSize(services.uiSettings)} sampleSizeState={getAllowedSampleSize(sampleSizeState, services.uiSettings)} - onUpdateSampleSize={!isTextBasedQuery ? onUpdateSampleSize : undefined} + onUpdateSampleSize={!isEsqlMode ? onUpdateSampleSize : undefined} onFieldEdited={onFieldEdited} configRowHeight={uiSettings.get(ROW_HEIGHT_OPTION)} showMultiFields={uiSettings.get(SHOW_MULTIFIELDS)} diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx index 7ce8f987e5911..d23dfbe4359eb 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.test.tsx @@ -17,7 +17,6 @@ import { DataDocuments$, DataMain$, DataTotalHits$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { discoverServiceMock } from '../../../../__mocks__/services'; import { FetchStatus, SidebarToggleState } from '../../../types'; @@ -52,12 +51,12 @@ function getStateContainer(savedSearch?: SavedSearch) { } const mountComponent = async ({ - isPlainRecord = false, + isEsqlMode = false, storage, savedSearch = savedSearchMockWithTimeField, searchSessionId = '123', }: { - isPlainRecord?: boolean; + isEsqlMode?: boolean; isTimeBased?: boolean; storage?: Storage; savedSearch?: SavedSearch; @@ -86,7 +85,6 @@ const mountComponent = async ({ const main$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: isPlainRecord ? RecordRawType.PLAIN : RecordRawType.DOCUMENT, foundDocuments: true, }) as DataMain$; @@ -121,7 +119,6 @@ const mountComponent = async ({ stateContainer.actions.undoSavedSearchChanges = jest.fn(); const props: DiscoverHistogramLayoutProps = { - isPlainRecord, dataView, stateContainer, onFieldEdited: jest.fn(), @@ -176,8 +173,8 @@ describe('Discover histogram layout component', () => { expect(component.isEmptyRender()).toBe(false); }, 10000); - it('should not render null if there is no search session, but isPlainRecord is true', async () => { - const { component } = await mountComponent({ isPlainRecord: true }); + it('should not render null if there is no search session, but isEsqlMode is true', async () => { + const { component } = await mountComponent({ isEsqlMode: true }); expect(component.isEmptyRender()).toBe(false); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx index 207a2263b0e06..68585d7faf5c0 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx @@ -15,6 +15,7 @@ import { useDiscoverHistogram } from './use_discover_histogram'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import { FetchStatus } from '../../../types'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; export interface DiscoverHistogramLayoutProps extends DiscoverMainContentProps { container: HTMLElement | null; @@ -25,7 +26,6 @@ const histogramLayoutCss = css` `; export const DiscoverHistogramLayout = ({ - isPlainRecord, dataView, stateContainer, container, @@ -35,11 +35,11 @@ export const DiscoverHistogramLayout = ({ const { dataState } = stateContainer; const searchSessionId = useObservable(stateContainer.searchSessionManager.searchSessionId$); const hideChart = useAppStateSelector((state) => state.hideChart); + const isEsqlMode = useIsEsqlMode(); const unifiedHistogramProps = useDiscoverHistogram({ stateContainer, inspectorAdapters: dataState.inspectorAdapters, hideChart, - isPlainRecord, }); const datatable = useObservable(dataState.data$.documents$); @@ -53,21 +53,21 @@ export const DiscoverHistogramLayout = ({ const table: Datatable | undefined = useMemo(() => { if ( - isPlainRecord && + isEsqlMode && datatable && [FetchStatus.PARTIAL, FetchStatus.COMPLETE].includes(datatable.fetchStatus) ) { return { type: 'datatable' as 'datatable', rows: datatable.result!.map((r) => r.raw), - columns: datatable.textBasedQueryColumns || [], + columns: datatable.esqlQueryColumns || [], }; } - }, [datatable, isPlainRecord]); + }, [datatable, isEsqlMode]); // Initialized when the first search has been requested or - // when in text-based mode since search sessions are not supported - if (!searchSessionId && !isPlainRecord) { + // when in ES|QL mode since search sessions are not supported + if (!searchSessionId && !isEsqlMode) { return null; } @@ -86,7 +86,6 @@ export const DiscoverHistogramLayout = ({ {...mainContentProps} stateContainer={stateContainer} dataView={dataView} - isPlainRecord={isPlainRecord} panelsToggle={panelsToggle} /> diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx index c1cc257f58cc3..cf3fd26a10d66 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.test.tsx @@ -25,7 +25,6 @@ import { DataDocuments$, DataMain$, DataTotalHits$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; import { FetchStatus } from '../../../types'; @@ -51,10 +50,8 @@ async function mountComponent( prevSidebarClosed?: boolean, mountOptions: { attachTo?: HTMLElement } = {}, query?: Query | AggregateQuery, - isPlainRecord?: boolean, main$: DataMain$ = new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: isPlainRecord ? RecordRawType.PLAIN : RecordRawType.DOCUMENT, foundDocuments: true, }) as DataMain$ ) { @@ -185,10 +182,8 @@ describe('Discover component', () => { undefined, undefined, undefined, - undefined, new BehaviorSubject({ fetchStatus: FetchStatus.ERROR, - recordRawType: RecordRawType.DOCUMENT, foundDocuments: false, error: new Error('No results'), }) as DataMain$ diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 0a7524ef8bddf..cc141ce3fd567 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import './discover_layout.scss'; import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { @@ -23,7 +24,7 @@ import { METRIC_TYPE } from '@kbn/analytics'; import classNames from 'classnames'; import { generateFilters } from '@kbn/data-plugin/public'; import { useDragDropContext } from '@kbn/dom-drag-drop'; -import { DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; +import { DataViewType } from '@kbn/data-views-plugin/public'; import { SEARCH_FIELDS_FROM_SOURCE, SHOW_FIELD_STATISTICS, @@ -44,10 +45,9 @@ import { DiscoverSidebarResponsive } from '../sidebar'; import { DiscoverTopNav } from '../top_nav/discover_topnav'; import { getResultState } from '../../utils/get_result_state'; import { DiscoverUninitialized } from '../uninitialized/uninitialized'; -import { DataMainMsg, RecordRawType } from '../../state_management/discover_data_state_container'; +import { DataMainMsg } from '../../state_management/discover_data_state_container'; import { FetchStatus, SidebarToggleState } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; -import { getRawRecordType } from '../../utils/get_raw_record_type'; import { SavedSearchURLConflictCallout } from '../../../../components/saved_search_url_conflict_callout/saved_search_url_conflict_callout'; import { DiscoverHistogramLayout } from './discover_histogram_layout'; import { ErrorCallout } from '../../../../components/common/error_callout'; @@ -55,6 +55,7 @@ import { addLog } from '../../../../utils/add_log'; import { DiscoverResizableLayout } from './discover_resizable_layout'; import { PanelsToggle, PanelsToggleProps } from '../../../../components/panels_toggle'; import { sendErrorMsg } from '../../hooks/use_saved_search_messages'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const SidebarMemoized = React.memo(DiscoverSidebarResponsive); const TopNavMemoized = React.memo(DiscoverTopNav); @@ -84,9 +85,9 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { state.columns, state.sort, ]); - const isPlainRecord = useMemo(() => getRawRecordType(query) === RecordRawType.PLAIN, [query]); + const isEsqlMode = useIsEsqlMode(); const viewMode: VIEW_MODE = useAppStateSelector((state) => { - if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true || isPlainRecord) + if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true || isEsqlMode) return VIEW_MODE.DOCUMENT_LEVEL; return state.viewMode ?? VIEW_MODE.DOCUMENT_LEVEL; }); @@ -140,13 +141,16 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { useEffect(() => { return observabilityAIAssistant?.service.setScreenContext({ screenDescription: `The user is looking at the Discover view on the ${ - isPlainRecord ? 'ES|QL' : 'dataView' + isEsqlMode ? 'ES|QL' : 'dataView' } mode. The index pattern is the ${dataView.getIndexPattern()}`, }); - }, [dataView, isPlainRecord, observabilityAIAssistant?.service]); + }, [dataView, isEsqlMode, observabilityAIAssistant?.service]); - const onAddFilter = useCallback( - (field: DataViewField | string, values: unknown, operation: '+' | '-') => { + const onAddFilter = useCallback( + (field, values, operation) => { + if (!field) { + return; + } const fieldName = typeof field === 'string' ? field : field.name; popularizeField(dataView, fieldName, dataViews, capabilities); const newFilters = generateFilters(filterManager, field, values, operation, dataView); @@ -173,36 +177,35 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { return operation; }; - const onPopulateWhereClause = useCallback( - (field: DataViewField | string, values: unknown, operation: '+' | '-') => { - if (query && isOfAggregateQueryType(query) && 'esql' in query) { - const fieldName = typeof field === 'string' ? field : field.name; - // send the field type for casting - const fieldType = typeof field !== 'string' ? field.type : undefined; - // weird existence logic from Discover components - // in the field it comes the operator _exists_ and in the value the field - // I need to take care of it here but I think it should be handled on the fieldlist instead - const updatedQuery = appendWhereClauseToESQLQuery( - query.esql, - fieldName === '_exists_' ? String(values) : fieldName, - fieldName === '_exists_' || values == null ? undefined : values, - getOperator(fieldName, values, operation), - fieldType - ); - data.query.queryString.setQuery({ - esql: updatedQuery, - }); - if (trackUiMetric) { - trackUiMetric(METRIC_TYPE.CLICK, 'esql_filter_added'); - } + const onPopulateWhereClause = useCallback( + (field, values, operation) => { + if (!field || !isOfAggregateQueryType(query)) { + return; + } + const fieldName = typeof field === 'string' ? field : field.name; + // send the field type for casting + const fieldType = typeof field !== 'string' ? field.type : undefined; + // weird existence logic from Discover components + // in the field it comes the operator _exists_ and in the value the field + // I need to take care of it here but I think it should be handled on the fieldlist instead + const updatedQuery = appendWhereClauseToESQLQuery( + query.esql, + fieldName === '_exists_' ? String(values) : fieldName, + fieldName === '_exists_' || values == null ? undefined : values, + getOperator(fieldName, values, operation), + fieldType + ); + data.query.queryString.setQuery({ + esql: updatedQuery, + }); + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'esql_filter_added'); } }, [data.query.queryString, query, trackUiMetric] ); - const onFilter = isPlainRecord - ? (onPopulateWhereClause as DocViewFilterFn) - : (onAddFilter as DocViewFilterFn); + const onFilter = isEsqlMode ? onPopulateWhereClause : onAddFilter; const onFieldEdited = useCallback( async ({ removedFieldName }: { removedFieldName?: string } = {}) => { @@ -227,17 +230,17 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { const contentCentered = resultState === 'uninitialized' || resultState === 'none'; const documentState = useDataState(stateContainer.dataState.data$.documents$); - const textBasedLanguageModeWarning = useMemo(() => { - if (isPlainRecord) { - return documentState.textBasedHeaderWarning; + const esqlModeWarning = useMemo(() => { + if (isEsqlMode) { + return documentState.esqlHeaderWarning; } - }, [documentState.textBasedHeaderWarning, isPlainRecord]); + }, [documentState.esqlHeaderWarning, isEsqlMode]); - const textBasedLanguageModeErrors = useMemo(() => { - if (isPlainRecord) { + const esqlModeErrors = useMemo(() => { + if (isEsqlMode) { return dataState.error; } - }, [dataState.error, isPlainRecord]); + }, [dataState.error, isEsqlMode]); const [sidebarContainer, setSidebarContainer] = useState(null); const [mainContainer, setMainContainer] = useState(null); @@ -277,7 +280,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { return ( <> { describe('DocumentViewModeToggle', () => { - it('should show DocumentViewModeToggle when isPlainRecord is false', async () => { + it('should show DocumentViewModeToggle when not in ES|QL mode', async () => { const component = await mountComponent(); expect(component.find(DiscoverDocuments).prop('viewModeToggle')).toBeDefined(); }); - it('should include DocumentViewModeToggle when isPlainRecord is true', async () => { - const component = await mountComponent({ isPlainRecord: true }); + it('should include DocumentViewModeToggle when in ES|QL mode', async () => { + const component = await mountComponent({ isEsqlMode: true }); expect(component.find(DiscoverDocuments).prop('viewModeToggle')).toBeDefined(); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx index 701bbdddb6e93..23e4001a39459 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_main_content.tsx @@ -22,6 +22,7 @@ import { DiscoverDocuments } from './discover_documents'; import { DOCUMENTS_VIEW_CLICK, FIELD_STATISTICS_VIEW_CLICK } from '../field_stats_table/constants'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; import type { PanelsToggleProps } from '../../../../components/panels_toggle'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const DROP_PROPS = { value: { @@ -38,7 +39,6 @@ const DROP_PROPS = { export interface DiscoverMainContentProps { dataView: DataView; - isPlainRecord: boolean; stateContainer: DiscoverStateContainer; viewMode: VIEW_MODE; onAddFilter: DocViewFilterFn | undefined; @@ -51,7 +51,6 @@ export interface DiscoverMainContentProps { export const DiscoverMainContent = ({ dataView, - isPlainRecord, viewMode, onAddFilter, onFieldEdited, @@ -62,7 +61,7 @@ export const DiscoverMainContent = ({ isChartAvailable, }: DiscoverMainContentProps) => { const { trackUiMetric, dataVisualizer: dataVisualizerService } = useDiscoverServices(); - + const isEsqlMode = useIsEsqlMode(); const shouldShowViewModeToggle = dataVisualizerService !== undefined; const setDiscoverViewMode = useCallback( @@ -86,7 +85,7 @@ export const DiscoverMainContent = ({ return shouldShowViewModeToggle ? ( ); }, [ + shouldShowViewModeToggle, viewMode, - setDiscoverViewMode, - isPlainRecord, + isEsqlMode, stateContainer, + setDiscoverViewMode, panelsToggle, isChartAvailable, - shouldShowViewModeToggle, ]); const showChart = useAppStateSelector((state) => !state.hideChart); @@ -132,7 +131,7 @@ export const DiscoverMainContent = ({ dataView={dataView} onAddFilter={onAddFilter} stateContainer={stateContainer} - onFieldEdited={!isPlainRecord ? onFieldEdited : undefined} + onFieldEdited={!isEsqlMode ? onFieldEdited : undefined} /> ) : ( <> @@ -141,7 +140,7 @@ export const DiscoverMainContent = ({ dataView={dataView} columns={columns} stateContainer={stateContainer} - onAddFilter={!isPlainRecord ? onAddFilter : undefined} + onAddFilter={!isEsqlMode ? onAddFilter : undefined} trackUiMetric={trackUiMetric} /> diff --git a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx index 4b6b2da23b2c2..b48d12d511abb 100644 --- a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.test.tsx @@ -10,45 +10,49 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { SelectedVSAvailableCallout } from './selected_vs_available_callout'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; +import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; describe('SelectedVSAvailableCallout', () => { - it('should render the callout if isPlainRecord is true and the selected columns are less than the available ones', async () => { + it('should render the callout if in ES|QL mode and the selected columns are less than the available ones', async () => { + const stateContainer = getDiscoverStateMock({}); + stateContainer.appState.update({ query: { esql: 'select *' } }); const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(true); }); - it('should not render the callout if isPlainRecord is false', async () => { + it('should not render the callout if not in ES|QL mode', async () => { const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(false); }); - it('should not render the callout if isPlainRecord is true but the selected columns are equal with the available ones', async () => { + it('should not render the callout if in ES|QL mode but the selected columns are equal with the available ones', async () => { const component = mountWithIntl( - + + + ); expect(component.find('[data-test-subj="dscSelectedColumnsCallout"]').exists()).toBe(false); }); diff --git a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx index ec05667e69b35..948f4cf27a50e 100644 --- a/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/selected_vs_available_callout.tsx @@ -9,33 +9,34 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiCallOut } from '@elastic/eui'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; interface SelectedVSAvailableCallout { - isPlainRecord: boolean; selectedColumns: string[]; - textBasedQueryColumns?: DatatableColumn[]; + esqlQueryColumns?: DatatableColumn[]; } export const SelectedVSAvailableCallout = ({ - isPlainRecord, - textBasedQueryColumns, + esqlQueryColumns, selectedColumns, }: SelectedVSAvailableCallout) => { + const isEsqlMode = useIsEsqlMode(); + return ( <> - {isPlainRecord && - textBasedQueryColumns && + {isEsqlMode && + esqlQueryColumns && selectedColumns.length > 0 && - selectedColumns.length < textBasedQueryColumns.length && ( + selectedColumns.length < esqlQueryColumns.length && ( { stateContainer = getStateContainer(), inspectorAdapters = { requests: new RequestAdapter() }, hideChart = false, - isPlainRecord = false, }: { stateContainer?: DiscoverStateContainer; inspectorAdapters?: InspectorAdapters; hideChart?: boolean; - isPlainRecord?: boolean; } = {}) => { const initialProps = { stateContainer, inspectorAdapters, hideChart, - isPlainRecord, }; const Wrapper: WrapperComponent = ({ children }) => ( @@ -174,8 +171,10 @@ describe('useDiscoverHistogram', () => { ]); }); - it('should return the isChartLoading params for text based languages', async () => { - const { hook } = await renderUseDiscoverHistogram({ isPlainRecord: true }); + it('should return the isChartLoading params for ES|QL mode', async () => { + const stateContainer = getStateContainer(); + stateContainer.appState.update({ query: { esql: 'from *' } }); + const { hook } = await renderUseDiscoverHistogram(); const isChartLoading = hook.result.current.isChartLoading; expect(isChartLoading).toBe(false); }); @@ -323,7 +322,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ fetchStatus: FetchStatus.COMPLETE, result: 100, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); act(() => { hook.result.current.ref(api); @@ -331,7 +329,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.COMPLETE, result: 100, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); expect(mockCheckHitCount).toHaveBeenCalledWith(stateContainer.dataState.data$.main$, 100); }); @@ -370,7 +367,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).not.toEqual({ fetchStatus: FetchStatus.ERROR, error, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); act(() => { hook.result.current.ref(api); @@ -379,7 +375,6 @@ describe('useDiscoverHistogram', () => { expect(stateContainer.dataState.data$.totalHits$.value).toEqual({ fetchStatus: FetchStatus.ERROR, error, - recordRawType: stateContainer.dataState.data$.totalHits$.value.recordRawType, }); expect(mockCheckHitCount).not.toHaveBeenCalled(); }); @@ -393,8 +388,9 @@ describe('useDiscoverHistogram', () => { searchSessionId: string; }>(); const stateContainer = getStateContainer(); + stateContainer.appState.update({ query: { esql: 'from *' } }); stateContainer.dataState.fetch$ = fetch$; - const { hook } = await renderUseDiscoverHistogram({ stateContainer, isPlainRecord: true }); + const { hook } = await renderUseDiscoverHistogram({ stateContainer }); act(() => { fetch$.next({ options: { reset: false, fetchMore: false }, diff --git a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts index bf5fbafa95794..385dd432dc3c1 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_discover_histogram.ts @@ -31,7 +31,7 @@ import useObservable from 'react-use/lib/useObservable'; import type { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { SavedSearch } from '@kbn/saved-search-plugin/common'; -import type { Filter } from '@kbn/es-query'; +import { Filter } from '@kbn/es-query'; import { useDiscoverCustomization } from '../../../../customizations'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FetchStatus } from '../../../types'; @@ -41,31 +41,28 @@ import type { DiscoverStateContainer } from '../../state_management/discover_sta import { addLog } from '../../../../utils/add_log'; import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; import type { DiscoverAppState } from '../../state_management/discover_app_state_container'; -import { - DataDocumentsMsg, - RecordRawType, -} from '../../state_management/discover_data_state_container'; +import { DataDocumentsMsg } from '../../state_management/discover_data_state_container'; import { useSavedSearch } from '../../state_management/discover_state_provider'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; -const EMPTY_TEXT_BASED_COLUMNS: DatatableColumn[] = []; +const EMPTY_ESQL_COLUMNS: DatatableColumn[] = []; const EMPTY_FILTERS: Filter[] = []; export interface UseDiscoverHistogramProps { stateContainer: DiscoverStateContainer; inspectorAdapters: InspectorAdapters; hideChart: boolean | undefined; - isPlainRecord: boolean; } export const useDiscoverHistogram = ({ stateContainer, inspectorAdapters, hideChart, - isPlainRecord, }: UseDiscoverHistogramProps) => { const services = useDiscoverServices(); const savedSearchData$ = stateContainer.dataState.data$; const savedSearchState = useSavedSearch(); + const isEsqlMode = useIsEsqlMode(); /** * API initialization @@ -163,10 +160,8 @@ export const useDiscoverHistogram = ({ useEffect(() => { const subscription = createTotalHitsObservable(unifiedHistogram?.state$)?.subscribe( ({ status, result }) => { - const { recordRawType, result: totalHitsResult } = savedSearchData$.totalHits$.getValue(); - - if (recordRawType === RecordRawType.PLAIN) { - // ignore histogram's total hits updates for text-based records as Discover manages them during docs fetching + if (isEsqlMode) { + // ignore histogram's total hits updates for ES|QL as Discover manages them during docs fetching return; } @@ -176,6 +171,8 @@ export const useDiscoverHistogram = ({ return; } + const { result: totalHitsResult } = savedSearchData$.totalHits$.getValue(); + if ( (status === UnifiedHistogramFetchStatus.loading || status === UnifiedHistogramFetchStatus.uninitialized) && @@ -190,7 +187,6 @@ export const useDiscoverHistogram = ({ savedSearchData$.totalHits$.next({ fetchStatus: status.toString() as FetchStatus, result, - recordRawType, }); if (status !== UnifiedHistogramFetchStatus.complete || typeof result !== 'number') { @@ -206,9 +202,11 @@ export const useDiscoverHistogram = ({ subscription?.unsubscribe(); }; }, [ + isEsqlMode, savedSearchData$.main$, savedSearchData$.totalHits$, setTotalHitsError, + stateContainer.appState, unifiedHistogram?.state$, ]); @@ -224,30 +222,30 @@ export const useDiscoverHistogram = ({ timefilter.getTime() ); - // When in text based language mode, update the data view, query, and + // When in ES|QL mode, update the data view, query, and // columns only when documents are done fetching so the Lens suggestions // don't frequently change, such as when the user modifies the table // columns, which would trigger unnecessary refetches. - const textBasedFetchComplete$ = useMemo( + const esqlFetchComplete$ = useMemo( () => createFetchCompleteObservable(stateContainer), [stateContainer] ); - const [initialTextBasedProps] = useState(() => - getUnifiedHistogramPropsForTextBased({ + const [initialEsqlProps] = useState(() => + getUnifiedHistogramPropsForEsql({ documentsValue: savedSearchData$.documents$.getValue(), savedSearch: stateContainer.savedSearchState.getState(), }) ); const { - dataView: textBasedDataView, - query: textBasedQuery, - columns: textBasedColumns, - } = useObservable(textBasedFetchComplete$, initialTextBasedProps); + dataView: esqlDataView, + query: esqlQuery, + columns: esqlColumns, + } = useObservable(esqlFetchComplete$, initialEsqlProps); useEffect(() => { - if (!isPlainRecord) { + if (!isEsqlMode) { return; } @@ -256,7 +254,7 @@ export const useDiscoverHistogram = ({ setIsSuggestionLoading(true); } }); - const fetchComplete = textBasedFetchComplete$.subscribe(() => { + const fetchComplete = esqlFetchComplete$.subscribe(() => { setIsSuggestionLoading(false); }); @@ -264,7 +262,7 @@ export const useDiscoverHistogram = ({ fetchStart.unsubscribe(); fetchComplete.unsubscribe(); }; - }, [isPlainRecord, stateContainer.dataState.fetch$, textBasedFetchComplete$]); + }, [isEsqlMode, stateContainer.dataState.fetch$, esqlFetchComplete$]); /** * Data fetching @@ -290,17 +288,17 @@ export const useDiscoverHistogram = ({ let fetch$: Observable; - // When in text based language mode, we refetch under two conditions: + // When in ES|QL mode, we refetch under two conditions: // 1. When the current Lens suggestion changes. This syncs the visualization // with the user's selection. // 2. When the documents are done fetching. This is necessary because we don't // have access to the latest columns until after the documents are fetched, // which are required to get the latest Lens suggestion, which would trigger // a refetch anyway and result in multiple unnecessary fetches. - if (isPlainRecord) { + if (isEsqlMode) { fetch$ = merge( createCurrentSuggestionObservable(unifiedHistogram.state$).pipe(map(() => 'lens')), - textBasedFetchComplete$.pipe(map(() => 'discover')) + esqlFetchComplete$.pipe(map(() => 'discover')) ).pipe(debounceTime(50)); } else { fetch$ = stateContainer.dataState.fetch$.pipe( @@ -320,14 +318,14 @@ export const useDiscoverHistogram = ({ }); // triggering the initial request for total hits hook - if (!isPlainRecord && !skipRefetch.current) { + if (!isEsqlMode && !skipRefetch.current) { unifiedHistogram.refetch(); } return () => { subscription.unsubscribe(); }; - }, [isPlainRecord, stateContainer.dataState.fetch$, textBasedFetchComplete$, unifiedHistogram]); + }, [isEsqlMode, stateContainer.dataState.fetch$, esqlFetchComplete$, unifiedHistogram]); const dataView = useInternalStateSelector((state) => state.dataView!); @@ -384,12 +382,12 @@ export const useDiscoverHistogram = ({ ref, getCreationOptions, services, - dataView: isPlainRecord ? textBasedDataView : dataView, - query: isPlainRecord ? textBasedQuery : query, + dataView: isEsqlMode ? esqlDataView : dataView, + query: isEsqlMode ? esqlQuery : query, filters: filtersMemoized, timeRange: timeRangeMemoized, relativeTimeRange, - columns: isPlainRecord ? textBasedColumns : undefined, + columns: isEsqlMode ? esqlColumns : undefined, onFilter: histogramCustomization?.onFilter, onBrushEnd: histogramCustomization?.onBrushEnd, withDefaultActions: histogramCustomization?.withDefaultActions, @@ -397,10 +395,10 @@ export const useDiscoverHistogram = ({ isChartLoading: isSuggestionLoading, // visContext should be in sync with current query externalVisContext: - isPlainRecord && canImportVisContext(savedSearchState?.visContext) + isEsqlMode && canImportVisContext(savedSearchState?.visContext) ? savedSearchState?.visContext : undefined, - onVisContextChanged: isPlainRecord ? onVisContextChanged : undefined, + onVisContextChanged: isEsqlMode ? onVisContextChanged : undefined, }; }; @@ -476,7 +474,7 @@ const createFetchCompleteObservable = (stateContainer: DiscoverStateContainer) = distinctUntilChanged((prev, curr) => prev.fetchStatus === curr.fetchStatus), filter(({ fetchStatus }) => [FetchStatus.COMPLETE, FetchStatus.ERROR].includes(fetchStatus)), map((documentsValue) => { - return getUnifiedHistogramPropsForTextBased({ + return getUnifiedHistogramPropsForEsql({ documentsValue, savedSearch: stateContainer.savedSearchState.getState(), }); @@ -498,14 +496,14 @@ const createCurrentSuggestionObservable = (state$: Observable { const records = esHitsMockWithSort.map((hit) => buildDataTableRecord(hit, dataViewMock)); @@ -43,18 +46,27 @@ describe('useFetchMoreRecords', () => { return stateContainer; }; + const getWrapper = ( + stateContainer: DiscoverStateContainer + ): WrapperComponent => { + return ({ children }) => ( + + <>{children} + + ); + }; + it('should not be allowed if all records are already loaded', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 3, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 3, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); @@ -63,17 +75,16 @@ describe('useFetchMoreRecords', () => { }); it('should be allowed when there are more records to load', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeDefined(); expect(current.isMoreDataLoading).toBe(false); @@ -81,17 +92,16 @@ describe('useFetchMoreRecords', () => { }); it('should not be allowed when there is no initial documents', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 0, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 0, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); expect(current.isMoreDataLoading).toBe(false); @@ -99,35 +109,34 @@ describe('useFetchMoreRecords', () => { }); it('should return loading status correctly', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.LOADING_MORE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: false, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.LOADING_MORE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeDefined(); expect(current.isMoreDataLoading).toBe(true); expect(current.totalHits).toBe(5); }); - it('should not be allowed for text-based queries', async () => { + it('should not be allowed for ES|QL queries', async () => { + const stateContainer = getStateContainer({ + fetchStatus: FetchStatus.COMPLETE, + loadedRecordsCount: 3, + totalRecordsCount: 5, + }); + stateContainer.appState.update({ query: { esql: 'from *' } }); const { result: { current }, } = renderHook((props) => useFetchMoreRecords(props), { - initialProps: { - isTextBasedQuery: true, - stateContainer: getStateContainer({ - fetchStatus: FetchStatus.COMPLETE, - loadedRecordsCount: 3, - totalRecordsCount: 5, - }), - }, + wrapper: getWrapper(stateContainer), + initialProps: { stateContainer }, }); expect(current.onFetchMoreRecords).toBeUndefined(); }); diff --git a/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts b/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts index 381ded3fc17d7..b6901c4f29369 100644 --- a/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts +++ b/src/plugins/discover/public/application/main/components/layout/use_fetch_more_records.ts @@ -9,13 +9,13 @@ import { useMemo } from 'react'; import { FetchStatus } from '../../../types'; import { useDataState } from '../../hooks/use_data_state'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; /** * Params for the hook */ export interface UseFetchMoreRecordsParams { - isTextBasedQuery: boolean; stateContainer: DiscoverStateContainer; } @@ -30,24 +30,23 @@ export interface UseFetchMoreRecordsResult { /** * Checks if more records can be loaded and returns a handler for it - * @param isTextBasedQuery * @param stateContainer */ export const useFetchMoreRecords = ({ - isTextBasedQuery, stateContainer, }: UseFetchMoreRecordsParams): UseFetchMoreRecordsResult => { const documents$ = stateContainer.dataState.data$.documents$; const totalHits$ = stateContainer.dataState.data$.totalHits$; const documentState = useDataState(documents$); const totalHitsState = useDataState(totalHits$); + const isEsqlMode = useIsEsqlMode(); const rows = documentState.result || []; const isMoreDataLoading = documentState.fetchStatus === FetchStatus.LOADING_MORE; const totalHits = totalHitsState.result || 0; const canFetchMoreRecords = - !isTextBasedQuery && + !isEsqlMode && rows.length > 0 && totalHits > rows.length && Boolean(rows[rows.length - 1].raw.sort?.length); diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx index d88eda61d7db0..d1079c5bddf1f 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results.test.tsx @@ -21,6 +21,7 @@ import { type Filter } from '@kbn/es-query'; import { DiscoverNoResults, DiscoverNoResultsProps } from './no_results'; import { createDiscoverServicesMock } from '../../../../__mocks__/services'; import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; +import { DiscoverMainProvider } from '../../state_management/discover_state_provider'; jest.spyOn(RxApi, 'lastValueFrom').mockImplementation(async () => ({ rawResponse: { @@ -61,25 +62,28 @@ async function mountAndFindSubjects( > ) { const isTimeBased = props.dataView.isTimeBased(); + const stateContainer = getDiscoverStateMock({ isTimeBased }); let component: ReactWrapper; - await act(async () => { - component = await mountWithIntl( + act(() => { + component = mountWithIntl( - {}} - {...props} - /> + + {}} + {...props} + /> + ); }); await new Promise((resolve) => setTimeout(resolve, 0)); - await act(async () => { - await component!.update(); + act(() => { + component!.update(); }); return { diff --git a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx index f75376d6ad32c..fa679fa00ecef 100644 --- a/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx +++ b/src/plugins/discover/public/application/main/components/no_results/no_results_suggestions/no_results_suggestions.tsx @@ -10,16 +10,9 @@ import React, { useCallback, useState } from 'react'; import { css } from '@emotion/react'; import { EuiEmptyPrompt, EuiButton, EuiSpacer, useEuiTheme } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { - isOfQueryType, - isOfAggregateQueryType, - type Query, - type AggregateQuery, - type Filter, -} from '@kbn/es-query'; +import { isOfQueryType, type Query, type AggregateQuery, type Filter } from '@kbn/es-query'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { isTextBasedQuery } from '../../../utils/is_text_based_query'; import { NoResultsSuggestionDefault } from './no_results_suggestion_default'; import { NoResultsSuggestionWhenFilters, @@ -31,6 +24,7 @@ import { hasActiveFilter } from '../../layout/utils'; import { useDiscoverServices } from '../../../../../hooks/use_discover_services'; import { useFetchOccurrencesRange, TimeRangeExtendingStatus } from './use_fetch_occurances_range'; import { NoResultsIllustration } from './assets/no_results_illustration'; +import { useIsEsqlMode } from '../../../hooks/use_is_esql_mode'; interface NoResultsSuggestionProps { dataView: DataView; @@ -50,8 +44,8 @@ export const NoResultsSuggestions: React.FC = ({ const { euiTheme } = useEuiTheme(); const services = useDiscoverServices(); const { data, uiSettings, timefilter, toastNotifications } = services; - const hasQuery = - (isOfQueryType(query) && !!query?.query) || (!!query && isOfAggregateQueryType(query)); + const isEsqlMode = useIsEsqlMode(); + const hasQuery = Boolean(isOfQueryType(query) && query.query) || isEsqlMode; const hasFilters = hasActiveFilter(filters); const [timeRangeExtendingStatus, setTimeRangeExtendingStatus] = @@ -148,7 +142,7 @@ export const NoResultsSuggestions: React.FC = ({ } body={body} actions={ - !isTextBasedQuery(query) && isTimeBased ? ( + !isEsqlMode && isTimeBased ? (
{ - const propsWithTextBasedMode = { + const propsWithEsqlMode = { ...props, columns: ['extension', 'bytes'], onAddFilter: undefined, documents$: new BehaviorSubject({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.PLAIN, result: getDataTableRecords(stubLogstashDataView), - textBasedQueryColumns: [ + esqlQueryColumns: [ { id: '1', name: 'extension', meta: { type: 'text' } }, { id: '2', name: 'bytes', meta: { type: 'number' } }, { id: '3', name: '@timestamp', meta: { type: 'date' } }, ], }) as DataDocuments$, }; - const compInTextBasedMode = await mountComponent(propsWithTextBasedMode, { + const compInEsqlMode = await mountComponent(propsWithEsqlMode, { query: { esql: 'FROM `index`' }, }); await act(async () => { await new Promise((resolve) => setTimeout(resolve, 0)); - compInTextBasedMode.update(); + compInEsqlMode.update(); }); - expect(findTestSubject(compInTextBasedMode, 'indexPattern-add-field_btn').length).toBe(0); + expect(findTestSubject(compInEsqlMode, 'indexPattern-add-field_btn').length).toBe(0); const popularFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedPopularFields-count' ); const selectedFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedSelectedFields-count' ); const availableFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedAvailableFields-count' ); - const emptyFieldsCount = findTestSubject( - compInTextBasedMode, - 'fieldListGroupedEmptyFields-count' - ); - const metaFieldsCount = findTestSubject( - compInTextBasedMode, - 'fieldListGroupedMetaFields-count' - ); + const emptyFieldsCount = findTestSubject(compInEsqlMode, 'fieldListGroupedEmptyFields-count'); + const metaFieldsCount = findTestSubject(compInEsqlMode, 'fieldListGroupedMetaFields-count'); const unmappedFieldsCount = findTestSubject( - compInTextBasedMode, + compInEsqlMode, 'fieldListGroupedUnmappedFields-count' ); @@ -561,7 +553,7 @@ describe('discover responsive sidebar', function () { expect(mockCalcFieldCounts.mock.calls.length).toBe(0); - expect(findTestSubject(compInTextBasedMode, 'fieldListGrouped__ariaDescription').text()).toBe( + expect(findTestSubject(compInEsqlMode, 'fieldListGrouped__ariaDescription').text()).toBe( '2 selected fields. 3 available fields.' ); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index 67c7bd28f0b0b..23778ada7566a 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -26,7 +26,6 @@ import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { AvailableFields$, DataDocuments$, - RecordRawType, } from '../../state_management/discover_data_state_container'; import { calcFieldCounts } from '../../utils/calc_field_counts'; import { FetchStatus, SidebarToggleState } from '../../../types'; @@ -39,6 +38,7 @@ import { } from './lib/sidebar_reducer'; import { useDiscoverCustomization } from '../../../../customizations'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; const EMPTY_FIELD_COUNTS = {}; @@ -151,6 +151,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) useState(null); const { euiTheme } = useEuiTheme(); const services = useDiscoverServices(); + const isEsqlMode = useIsEsqlMode(); const { fieldListVariant, selectedDataView, @@ -174,8 +175,6 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) useEffect(() => { const subscription = props.documents$.subscribe((documentState) => { - const isPlainRecordType = documentState.recordRawType === RecordRawType.PLAIN; - switch (documentState?.fetchStatus) { case FetchStatus.UNINITIALIZED: dispatchSidebarStateAction({ @@ -189,7 +188,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) dispatchSidebarStateAction({ type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: isPlainRecordType, + isEsqlMode, }, }); break; @@ -198,11 +197,9 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { dataView: selectedDataViewRef.current, - fieldCounts: isPlainRecordType - ? EMPTY_FIELD_COUNTS - : calcFieldCounts(documentState.result), - textBasedQueryColumns: documentState.textBasedQueryColumns, - isPlainRecord: isPlainRecordType, + fieldCounts: isEsqlMode ? EMPTY_FIELD_COUNTS : calcFieldCounts(documentState.result), + esqlQueryColumns: documentState.esqlQueryColumns, + isEsqlMode, }, }); break; @@ -212,7 +209,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) payload: { dataView: selectedDataViewRef.current, fieldCounts: EMPTY_FIELD_COUNTS, - isPlainRecord: isPlainRecordType, + isEsqlMode, }, }); break; @@ -221,7 +218,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) } }); return () => subscription.unsubscribe(); - }, [props.documents$, dispatchSidebarStateAction, selectedDataViewRef]); + }, [props.documents$, dispatchSidebarStateAction, selectedDataViewRef, isEsqlMode]); useEffect(() => { if (selectedDataView !== selectedDataViewRef.current) { diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts index 071454b0bd579..99d911dd14f61 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts @@ -61,13 +61,11 @@ export function getDataViewFieldList( return [...dataViewFields, ...unknownFields]; } -export function getTextBasedQueryFieldList( - textBasedQueryColumns?: DatatableColumn[] -): DataViewField[] { - if (!textBasedQueryColumns) { +export function getEsqlQueryFieldList(esqlQueryColumns?: DatatableColumn[]): DataViewField[] { + if (!esqlQueryColumns) { return []; } - return textBasedQueryColumns.map( + return esqlQueryColumns.map( (column) => new DataViewField({ name: column.name, diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts index 8c6ce4888fb47..d1135192091bf 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.test.ts @@ -40,7 +40,7 @@ describe('sidebar reducer', function () { const resultForDocuments = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: false, + isEsqlMode: false, }, }); expect(resultForDocuments).toEqual( @@ -51,13 +51,13 @@ describe('sidebar reducer', function () { status: DiscoverSidebarReducerStatus.PROCESSING, }) ); - const resultForTextBasedQuery = discoverSidebarReducer(state, { + const resultForEsqlQuery = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING, payload: { - isPlainRecord: true, + isEsqlMode: true, }, }); - expect(resultForTextBasedQuery).toEqual( + expect(resultForEsqlQuery).toEqual( expect.objectContaining({ dataView, allFields: null, @@ -75,7 +75,7 @@ describe('sidebar reducer', function () { const resultForDocuments = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: false, + isEsqlMode: false, dataView: stubDataViewWithoutTimeField, fieldCounts, }, @@ -96,13 +96,13 @@ describe('sidebar reducer', function () { status: DiscoverSidebarReducerStatus.COMPLETED, }); - const resultForTextBasedQuery = discoverSidebarReducer(state, { + const resultForEsqlQuery = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: true, + isEsqlMode: true, dataView: stubDataViewWithoutTimeField, fieldCounts: {}, - textBasedQueryColumns: [ + esqlQueryColumns: [ { id: '1', name: 'text1', @@ -122,7 +122,7 @@ describe('sidebar reducer', function () { ] as DatatableColumn[], }, }); - expect(resultForTextBasedQuery).toStrictEqual({ + expect(resultForEsqlQuery).toStrictEqual({ dataView: stubDataViewWithoutTimeField, allFields: [ new DataViewField({ @@ -149,7 +149,7 @@ describe('sidebar reducer', function () { const resultWhileLoading = discoverSidebarReducer(state, { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED, payload: { - isPlainRecord: false, + isEsqlMode: false, dataView: stubDataViewWithoutTimeField, fieldCounts: null, }, diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts index 54e9a2c95ce12..e771ed9a19a52 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/sidebar_reducer.ts @@ -8,7 +8,7 @@ import { type DataView, type DataViewField } from '@kbn/data-views-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { getDataViewFieldList, getTextBasedQueryFieldList } from './get_field_list'; +import { getDataViewFieldList, getEsqlQueryFieldList } from './get_field_list'; export enum DiscoverSidebarReducerActionType { RESET = 'RESET', @@ -33,15 +33,15 @@ type DiscoverSidebarReducerAction = | { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADING; payload: { - isPlainRecord: boolean; + isEsqlMode: boolean; }; } | { type: DiscoverSidebarReducerActionType.DOCUMENTS_LOADED; payload: { fieldCounts: DiscoverSidebarReducerState['fieldCounts']; - textBasedQueryColumns?: DatatableColumn[]; // from text-based searches - isPlainRecord: boolean; + esqlQueryColumns?: DatatableColumn[]; // from ES|QL searches + isEsqlMode: boolean; dataView: DataView | null | undefined; }; }; @@ -92,12 +92,12 @@ export function discoverSidebarReducer( return { ...state, fieldCounts: null, - allFields: action.payload.isPlainRecord ? null : state.allFields, + allFields: action.payload.isEsqlMode ? null : state.allFields, status: DiscoverSidebarReducerStatus.PROCESSING, }; case DiscoverSidebarReducerActionType.DOCUMENTS_LOADED: - const mappedAndUnmappedFields = action.payload.isPlainRecord - ? getTextBasedQueryFieldList(action.payload.textBasedQueryColumns) + const mappedAndUnmappedFields = action.payload.isEsqlMode + ? getEsqlQueryFieldList(action.payload.esqlQueryColumns) : getDataViewFieldList(action.payload.dataView, action.payload.fieldCounts); return { ...state, diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 61acdd1875a14..d05226a0098ce 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -23,14 +23,14 @@ import { onSaveSearch } from './on_save_search'; import { useDiscoverCustomization } from '../../../../customizations'; import { addLog } from '../../../../utils/add_log'; import { useAppStateSelector } from '../../state_management/discover_app_state_container'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { useDiscoverTopNav } from './use_discover_topnav'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; export interface DiscoverTopNavProps { savedQuery?: string; stateContainer: DiscoverStateContainer; - textBasedLanguageModeErrors?: Error; - textBasedLanguageModeWarning?: string; + esqlModeErrors?: Error; + esqlModeWarning?: string; onFieldEdited: () => Promise; isLoading?: boolean; onCancelClick?: () => void; @@ -39,8 +39,8 @@ export interface DiscoverTopNavProps { export const DiscoverTopNav = ({ savedQuery, stateContainer, - textBasedLanguageModeErrors, - textBasedLanguageModeWarning, + esqlModeErrors, + esqlModeWarning, onFieldEdited, isLoading, onCancelClick, @@ -60,14 +60,13 @@ export const DiscoverTopNav = ({ const dataView = useInternalStateSelector((state) => state.dataView!); const savedDataViews = useInternalStateSelector((state) => state.savedDataViews); const savedSearch = useSavedSearchInitial(); + const isEsqlMode = useIsEsqlMode(); const showDatePicker = useMemo(() => { - // always show the timepicker for text based languages - const isTextBased = isTextBasedQuery(query); + // always show the timepicker for ES|QL mode return ( - isTextBased || - (!isTextBased && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) + isEsqlMode || (!isEsqlMode && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) ); - }, [dataView, query]); + }, [dataView, isEsqlMode]); const closeFieldEditor = useRef<() => void | undefined>(); const closeDataViewEditor = useRef<() => void | undefined>(); @@ -151,7 +150,7 @@ export const DiscoverTopNav = ({ } }; - const onTextBasedSavedAndExit = useCallback( + const onEsqlSavedAndExit = useCallback( ({ onSave, onCancel }) => { onSaveSearch({ savedSearch: stateContainer.savedSearchState.getState(), @@ -257,11 +256,9 @@ export const DiscoverTopNav = ({ shouldHideDefaultDataviewPicker ? undefined : dataViewPickerProps } displayStyle="detached" - textBasedLanguageModeErrors={ - textBasedLanguageModeErrors ? [textBasedLanguageModeErrors] : undefined - } - textBasedLanguageModeWarning={textBasedLanguageModeWarning} - onTextBasedSavedAndExit={onTextBasedSavedAndExit} + textBasedLanguageModeErrors={esqlModeErrors ? [esqlModeErrors] : undefined} + textBasedLanguageModeWarning={esqlModeWarning} + onTextBasedSavedAndExit={onEsqlSavedAndExit} prependFilterBar={ searchBarCustomization?.PrependFilterBar ? ( diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 9b5451558529f..2faf12d62bf0a 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -27,7 +27,7 @@ test('getTopNavLinks result', () => { onOpenInspector: jest.fn(), services, state, - isTextBased: false, + isEsqlMode: false, adHocDataViews: [], topNavCustomization: undefined, }); @@ -80,7 +80,7 @@ test('getTopNavLinks result for ES|QL mode', () => { onOpenInspector: jest.fn(), services, state, - isTextBased: true, + isEsqlMode: true, adHocDataViews: [], topNavCustomization: undefined, }); diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx index ce1592c1598d3..a25e10ce3b0be 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx @@ -28,7 +28,7 @@ export const getTopNavLinks = ({ services, state, onOpenInspector, - isTextBased, + isEsqlMode, adHocDataViews, topNavCustomization, }: { @@ -36,7 +36,7 @@ export const getTopNavLinks = ({ services: DiscoverServices; state: DiscoverStateContainer; onOpenInspector: () => void; - isTextBased: boolean; + isEsqlMode: boolean; adHocDataViews: DataView[]; topNavCustomization: TopNavCustomization | undefined; }): TopNavMenuData[] => { @@ -54,7 +54,7 @@ export const getTopNavLinks = ({ services, stateContainer: state, adHocDataViews, - isPlainRecord: isTextBased, + isEsqlMode, }); }, testId: 'discoverAlertsButton', @@ -127,7 +127,7 @@ export const getTopNavLinks = ({ savedSearch.searchSource, state.appState.getState(), services, - isTextBased + isEsqlMode ); const { locator, notifications } = services; @@ -189,7 +189,7 @@ export const getTopNavLinks = ({ }), }, sharingData: { - isTextBased, + isTextBased: isEsqlMode, locatorParams: [{ id: locator.id, params }], ...searchSourceSharingData, // CSV reports can be generated without a saved search so we provide a fallback title diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx index 57bcf6baa4e8d..a2ecbe1f8123f 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.tsx @@ -10,13 +10,13 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { isOfAggregateQueryType } from '@kbn/es-query'; import { SavedObjectSaveModal, showSaveModal, OnSaveProps } from '@kbn/saved-objects-plugin/public'; import { SavedSearch, SaveSavedSearchOptions } from '@kbn/saved-search-plugin/public'; import { isLegacyTableEnabled } from '@kbn/discover-utils'; import { DiscoverServices } from '../../../../build_services'; import { DiscoverStateContainer } from '../../state_management/discover_state'; import { getAllowedSampleSize } from '../../../../utils/get_allowed_sample_size'; +import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources'; async function saveDataSource({ savedSearch, @@ -114,6 +114,7 @@ export async function onSaveSearch({ isTitleDuplicateConfirmed: boolean; onTitleDuplicate: () => void; }) => { + const appState = state.appState.getState(); const currentTitle = savedSearch.title; const currentTimeRestore = savedSearch.timeRestore; const currentRowsPerPage = savedSearch.rowsPerPage; @@ -121,18 +122,19 @@ export async function onSaveSearch({ const currentDescription = savedSearch.description; const currentTags = savedSearch.tags; const currentVisContext = savedSearch.visContext; + savedSearch.title = newTitle; savedSearch.description = newDescription; savedSearch.timeRestore = newTimeRestore; savedSearch.rowsPerPage = isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: isOfAggregateQueryType(savedSearch.searchSource.getField('query')), + isEsqlMode: isDataSourceType(appState.dataSource, DataSourceType.Esql), }) ? currentRowsPerPage - : state.appState.getState().rowsPerPage; + : appState.rowsPerPage; // save the custom value or reset it if it's invalid - const appStateSampleSize = state.appState.getState().sampleSize; + const appStateSampleSize = appState.sampleSize; const allowedSampleSize = getAllowedSampleSize(appStateSampleSize, uiSettings); savedSearch.sampleSize = appStateSampleSize && allowedSampleSize === appStateSampleSize @@ -165,6 +167,7 @@ export async function onSaveSearch({ state, navigateOrReloadSavedSearch, }); + // If the save wasn't successful, put the original values back. if (!response) { savedSearch.title = currentTitle; @@ -180,7 +183,9 @@ export async function onSaveSearch({ state.internalState.transitions.resetOnSavedSearchChange(); state.appState.resetInitialState(); } + onSaveCb?.(); + return response; }; @@ -199,6 +204,7 @@ export async function onSaveSearch({ onClose={onClose ?? (() => {})} /> ); + showSaveModal(saveModal); } diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx index bea933950200d..0ef82691681e1 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.test.tsx @@ -17,7 +17,7 @@ import { dataViewWithNoTimefieldMock } from '../../../../__mocks__/data_view_no_ import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; import { getDiscoverStateMock } from '../../../../__mocks__/discover_state.mock'; -const mount = (dataView = dataViewMock, isPlainRecord = false) => { +const mount = (dataView = dataViewMock, isEsqlMode = false) => { const stateContainer = getDiscoverStateMock({ isTimeBased: true }); stateContainer.actions.setDataView(dataView); return mountWithIntl( @@ -26,7 +26,7 @@ const mount = (dataView = dataViewMock, isPlainRecord = false) => { stateContainer={stateContainer} anchorElement={document.createElement('div')} adHocDataViews={[]} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} services={discoverServiceMock} onClose={jest.fn()} /> diff --git a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx index 36bea3fb21506..097278ac9cd66 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/open_alerts_popover.tsx @@ -40,7 +40,7 @@ interface AlertsPopoverProps { savedQueryId?: string; adHocDataViews: DataView[]; services: DiscoverServices; - isPlainRecord?: boolean; + isEsqlMode?: boolean; } interface EsQueryAlertMetaData extends RuleTypeMetaData { @@ -54,7 +54,7 @@ export function AlertsPopover({ services, stateContainer, onClose: originalOnClose, - isPlainRecord, + isEsqlMode, }: AlertsPopoverProps) { const dataView = stateContainer.internalState.getState().dataView; const query = stateContainer.appState.getState().query; @@ -72,7 +72,7 @@ export function AlertsPopover({ * Provides the default parameters used to initialize the new rule */ const getParams = useCallback(() => { - if (isPlainRecord) { + if (isEsqlMode) { return { searchType: 'esqlQuery', esqlQuery: query, @@ -87,7 +87,7 @@ export function AlertsPopover({ .searchSource.getSerializedFields(), savedQueryId, }; - }, [isPlainRecord, stateContainer.appState, stateContainer.savedSearchState, query, timeField]); + }, [isEsqlMode, stateContainer.appState, stateContainer.savedSearchState, query, timeField]); const discoverMetadata: EsQueryAlertMetaData = useMemo( () => ({ @@ -128,12 +128,12 @@ export function AlertsPopover({ }, [alertFlyoutVisible, triggersActionsUi, discoverMetadata, getParams, onClose, stateContainer]); const hasTimeFieldName: boolean = useMemo(() => { - if (!isPlainRecord) { + if (!isEsqlMode) { return Boolean(dataView?.timeFieldName); } else { return Boolean(timeField); } - }, [dataView?.timeFieldName, isPlainRecord, timeField]); + }, [dataView?.timeFieldName, isEsqlMode, timeField]); const panels = [ { @@ -201,13 +201,13 @@ export function openAlertsPopover({ stateContainer, services, adHocDataViews, - isPlainRecord, + isEsqlMode, }: { anchorElement: HTMLElement; stateContainer: DiscoverStateContainer; services: DiscoverServices; adHocDataViews: DataView[]; - isPlainRecord?: boolean; + isEsqlMode?: boolean; }) { if (isOpen) { closeAlertsPopover(); @@ -226,7 +226,7 @@ export function openAlertsPopover({ stateContainer={stateContainer} adHocDataViews={adHocDataViews} services={services} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} /> diff --git a/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts index 58eda4f292674..06efd3205d1be 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts @@ -11,10 +11,9 @@ import useObservable from 'react-use/lib/useObservable'; import { useDiscoverCustomization } from '../../../../customizations'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { useInspector } from '../../hooks/use_inspector'; -import { useAppStateSelector } from '../../state_management/discover_app_state_container'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; import { useInternalStateSelector } from '../../state_management/discover_internal_state_container'; import type { DiscoverStateContainer } from '../../state_management/discover_state'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { getTopNavBadges } from './get_top_nav_badges'; import { getTopNavLinks } from './get_top_nav_links'; @@ -43,8 +42,7 @@ export const useDiscoverTopNav = ({ const dataView = useInternalStateSelector((state) => state.dataView); const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews); - const query = useAppStateSelector((state) => state.query); - const isTextBased = useMemo(() => isTextBasedQuery(query), [query]); + const isEsqlMode = useIsEsqlMode(); const onOpenInspector = useInspector({ inspector: services.inspector, stateContainer, @@ -57,14 +55,14 @@ export const useDiscoverTopNav = ({ services, state: stateContainer, onOpenInspector, - isTextBased, + isEsqlMode, adHocDataViews, topNavCustomization, }), [ adHocDataViews, dataView, - isTextBased, + isEsqlMode, onOpenInspector, services, stateContainer, diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts index e41047dedb887..d6999c2200436 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.test.ts @@ -18,11 +18,10 @@ import { DataDocumentsMsg, DataMainMsg, DataTotalHitsMsg, - RecordRawType, SavedSearchData, } from '../state_management/discover_data_state_container'; import { fetchDocuments } from './fetch_documents'; -import { fetchTextBased } from './fetch_text_based'; +import { fetchEsql } from './fetch_esql'; import { buildDataTableRecord } from '@kbn/discover-utils'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { searchResponseIncompleteWarningLocalCluster } from '@kbn/search-response-warnings/src/__mocks__/search_response_warnings'; @@ -31,12 +30,12 @@ jest.mock('./fetch_documents', () => ({ fetchDocuments: jest.fn().mockResolvedValue([]), })); -jest.mock('./fetch_text_based', () => ({ - fetchTextBased: jest.fn().mockResolvedValue([]), +jest.mock('./fetch_esql', () => ({ + fetchEsql: jest.fn().mockResolvedValue([]), })); const mockFetchDocuments = fetchDocuments as unknown as jest.MockedFunction; -const mockfetchTextBased = fetchTextBased as unknown as jest.MockedFunction; +const mockfetchEsql = fetchEsql as unknown as jest.MockedFunction; function subjectCollector(subject: Subject): () => Promise { const promise = firstValueFrom( @@ -90,7 +89,7 @@ describe('test fetchAll', () => { }; mockFetchDocuments.mockReset().mockResolvedValue({ records: [] }); - mockfetchTextBased.mockReset().mockResolvedValue({ records: [] }); + mockfetchEsql.mockReset().mockResolvedValue({ records: [] }); }); test('changes of fetchStatus when starting with FetchStatus.UNINITIALIZED', async () => { @@ -120,10 +119,9 @@ describe('test fetchAll', () => { await waitForNextTick(); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: 'document', result: documents, }, ]); @@ -141,21 +139,19 @@ describe('test fetchAll', () => { subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 42, }); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 2 }, - { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 42 }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 2 }, + { fetchStatus: FetchStatus.COMPLETE, result: 42 }, ]); }); @@ -164,21 +160,19 @@ describe('test fetchAll', () => { searchSource.getField('index')!.isTimeBased = () => true; subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 32, }); expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 0 }, // From documents query - { fetchStatus: FetchStatus.COMPLETE, recordRawType: 'document', result: 32 }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 0 }, // From documents query + { fetchStatus: FetchStatus.COMPLETE, result: 32 }, ]); }); @@ -191,31 +185,28 @@ describe('test fetchAll', () => { mockFetchDocuments.mockResolvedValue({ records: documents }); subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.ERROR, - recordRawType: RecordRawType.DOCUMENT, error: { msg: 'Oh noes!' } as unknown as Error, }); expect(await collectTotalHits()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document', result: 1 }, - { fetchStatus: FetchStatus.ERROR, recordRawType: 'document', error: { msg: 'Oh noes!' } }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL, result: 1 }, + { fetchStatus: FetchStatus.ERROR, error: { msg: 'Oh noes!' } }, ]); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, - { fetchStatus: FetchStatus.PARTIAL, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, + { fetchStatus: FetchStatus.PARTIAL }, { fetchStatus: FetchStatus.COMPLETE, foundDocuments: true, error: undefined, - recordRawType: 'document', }, ]); }); @@ -226,23 +217,20 @@ describe('test fetchAll', () => { mockFetchDocuments.mockRejectedValue({ msg: 'This query failed' }); subjects.totalHits$.next({ fetchStatus: FetchStatus.LOADING, - recordRawType: RecordRawType.DOCUMENT, }); fetchAll(subjects, false, deps); await waitForNextTick(); subjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: 5, }); expect(await collectMain()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'document' }, + { fetchStatus: FetchStatus.LOADING }, { fetchStatus: FetchStatus.ERROR, error: { msg: 'This query failed' }, - recordRawType: 'document', }, // Here should be no COMPLETE coming anymore ]); @@ -255,9 +243,9 @@ describe('test fetchAll', () => { { _id: '2', _index: 'logs' }, ]; const documents = hits.map((hit) => buildDataTableRecord(hit, dataViewMock)); - mockfetchTextBased.mockResolvedValue({ + mockfetchEsql.mockResolvedValue({ records: documents, - textBasedQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], + esqlQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], }); const query = { esql: 'from foo' }; deps = { @@ -284,12 +272,11 @@ describe('test fetchAll', () => { expect(await collect()).toEqual([ { fetchStatus: FetchStatus.UNINITIALIZED }, - { fetchStatus: FetchStatus.LOADING, recordRawType: 'plain', query }, + { fetchStatus: FetchStatus.LOADING, query }, { fetchStatus: FetchStatus.PARTIAL, - recordRawType: 'plain', result: documents, - textBasedQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], + esqlQueryColumns: [{ id: '1', name: 'test1', meta: { type: 'number' } }], query, }, ]); @@ -308,7 +295,6 @@ describe('test fetchAll', () => { mockFetchDocuments.mockResolvedValue({ records: moreRecords, interceptedWarnings }); subjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }); fetchMoreDocuments(subjects, deps); @@ -318,17 +304,14 @@ describe('test fetchAll', () => { { fetchStatus: FetchStatus.UNINITIALIZED }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.LOADING_MORE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: [...initialRecords, ...moreRecords], interceptedWarnings, }, @@ -346,7 +329,6 @@ describe('test fetchAll', () => { mockFetchDocuments.mockRejectedValue({ msg: 'This query failed' }); subjects.documents$.next({ fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }); fetchMoreDocuments(subjects, deps); @@ -356,17 +338,14 @@ describe('test fetchAll', () => { { fetchStatus: FetchStatus.UNINITIALIZED }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.LOADING_MORE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, { fetchStatus: FetchStatus.COMPLETE, - recordRawType: RecordRawType.DOCUMENT, result: initialRecords, }, ]); @@ -385,7 +364,7 @@ describe('test fetchAll', () => { test('should swallow abort errors', async () => { const collect = subjectCollector(subjects.documents$); - mockfetchTextBased.mockRejectedValue({ msg: 'The query was aborted' }); + mockfetchEsql.mockRejectedValue({ msg: 'The query was aborted' }); const query = { esql: 'from foo' }; deps = { abortController: new AbortController(), diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts index 2d003680904d6..410d1d468275d 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_all.ts @@ -5,14 +5,15 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { Adapters } from '@kbn/inspector-plugin/common'; import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { BehaviorSubject, filter, firstValueFrom, map, merge, scan } from 'rxjs'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { isEqual } from 'lodash'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import type { DiscoverAppState } from '../state_management/discover_app_state_container'; import { updateVolatileSearchSource } from './update_search_source'; -import { getRawRecordType } from '../utils/get_raw_record_type'; import { checkHitCount, sendCompleteMsg, @@ -25,13 +26,9 @@ import { } from '../hooks/use_saved_search_messages'; import { fetchDocuments } from './fetch_documents'; import { FetchStatus } from '../../types'; -import { - DataMsg, - RecordRawType, - SavedSearchData, -} from '../state_management/discover_data_state_container'; +import { DataMsg, SavedSearchData } from '../state_management/discover_data_state_container'; import { DiscoverServices } from '../../../build_services'; -import { fetchTextBased } from './fetch_text_based'; +import { fetchEsql } from './fetch_esql'; import { InternalState } from '../state_management/discover_internal_state_container'; export interface FetchDeps { @@ -74,13 +71,13 @@ export function fetchAll( const dataView = searchSource.getField('index')!; const query = getAppState().query; const prevQuery = dataSubjects.documents$.getValue().query; - const recordRawType = getRawRecordType(query); - const useTextBased = recordRawType === RecordRawType.PLAIN; + const isEsqlQuery = isOfAggregateQueryType(query); + if (reset) { - sendResetMsg(dataSubjects, initialFetchStatus, recordRawType); + sendResetMsg(dataSubjects, initialFetchStatus); } - if (recordRawType === RecordRawType.DOCUMENT) { + if (!isEsqlQuery) { // Update the base searchSource, base for all child fetches updateVolatileSearchSource(searchSource, { dataView, @@ -90,23 +87,20 @@ export function fetchAll( }); } - const shouldFetchTextBased = useTextBased && !!query; - // Mark all subjects as loading - sendLoadingMsg(dataSubjects.main$, { recordRawType }); - sendLoadingMsg(dataSubjects.documents$, { recordRawType, query }); + sendLoadingMsg(dataSubjects.main$); + sendLoadingMsg(dataSubjects.documents$, { query }); // histogram for data view mode will send `loading` for totalHits$ - if (shouldFetchTextBased) { + if (isEsqlQuery) { sendLoadingMsg(dataSubjects.totalHits$, { - recordRawType, result: dataSubjects.totalHits$.getValue().result, }); } // Start fetching all required requests - const response = shouldFetchTextBased - ? fetchTextBased( + const response = isEsqlQuery + ? fetchEsql( query, dataView, data, @@ -115,11 +109,12 @@ export function fetchAll( abortController.signal ) : fetchDocuments(searchSource, fetchDeps); - const fetchType = shouldFetchTextBased ? 'fetchTextBased' : 'fetchDocuments'; + const fetchType = isEsqlQuery ? 'fetchTextBased' : 'fetchDocuments'; const startTime = window.performance.now(); + // Handle results of the individual queries and forward the results to the corresponding dataSubjects response - .then(({ records, textBasedQueryColumns, interceptedWarnings, textBasedHeaderWarning }) => { + .then(({ records, esqlQueryColumns, interceptedWarnings, esqlHeaderWarning }) => { if (services.analytics) { const duration = window.performance.now() - startTime; reportPerformanceMetricEvent(services.analytics, { @@ -129,11 +124,10 @@ export function fetchAll( }); } - if (shouldFetchTextBased) { + if (isEsqlQuery) { dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.COMPLETE, result: records.length, - recordRawType, }); } else { const currentTotalHits = dataSubjects.totalHits$.getValue(); @@ -144,29 +138,28 @@ export function fetchAll( dataSubjects.totalHits$.next({ fetchStatus: FetchStatus.PARTIAL, result: records.length, - recordRawType, }); } } + /** - * The partial state for text based query languages is necessary in case the query has changed - * In the follow up useTextBasedQueryLanguage hook in this case new columns are added to AppState + * The partial state for ES|QL mode is necessary in case the query has changed + * In the follow up useEsqlMode hook in this case new columns are added to AppState * So the data table shows the new columns of the table. The partial state was introduced to prevent * To frequent change of state causing the table to re-render to often, which causes race conditions * So it takes too long, a bad user experience, also a potential flakniess in tests */ const fetchStatus = - useTextBased && (!prevQuery || !isEqual(query, prevQuery)) + isEsqlQuery && (!prevQuery || !isEqual(query, prevQuery)) ? FetchStatus.PARTIAL : FetchStatus.COMPLETE; dataSubjects.documents$.next({ fetchStatus, result: records, - textBasedQueryColumns, - textBasedHeaderWarning, + esqlQueryColumns, + esqlHeaderWarning, interceptedWarnings, - recordRawType, query, }); @@ -210,12 +203,11 @@ export async function fetchMoreDocuments( try { const { getAppState, getInternalState, services, savedSearch } = fetchDeps; const searchSource = savedSearch.searchSource.createChild(); - const dataView = searchSource.getField('index')!; const query = getAppState().query; - const recordRawType = getRawRecordType(query); + const isEsqlQuery = isOfAggregateQueryType(query); - if (recordRawType === RecordRawType.PLAIN) { + if (isEsqlQuery) { // not supported yet return; } diff --git a/src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts similarity index 80% rename from src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts rename to src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts index 7045eb3c28cbc..3aba795d26920 100644 --- a/src/plugins/discover/public/application/main/data_fetching/fetch_text_based.ts +++ b/src/plugins/discover/public/application/main/data_fetching/fetch_esql.ts @@ -18,14 +18,14 @@ import { textBasedQueryStateToAstWithValidation } from '@kbn/data-plugin/common' import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { RecordsFetchResponse } from '../../types'; -interface TextBasedErrorResponse { +interface EsqlErrorResponse { error: { message: string; }; type: 'error'; } -export function fetchTextBased( +export function fetchEsql( query: Query | AggregateQuery, dataView: DataView, data: DataPublicPluginStart, @@ -42,10 +42,10 @@ export function fetchTextBased( time: timeRange, dataView, inputQuery, - titleForInspector: i18n.translate('discover.inspectorTextBasedRequestTitle', { + titleForInspector: i18n.translate('discover.inspectorEsqlRequestTitle', { defaultMessage: 'Table', }), - descriptionForInspector: i18n.translate('discover.inspectorTextBasedRequestDescription', { + descriptionForInspector: i18n.translate('discover.inspectorEsqlRequestDescription', { defaultMessage: 'This request queries Elasticsearch to fetch results for the table.', }), }) @@ -57,18 +57,18 @@ export function fetchTextBased( abortSignal?.addEventListener('abort', contract.cancel); const execution = contract.getData(); let finalData: DataTableRecord[] = []; - let textBasedQueryColumns: Datatable['columns'] | undefined; + let esqlQueryColumns: Datatable['columns'] | undefined; let error: string | undefined; - let textBasedHeaderWarning: string | undefined; + let esqlHeaderWarning: string | undefined; execution.pipe(pluck('result')).subscribe((resp) => { - const response = resp as Datatable | TextBasedErrorResponse; + const response = resp as Datatable | EsqlErrorResponse; if (response.type === 'error') { error = response.error.message; } else { const table = response as Datatable; const rows = table?.rows ?? []; - textBasedQueryColumns = table?.columns ?? undefined; - textBasedHeaderWarning = table.warning ?? undefined; + esqlQueryColumns = table?.columns ?? undefined; + esqlHeaderWarning = table.warning ?? undefined; finalData = rows.map((row: Record, idx: number) => { return { id: String(idx), @@ -84,16 +84,16 @@ export function fetchTextBased( } else { return { records: finalData || [], - textBasedQueryColumns, - textBasedHeaderWarning, + esqlQueryColumns, + esqlHeaderWarning, }; } }); } return { records: [] as DataTableRecord[], - textBasedQueryColumns: [], - textBasedHeaderWarning: undefined, + esqlQueryColumns: [], + esqlHeaderWarning: undefined, }; }) .catch((err) => { diff --git a/src/plugins/discover/public/application/main/discover_main_app.tsx b/src/plugins/discover/public/application/main/discover_main_app.tsx index 7872aa5440304..e59be8cb10185 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -17,7 +17,7 @@ import { useDiscoverServices } from '../../hooks/use_discover_services'; import { useSavedSearchAliasMatchRedirect } from '../../hooks/saved_search_alias_match_redirect'; import { useSavedSearchInitial } from './state_management/discover_state_provider'; import { useAdHocDataViews } from './hooks/use_adhoc_data_views'; -import { useTextBasedQueryLanguage } from './hooks/use_text_based_query_language'; +import { useEsqlMode } from './hooks/use_esql_mode'; import { addLog } from '../../utils/add_log'; const DiscoverLayoutMemoized = React.memo(DiscoverLayout); @@ -45,10 +45,11 @@ export function DiscoverMainApp(props: DiscoverMainProps) { /** * State changes (data view, columns), when a text base query result is returned */ - useTextBasedQueryLanguage({ + useEsqlMode({ dataViews: services.dataViews, stateContainer, }); + /** * Start state syncing and fetch data if necessary */ diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index b0c5ff4bc534f..560f4cb03535e 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -19,7 +19,6 @@ import { getSavedSearchFullPathUrl } from '@kbn/saved-search-plugin/public'; import useObservable from 'react-use/lib/useObservable'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { withSuspense } from '@kbn/shared-ux-utility'; -import { isOfAggregateQueryType } from '@kbn/es-query'; import { getInitialESQLQuery } from '@kbn/esql-utils'; import { ESQL_TYPE } from '@kbn/data-view-utils'; import { useUrl } from './hooks/use_url'; @@ -39,8 +38,8 @@ import { useDiscoverCustomizationService, } from '../../customizations'; import { DiscoverTopNavInline } from './components/top_nav/discover_topnav_inline'; -import { isTextBasedQuery } from './utils/is_text_based_query'; import { DiscoverStateContainer, LoadParams } from './state_management/discover_state'; +import { DataSourceType, isDataSourceType } from '../../../common/data_sources'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); @@ -78,7 +77,6 @@ export function DiscoverMainRoute({ customizationContext, stateStorageContainer, }); - const { customizationService, isInitialized: isCustomizationServiceInitialized } = useDiscoverCustomizationService({ customizationCallbacks, @@ -116,7 +114,7 @@ export function DiscoverMainRoute({ return true; // bypass NoData screen } - if (isOfAggregateQueryType(stateContainer.appState.getState().query)) { + if (isDataSourceType(stateContainer.appState.getState().dataSource, DataSourceType.Esql)) { return true; } @@ -369,8 +367,7 @@ function getLoadParamsForNewSearch(stateContainer: DiscoverStateContainer): { const prevAppState = stateContainer.appState.getState(); const prevDataView = stateContainer.internalState.getState().dataView; const initialAppState = - prevAppState?.query && - isTextBasedQuery(prevAppState.query) && + isDataSourceType(prevAppState.dataSource, DataSourceType.Esql) && prevDataView && prevDataView.type === ESQL_TYPE ? { diff --git a/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts b/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts index 9bb13020533f7..71a42b7d394a2 100644 --- a/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts +++ b/src/plugins/discover/public/application/main/hooks/use_adhoc_data_views.ts @@ -10,12 +10,11 @@ import { useEffect } from 'react'; import { METRIC_TYPE } from '@kbn/analytics'; import { DiscoverServices } from '../../../build_services'; import { useSavedSearch } from '../state_management/discover_state_provider'; -import { isTextBasedQuery } from '../utils/is_text_based_query'; -import { useAppStateSelector } from '../state_management/discover_app_state_container'; import { useInternalStateSelector } from '../state_management/discover_internal_state_container'; import { ADHOC_DATA_VIEW_RENDER_EVENT } from '../../../constants'; import { DiscoverStateContainer } from '../state_management/discover_state'; import { useFiltersValidation } from './use_filters_validation'; +import { useIsEsqlMode } from './use_is_esql_mode'; export const useAdHocDataViews = ({ services, @@ -23,17 +22,16 @@ export const useAdHocDataViews = ({ stateContainer: DiscoverStateContainer; services: DiscoverServices; }) => { - const query = useAppStateSelector((state) => state.query); const dataView = useInternalStateSelector((state) => state.dataView); const savedSearch = useSavedSearch(); - const isTextBasedMode = isTextBasedQuery(query); + const isEsqlMode = useIsEsqlMode(); const { filterManager, toastNotifications } = services; useEffect(() => { if (dataView && !dataView.isPersisted()) { services.trackUiMetric?.(METRIC_TYPE.COUNT, ADHOC_DATA_VIEW_RENDER_EVENT); } - }, [dataView, isTextBasedMode, services]); + }, [dataView, isEsqlMode, services]); /** * Takes care of checking data view id references in filters diff --git a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx b/src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx similarity index 87% rename from src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx rename to src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx index 8777b357714ba..5ae16f0429288 100644 --- a/src/plugins/discover/public/application/main/hooks/use_test_based_query_language.test.tsx +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.test.tsx @@ -10,9 +10,8 @@ import { renderHook } from '@testing-library/react-hooks'; import { waitFor } from '@testing-library/react'; import { DataViewsContract } from '@kbn/data-plugin/public'; import { discoverServiceMock } from '../../../__mocks__/services'; -import { useTextBasedQueryLanguage } from './use_text_based_query_language'; +import { useEsqlMode } from './use_esql_mode'; import { FetchStatus } from '../../types'; -import { RecordRawType } from '../state_management/discover_data_state_container'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { AggregateQuery, Query } from '@kbn/es-query'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; @@ -37,7 +36,6 @@ function getHookProps( stateContainer.internalState.transitions.setSavedDataViews([dataViewMock as DataViewListItem]); const msgLoading = { - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, query, }; @@ -52,7 +50,6 @@ function getHookProps( } const query = { esql: 'from the-data-view-title' }; const msgComplete = { - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -92,14 +89,14 @@ const renderHookWithContext = ( }); } - renderHook(() => useTextBasedQueryLanguage(props), { + renderHook(() => useEsqlMode(props), { wrapper: getHookContext(props.stateContainer), }); return props; }; -describe('useTextBasedQueryLanguage', () => { - test('a text based query should change state when loading and finished', async () => { +describe('useEsqlMode', () => { + test('an ES|QL query should change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(true); replaceUrlState.mockReset(); @@ -117,14 +114,13 @@ describe('useTextBasedQueryLanguage', () => { viewMode: undefined, }); }); - test('changing a text based query with different result columns should change state when loading and finished', async () => { + test('changing an ES|QL query with different result columns should change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; stateContainer.dataState.data$.documents$.next(msgComplete); replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -145,13 +141,12 @@ describe('useTextBasedQueryLanguage', () => { }); }); - test('changing a text based query with same result columns should not change state when loading and finished', async () => { + test('changing an ES|QL query with same result columns should not change state when loading and finished', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; stateContainer.dataState.data$.documents$.next(msgComplete); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -165,7 +160,7 @@ describe('useTextBasedQueryLanguage', () => { await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); }); - test('only changing a text based query with same result columns should not change columns', async () => { + test('only changing an ES|QL query with same result columns should not change columns', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; @@ -175,7 +170,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -195,7 +189,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -209,7 +202,7 @@ describe('useTextBasedQueryLanguage', () => { await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); }); - test('if its not a text based query coming along, it should be ignored', async () => { + test('if its not an ES|QL query coming along, it should be ignored', async () => { const { replaceUrlState, stateContainer } = renderHookWithContext(false); const documents$ = stateContainer.dataState.data$.documents$; @@ -218,7 +211,6 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.DOCUMENT, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -230,7 +222,6 @@ describe('useTextBasedQueryLanguage', () => { }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -257,7 +248,6 @@ describe('useTextBasedQueryLanguage', () => { expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -272,7 +262,6 @@ describe('useTextBasedQueryLanguage', () => { expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -296,7 +285,6 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -316,7 +304,6 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -329,7 +316,6 @@ describe('useTextBasedQueryLanguage', () => { }); expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -353,13 +339,11 @@ describe('useTextBasedQueryLanguage', () => { const documents$ = stateContainer.dataState.data$.documents$; documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | WHERE field1=2' }, }); expect(replaceUrlState).toHaveBeenCalledTimes(0); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -377,24 +361,20 @@ describe('useTextBasedQueryLanguage', () => { replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | keep field 1; | WHERE field1=2' }, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.ERROR, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.LOADING, query: { esql: 'from the-data-view-title | keep field1' }, }); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { @@ -412,20 +392,19 @@ describe('useTextBasedQueryLanguage', () => { }); }); - test('changing a text based query with an index pattern that not corresponds to a dataview should return results', async () => { + test('changing an ES|QL query with an index pattern that not corresponds to a dataview should return results', async () => { const props = getHookProps(query, discoverServiceMock.dataViews); const { stateContainer, replaceUrlState } = props; const documents$ = stateContainer.dataState.data$.documents$; props.stateContainer.actions.setDataView(dataViewMock); - renderHook(() => useTextBasedQueryLanguage(props), { wrapper: getHookContext(stateContainer) }); + renderHook(() => useEsqlMode(props), { wrapper: getHookContext(stateContainer) }); documents$.next(msgComplete); await waitFor(() => expect(replaceUrlState).toHaveBeenCalledTimes(0)); replaceUrlState.mockReset(); documents$.next({ - recordRawType: RecordRawType.PLAIN, fetchStatus: FetchStatus.PARTIAL, result: [ { diff --git a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts similarity index 90% rename from src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts rename to src/plugins/discover/public/application/main/hooks/use_esql_mode.ts index 063a5c21bfbe3..a907b1e796c87 100644 --- a/src/plugins/discover/public/application/main/hooks/use_text_based_query_language.ts +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isEqual } from 'lodash'; import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; import { useCallback, useEffect, useRef } from 'react'; @@ -20,10 +21,10 @@ const MAX_NUM_OF_COLUMNS = 50; const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; /** - * Hook to take care of text based query language state transformations when a new result is returned + * Hook to take care of ES|QL state transformations when a new result is returned * If necessary this is setting displayed columns and selected data view */ -export function useTextBasedQueryLanguage({ +export function useEsqlMode({ dataViews, stateContainer, }: { @@ -42,7 +43,7 @@ export function useTextBasedQueryLanguage({ const cleanup = useCallback(() => { if (prev.current.query) { - // cleanup when it's not a text based query lang + // cleanup when it's not an ES|QL query prev.current = { columns: [], query: '', @@ -54,7 +55,7 @@ export function useTextBasedQueryLanguage({ const subscription = stateContainer.dataState.data$.documents$ .pipe( switchMap(async (next) => { - const { query, recordRawType } = next; + const { query } = next; if (!query || next.fetchStatus === FetchStatus.ERROR) { return; } @@ -66,7 +67,7 @@ export function useTextBasedQueryLanguage({ }; const { viewMode } = stateContainer.appState.getState(); let nextColumns: string[] = []; - const isTextBasedQueryLang = recordRawType === 'plain' && isOfAggregateQueryType(query); + const isEsqlQuery = isOfAggregateQueryType(query); const hasResults = Boolean(next.result?.length); let queryHasTransformationalCommands = false; if ('esql' in query) { @@ -78,7 +79,7 @@ export function useTextBasedQueryLanguage({ }); } - if (isTextBasedQueryLang) { + if (isEsqlQuery) { const language = getAggregateQueryMode(query); if (next.fetchStatus !== FetchStatus.PARTIAL) { return; @@ -100,8 +101,7 @@ export function useTextBasedQueryLanguage({ } const addColumnsToState = !isEqual(nextColumns, prev.current.columns); const queryChanged = query[language] !== prev.current.query; - const changeViewMode = - viewMode !== getValidViewMode({ viewMode, isTextBasedQueryMode: true }); + const changeViewMode = viewMode !== getValidViewMode({ viewMode, isEsqlMode: true }); if (!queryChanged || (!addColumnsToState && !changeViewMode)) { sendComplete(); return; diff --git a/src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts b/src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts similarity index 50% rename from src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts rename to src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts index 5be6bd3266c7b..49f83ff1bf9c5 100644 --- a/src/plugins/discover/public/application/main/hooks/use_behavior_subject.ts +++ b/src/plugins/discover/public/application/main/hooks/use_is_esql_mode.ts @@ -5,15 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { useRef } from 'react'; -import { BehaviorSubject } from 'rxjs'; -export function useBehaviorSubject(props: T): BehaviorSubject { - const ref = useRef | null>(null); +import { DataSourceType, isDataSourceType } from '../../../../common/data_sources'; +import { useAppStateSelector } from '../state_management/discover_app_state_container'; - if (ref.current === null) { - ref.current = new BehaviorSubject(props); - } - - return ref.current; -} +export const useIsEsqlMode = () => { + const dataSource = useAppStateSelector((state) => state.dataSource); + return isDataSourceType(dataSource, DataSourceType.Esql); +}; diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts index 6faf1c29257d9..4097831464669 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.test.ts @@ -18,11 +18,7 @@ import { } from './use_saved_search_messages'; import { FetchStatus } from '../../types'; import { BehaviorSubject } from 'rxjs'; -import { - DataDocumentsMsg, - DataMainMsg, - RecordRawType, -} from '../state_management/discover_data_state_container'; +import { DataDocumentsMsg, DataMainMsg } from '../state_management/discover_data_state_container'; import { filter } from 'rxjs'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { buildDataTableRecord } from '@kbn/discover-utils'; @@ -69,13 +65,11 @@ describe('test useSavedSearch message generators', () => { main$.subscribe((value) => { if (value.fetchStatus !== FetchStatus.COMPLETE) { expect(value.fetchStatus).toBe(FetchStatus.LOADING); - expect(value.recordRawType).toBe(RecordRawType.DOCUMENT); done(); } }); sendLoadingMsg(main$, { foundDocuments: true, - recordRawType: RecordRawType.DOCUMENT, }); }); test('sendLoadingMoreMsg', (done) => { diff --git a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts index 9374045d4b0d5..66d022df5f3ff 100644 --- a/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts +++ b/src/plugins/discover/public/application/main/hooks/use_saved_search_messages.ts @@ -17,7 +17,6 @@ import type { DataTotalHits$, SavedSearchData, } from '../state_management/discover_data_state_container'; -import { RecordRawType } from '../state_management/discover_data_state_container'; /** * Sends COMPLETE message to the main$ observable with the information @@ -37,13 +36,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { if (main$.getValue().fetchStatus === FetchStatus.COMPLETE) { return; } - const recordRawType = main$.getValue().recordRawType; - main$.next({ - fetchStatus: FetchStatus.COMPLETE, - foundDocuments, - error: undefined, - recordRawType, - }); + main$.next({ fetchStatus: FetchStatus.COMPLETE, foundDocuments, error: undefined }); } /** @@ -51,11 +44,7 @@ export function sendCompleteMsg(main$: DataMain$, foundDocuments = true) { */ export function sendPartialMsg(main$: DataMain$) { if (main$.getValue().fetchStatus === FetchStatus.LOADING) { - const recordRawType = main$.getValue().recordRawType; - main$.next({ - fetchStatus: FetchStatus.PARTIAL, - recordRawType, - }); + main$.next({ fetchStatus: FetchStatus.PARTIAL }); } } @@ -64,13 +53,10 @@ export function sendPartialMsg(main$: DataMain$) { */ export function sendLoadingMsg( data$: BehaviorSubject, - props: Omit + props?: Omit ) { if (data$.getValue().fetchStatus !== FetchStatus.LOADING) { - data$.next({ - ...props, - fetchStatus: FetchStatus.LOADING, - } as T); + data$.next({ ...props, fetchStatus: FetchStatus.LOADING } as T); } } @@ -79,10 +65,7 @@ export function sendLoadingMsg( */ export function sendLoadingMoreMsg(documents$: DataDocuments$) { if (documents$.getValue().fetchStatus !== FetchStatus.LOADING_MORE) { - documents$.next({ - ...documents$.getValue(), - fetchStatus: FetchStatus.LOADING_MORE, - }); + documents$.next({ ...documents$.getValue(), fetchStatus: FetchStatus.LOADING_MORE }); } } @@ -116,38 +99,17 @@ export function sendLoadingMoreFinishedMsg( * Send ERROR message */ export function sendErrorMsg(data$: DataMain$ | DataDocuments$ | DataTotalHits$, error?: Error) { - const recordRawType = data$.getValue().recordRawType; - data$.next({ - fetchStatus: FetchStatus.ERROR, - error, - recordRawType, - }); + data$.next({ fetchStatus: FetchStatus.ERROR, error }); } /** * Sends a RESET message to all data subjects * Needed when data view is switched or a new runtime field is added */ -export function sendResetMsg( - data: SavedSearchData, - initialFetchStatus: FetchStatus, - recordRawType: RecordRawType -) { - data.main$.next({ - fetchStatus: initialFetchStatus, - foundDocuments: undefined, - recordRawType, - }); - data.documents$.next({ - fetchStatus: initialFetchStatus, - result: [], - recordRawType, - }); - data.totalHits$.next({ - fetchStatus: initialFetchStatus, - result: undefined, - recordRawType, - }); +export function sendResetMsg(data: SavedSearchData, initialFetchStatus: FetchStatus) { + data.main$.next({ fetchStatus: initialFetchStatus, foundDocuments: undefined }); + data.documents$.next({ fetchStatus: initialFetchStatus, result: [] }); + data.totalHits$.next({ fetchStatus: initialFetchStatus, result: undefined }); } /** diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts index ac022d5875359..67586670c01c4 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.test.ts @@ -10,9 +10,8 @@ import { waitFor } from '@testing-library/react'; import { buildDataTableRecord } from '@kbn/discover-utils'; import { dataViewMock, esHitsMockWithSort } from '@kbn/discover-utils/src/__mocks__'; import { discoverServiceMock } from '../../../__mocks__/services'; -import { savedSearchMockWithESQL } from '../../../__mocks__/saved_search'; import { FetchStatus } from '../../types'; -import { DataDocuments$, RecordRawType } from './discover_data_state_container'; +import { DataDocuments$ } from './discover_data_state_container'; import { getDiscoverStateMock } from '../../../__mocks__/discover_state.mock'; import { fetchDocuments } from '../data_fetching/fetch_documents'; @@ -92,23 +91,13 @@ describe('test getDataStateContainer', () => { await waitFor(() => { expect(dataState.data$.main$.value.fetchStatus).toBe(FetchStatus.COMPLETE); }); - dataState.reset(stateContainer.savedSearchState.getState()); + dataState.reset(); await waitFor(() => { expect(dataState.data$.main$.value.fetchStatus).toBe(FetchStatus.LOADING); }); unsubscribe(); }); - test('useSavedSearch returns plain record raw type', async () => { - const stateContainer = getDiscoverStateMock({ - savedSearch: savedSearchMockWithESQL, - }); - stateContainer.savedSearchState.load = jest.fn().mockResolvedValue(savedSearchMockWithESQL); - await stateContainer.actions.loadSavedSearch({ savedSearchId: savedSearchMockWithESQL.id }); - - expect(stateContainer.dataState.data$.main$.getValue().recordRawType).toBe(RecordRawType.PLAIN); - }); - test('refetch$ accepts "fetch_more" signal', (done) => { const records = esHitsMockWithSort.map((hit) => buildDataTableRecord(hit, dataViewMock)); const initialRecords = [records[0], records[1]]; diff --git a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts index 203bd87ec84f1..71ad2ed87e79b 100644 --- a/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts +++ b/src/plugins/discover/public/application/main/state_management/discover_data_state_container.ts @@ -5,21 +5,20 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { BehaviorSubject, filter, map, mergeMap, Observable, share, Subject, tap } from 'rxjs'; import type { AutoRefreshDoneFn } from '@kbn/data-plugin/public'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; -import { AggregateQuery, Query } from '@kbn/es-query'; +import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; import { DataView } from '@kbn/data-views-plugin/common'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import type { SearchResponseWarning } from '@kbn/search-response-warnings'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { SEARCH_FIELDS_FROM_SOURCE, SEARCH_ON_PAGE_LOAD_SETTING } from '@kbn/discover-utils'; -import { getDataViewByTextBasedQueryLang } from './utils/get_data_view_by_text_based_query_lang'; -import { isTextBasedQuery } from '../utils/is_text_based_query'; -import { getRawRecordType } from '../utils/get_raw_record_type'; +import { getEsqlDataView } from './utils/get_esql_data_view'; import { DiscoverAppState } from './discover_app_state_container'; import { DiscoverServices } from '../../../build_services'; import { DiscoverSearchSessionManager } from './discover_search_session'; @@ -51,23 +50,11 @@ export type DataFetch$ = Observable<{ export type DataRefetch$ = Subject; -export enum RecordRawType { - /** - * Documents returned Elasticsearch, nested structure - */ - DOCUMENT = 'document', - /** - * Data returned e.g. ES|QL queries, flat structure - * */ - PLAIN = 'plain', -} - export type DataRefetchMsg = 'reset' | 'fetch_more' | undefined; export interface DataMsg { fetchStatus: FetchStatus; error?: Error; - recordRawType?: RecordRawType; query?: AggregateQuery | Query | undefined; } @@ -77,8 +64,8 @@ export interface DataMainMsg extends DataMsg { export interface DataDocumentsMsg extends DataMsg { result?: DataTableRecord[]; - textBasedQueryColumns?: DatatableColumn[]; // columns from text-based request - textBasedHeaderWarning?: string; + esqlQueryColumns?: DatatableColumn[]; // columns from ES|QL request + esqlHeaderWarning?: string; interceptedWarnings?: SearchResponseWarning[]; // warnings (like shard failures) } @@ -122,7 +109,7 @@ export interface DiscoverDataStateContainer { /** * resetting all data observable to initial state */ - reset: (savedSearch: SavedSearch) => void; + reset: () => void; /** * cancels the running queries @@ -168,8 +155,7 @@ export function getDataStateContainer({ const { data, uiSettings, toastNotifications } = services; const { timefilter } = data.query.timefilter; const inspectorAdapters = { requests: new RequestAdapter() }; - const appState = getAppState(); - const recordRawType = getRawRecordType(appState.query); + /** * The observable to trigger data fetching in UI * By refetch$.next('reset') rows and fieldcounts are reset to allow e.g. editing of runtime fields @@ -189,7 +175,7 @@ export function getDataStateContainer({ * The observables the UI (aka React component) subscribes to get notified about * the changes in the data fetching process (high level: fetching started, data was received) */ - const initialState = { fetchStatus: getInitialFetchStatus(), recordRawType }; + const initialState = { fetchStatus: getInitialFetchStatus() }; const dataSubjects: SavedSearchData = { main$: new BehaviorSubject(initialState), documents$: new BehaviorSubject(initialState), @@ -300,8 +286,8 @@ export function getDataStateContainer({ const query = getAppState().query; const currentDataView = getSavedSearch().searchSource.getField('index'); - if (isTextBasedQuery(query)) { - const nextDataView = await getDataViewByTextBasedQueryLang(query, currentDataView, services); + if (isOfAggregateQueryType(query)) { + const nextDataView = await getEsqlDataView(query, currentDataView, services); if (nextDataView !== currentDataView) { setDataView(nextDataView); } @@ -320,9 +306,8 @@ export function getDataStateContainer({ return refetch$; }; - const reset = (savedSearch: SavedSearch) => { - const recordType = getRawRecordType(savedSearch.searchSource.getField('query')); - sendResetMsg(dataSubjects, getInitialFetchStatus(), recordType); + const reset = () => { + sendResetMsg(dataSubjects, getInitialFetchStatus()); }; const cancel = () => { diff --git a/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts b/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts index fbd324a1a417c..5c71311377595 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/build_state_subscribe.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isEqual } from 'lodash'; import type { DiscoverInternalStateContainer } from '../discover_internal_state_container'; import type { DiscoverServices } from '../../../../build_services'; @@ -17,7 +18,6 @@ import { isEqualState, } from '../discover_app_state_container'; import { addLog } from '../../../../utils/add_log'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { FetchStatus } from '../../../types'; import { loadAndResolveDataView } from './resolve_data_view'; import { @@ -52,11 +52,11 @@ export const buildStateSubscribe = const nextQuery = nextState.query; const savedSearch = savedSearchState.getState(); const prevQuery = savedSearch.searchSource.getField('query'); - const isTextBasedQueryLang = isTextBasedQuery(nextQuery); + const isEsqlMode = isDataSourceType(nextState.dataSource, DataSourceType.Esql); const queryChanged = !isEqual(nextQuery, prevQuery) || !isEqual(nextQuery, prevState.query); if ( - isTextBasedQueryLang && + isEsqlMode && isEqualState(prevState, nextState, ['dataSource', 'viewMode']) && !queryChanged ) { @@ -73,11 +73,11 @@ export const buildStateSubscribe = addLog('[appstate] subscribe triggered', nextState); - if (isTextBasedQueryLang) { - const isTextBasedQueryLangPrev = isTextBasedQuery(prevQuery); - if (!isTextBasedQueryLangPrev) { + if (isEsqlMode) { + const isEsqlModePrev = isDataSourceType(prevState.dataSource, DataSourceType.Esql); + if (!isEsqlModePrev) { savedSearchState.update({ nextState }); - dataState.reset(savedSearch); + dataState.reset(); } } @@ -85,11 +85,11 @@ export const buildStateSubscribe = // Cast to boolean to avoid false positives when comparing // undefined and false, which would trigger a refetch const chartDisplayChanged = Boolean(nextState.hideChart) !== Boolean(hideChart); - const chartIntervalChanged = nextState.interval !== interval && !isTextBasedQueryLang; + const chartIntervalChanged = nextState.interval !== interval && !isEsqlMode; const breakdownFieldChanged = nextState.breakdownField !== breakdownField; const sampleSizeChanged = nextState.sampleSize !== sampleSize; - const docTableSortChanged = !isEqual(nextState.sort, sort) && !isTextBasedQueryLang; - const dataSourceChanged = !isEqual(nextState.dataSource, dataSource) && !isTextBasedQueryLang; + const docTableSortChanged = !isEqual(nextState.sort, sort) && !isEsqlMode; + const dataSourceChanged = !isEqual(nextState.dataSource, dataSource) && !isEsqlMode; let savedSearchDataView; @@ -100,7 +100,7 @@ export const buildStateSubscribe = : undefined; const { dataView: nextDataView, fallback } = await loadAndResolveDataView( - { id: dataViewId, savedSearch, isTextBasedQuery: isTextBasedQueryLang }, + { id: dataViewId, savedSearch, isEsqlMode }, { internalStateContainer: internalState, services } ); @@ -120,7 +120,7 @@ export const buildStateSubscribe = } savedSearch.searchSource.setField('index', nextDataView); - dataState.reset(savedSearch); + dataState.reset(); setDataView(nextDataView); savedSearchDataView = nextDataView; } diff --git a/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts b/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts index ebf5f8dc90bd3..861c85ee20e65 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/cleanup_url_state.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { isOfAggregateQueryType } from '@kbn/es-query'; import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { DiscoverAppState, AppStateUrl } from '../discover_app_state_container'; @@ -20,12 +21,11 @@ export function cleanupUrlState( appStateFromUrl: AppStateUrl, uiSettings: IUiSettingsClient ): DiscoverAppState { - if ( - appStateFromUrl.query && - !isOfAggregateQueryType(appStateFromUrl.query) && - !appStateFromUrl.query.language - ) { - appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); + const query = appStateFromUrl.query; + const isEsqlQuery = isOfAggregateQueryType(query); + + if (!isEsqlQuery && query && !query.language) { + appStateFromUrl.query = migrateLegacyQuery(query); } if (typeof appStateFromUrl.sort?.[0] === 'string') { @@ -53,7 +53,7 @@ export function cleanupUrlState( if ( appStateFromUrl.sampleSize && - (isOfAggregateQueryType(appStateFromUrl.query) || // not supported yet for ES|QL + (isEsqlQuery || // not supported yet for ES|QL !( typeof appStateFromUrl.sampleSize === 'number' && appStateFromUrl.sampleSize > 0 && @@ -67,7 +67,7 @@ export function cleanupUrlState( if (appStateFromUrl.index) { if (!appStateFromUrl.dataSource) { // Convert the provided index to a data source - appStateFromUrl.dataSource = isOfAggregateQueryType(appStateFromUrl.query) + appStateFromUrl.dataSource = isEsqlQuery ? createEsqlDataSource() : createDataViewDataSource({ dataViewId: appStateFromUrl.index }); } diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts similarity index 81% rename from src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts rename to src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts index b8053c47fd174..81189661b543b 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.test.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { getDataViewByTextBasedQueryLang } from './get_data_view_by_text_based_query_lang'; +import { getEsqlDataView } from './get_esql_data_view'; import { dataViewAdHoc } from '../../../../__mocks__/data_view_complex'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; import { discoverServiceMock } from '../../../../__mocks__/services'; -describe('getDataViewByTextBasedQueryLang', () => { +describe('getEsqlDataView', () => { discoverServiceMock.dataViews.create = jest.fn().mockReturnValue({ ...dataViewMock, isPersisted: () => false, @@ -21,13 +21,13 @@ describe('getDataViewByTextBasedQueryLang', () => { const services = discoverServiceMock; it('returns the current dataview if is adhoc and query has not changed', async () => { const query = { esql: 'from data-view-ad-hoc-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView).toStrictEqual(dataViewAdHoc); }); it('creates an adhoc dataview if the current dataview is persistent and query has not changed', async () => { const query = { esql: 'from the-data-view-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewMock, services); + const dataView = await getEsqlDataView(query, dataViewMock, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.timeFieldName).toBe('@timestamp'); }); @@ -41,7 +41,7 @@ describe('getDataViewByTextBasedQueryLang', () => { timeFieldName: undefined, }); const query = { esql: 'from the-data-view-title' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.timeFieldName).toBeUndefined(); }); @@ -55,7 +55,7 @@ describe('getDataViewByTextBasedQueryLang', () => { timeFieldName: undefined, }); const query = { esql: 'ROW x = "ES|QL is awesome"' }; - const dataView = await getDataViewByTextBasedQueryLang(query, dataViewAdHoc, services); + const dataView = await getEsqlDataView(query, dataViewAdHoc, services); expect(dataView.isPersisted()).toEqual(false); expect(dataView.name).toEqual(dataViewAdHoc.name); expect(dataView.timeFieldName).toBeUndefined(); diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts similarity index 96% rename from src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts rename to src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts index db964c073a253..c4dd4d7e163b8 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_data_view_by_text_based_query_lang.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_esql_data_view.ts @@ -10,7 +10,7 @@ import { getESQLAdHocDataview, getIndexPatternFromESQLQuery } from '@kbn/esql-ut import { DataView } from '@kbn/data-views-plugin/common'; import { DiscoverServices } from '../../../../build_services'; -export async function getDataViewByTextBasedQueryLang( +export async function getEsqlDataView( query: AggregateQuery, currentDataView: DataView | undefined, services: DiscoverServices diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts index 5e070ef099cde..ac85a24f91f34 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.test.ts @@ -98,24 +98,24 @@ describe('getStateDefaults', () => { }); expect(actualForUndefinedViewMode.viewMode).toBeUndefined(); - const actualForTextBasedWithInvalidViewMode = getStateDefaults({ + const actualForEsqlWithInvalidViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMockWithESQL, viewMode: VIEW_MODE.AGGREGATED_LEVEL, }, }); - expect(actualForTextBasedWithInvalidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + expect(actualForEsqlWithInvalidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); - const actualForTextBasedWithValidViewMode = getStateDefaults({ + const actualForEsqlWithValidViewMode = getStateDefaults({ services: discoverServiceMock, savedSearch: { ...savedSearchMockWithESQL, viewMode: VIEW_MODE.DOCUMENT_LEVEL, }, }); - expect(actualForTextBasedWithValidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); - expect(actualForTextBasedWithValidViewMode.dataSource).toEqual(createEsqlDataSource()); + expect(actualForEsqlWithValidViewMode.viewMode).toBe(VIEW_MODE.DOCUMENT_LEVEL); + expect(actualForEsqlWithValidViewMode.dataSource).toEqual(createEsqlDataSource()); const actualForWithValidViewMode = getStateDefaults({ services: discoverServiceMock, @@ -133,11 +133,11 @@ describe('getStateDefaults', () => { }); test('should return expected dataSource', () => { - const actualForTextBased = getStateDefaults({ + const actualForEsql = getStateDefaults({ services: discoverServiceMock, savedSearch: savedSearchMockWithESQL, }); - expect(actualForTextBased.dataSource).toMatchInlineSnapshot(` + expect(actualForEsql.dataSource).toMatchInlineSnapshot(` Object { "type": "esql", } diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts index 7a1f2734aa7c8..7e44adf64dbc1 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_state_defaults.ts @@ -16,16 +16,12 @@ import { SEARCH_FIELDS_FROM_SOURCE, SORT_DEFAULT_ORDER_SETTING, } from '@kbn/discover-utils'; +import { isOfAggregateQueryType } from '@kbn/es-query'; import { DiscoverAppState } from '../discover_app_state_container'; import { DiscoverServices } from '../../../../build_services'; import { getDefaultSort, getSortArray } from '../../../../utils/sorting'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import { getValidViewMode } from '../../utils/get_valid_view_mode'; -import { - createDataViewDataSource, - createEsqlDataSource, - DiscoverDataSource, -} from '../../../../../common/data_sources'; +import { createDataViewDataSource, createEsqlDataSource } from '../../../../../common/data_sources'; function getDefaultColumns(savedSearch: SavedSearch, uiSettings: IUiSettingsClient) { if (savedSearch.columns && savedSearch.columns.length > 0) { @@ -51,11 +47,11 @@ export function getStateDefaults({ const { data, uiSettings, storage } = services; const dataView = searchSource.getField('index'); const query = searchSource.getField('query') || data.query.queryString.getDefaultQuery(); - const isTextBasedQueryMode = isTextBasedQuery(query); - const sort = getSortArray(savedSearch.sort ?? [], dataView!, isTextBasedQueryMode); + const isEsqlQuery = isOfAggregateQueryType(query); + const sort = getSortArray(savedSearch.sort ?? [], dataView!, isEsqlQuery); const columns = getDefaultColumns(savedSearch, uiSettings); const chartHidden = getChartHidden(storage, 'discover'); - const dataSource: DiscoverDataSource | undefined = isTextBasedQueryMode + const dataSource = isEsqlQuery ? createEsqlDataSource() : dataView?.id ? createDataViewDataSource({ dataViewId: dataView.id }) @@ -68,7 +64,7 @@ export function getStateDefaults({ dataView, uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - isTextBasedQueryMode + isEsqlQuery ) : sort, columns, @@ -102,7 +98,7 @@ export function getStateDefaults({ if (savedSearch.viewMode) { defaultState.viewMode = getValidViewMode({ viewMode: savedSearch.viewMode, - isTextBasedQueryMode, + isEsqlMode: isEsqlQuery, }); } if (savedSearch.hideAggregatedPreview) { diff --git a/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts b/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts index c06cb3b67f235..0960471c4a6dc 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/get_switch_data_view_app_state.ts @@ -39,9 +39,9 @@ export function getDataViewAppState( ); } - const isTextBasedQueryMode = isOfAggregateQueryType(query); + const isEsqlQuery = isOfAggregateQueryType(query); - if (isTextBasedQueryMode) { + if (isEsqlQuery) { columns = []; } @@ -49,7 +49,7 @@ export function getDataViewAppState( // filter out sorting by timeField in case it is set. data views without timeField don't // prepend this field in the table, so in legacy grid you would need to add this column to // remove sorting - let nextSort = getSortArray(currentSort, nextDataView, isTextBasedQueryMode).filter((value) => { + let nextSort = getSortArray(currentSort, nextDataView, isEsqlQuery).filter((value) => { return nextDataView.timeFieldName || value[0] !== currentDataView.timeFieldName; }); diff --git a/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts b/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts index 0997f0f58b0aa..6a6d55535f47a 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/load_saved_search.ts @@ -5,10 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { cloneDeep, isEqual } from 'lodash'; -import { getDataViewByTextBasedQueryLang } from './get_data_view_by_text_based_query_lang'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; +import { isOfAggregateQueryType } from '@kbn/es-query'; +import { getEsqlDataView } from './get_esql_data_view'; import { loadAndResolveDataView } from './resolve_data_view'; import { DiscoverInternalStateContainer } from '../discover_internal_state_container'; import { DiscoverDataStateContainer } from '../discover_data_state_container'; @@ -160,7 +161,7 @@ function updateBySavedSearch(savedSearch: SavedSearch, deps: LoadSavedSearchDeps } // Finally notify dataStateContainer, data.query and filterManager about new derived state - dataStateContainer.reset(savedSearch); + dataStateContainer.reset(); // set data service filters const filters = savedSearch.searchSource.getField('filter'); if (Array.isArray(filters) && filters.length) { @@ -204,14 +205,14 @@ const getStateDataView = async ( } ) => { const { dataView, dataViewSpec } = params; - const isTextBased = isTextBasedQuery(query); + const isEsqlQuery = isOfAggregateQueryType(query); if (dataView) { return dataView; } - if (isTextBased) { - return await getDataViewByTextBasedQueryLang(query, dataView, services); + if (isEsqlQuery) { + return await getEsqlDataView(query, dataView, services); } const result = await loadAndResolveDataView( @@ -219,7 +220,7 @@ const getStateDataView = async ( id: dataViewId, dataViewSpec, savedSearch, - isTextBasedQuery: isTextBased, + isEsqlMode: isEsqlQuery, }, { services, internalStateContainer } ); diff --git a/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts b/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts index 031193c93cba5..55e18899a4e33 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/resolve_data_view.ts @@ -115,7 +115,7 @@ export function resolveDataView( ip: DataViewData, savedSearch: SavedSearch | undefined, toastNotifications: ToastsStart, - isTextBasedQuery?: boolean + isEsqlMode?: boolean ) { const { loaded: loadedDataView, stateVal, stateValFound } = ip; @@ -126,8 +126,8 @@ export function resolveDataView( return ownDataView; } - // no warnings for text based mode - if (stateVal && !stateValFound && !Boolean(isTextBasedQuery)) { + // no warnings for ES|QL mode + if (stateVal && !stateValFound && !Boolean(isEsqlMode)) { const warningTitle = i18n.translate('discover.valueIsNotConfiguredDataViewIDWarningTitle', { defaultMessage: '{stateVal} is not a configured data view ID', values: { @@ -172,12 +172,12 @@ export const loadAndResolveDataView = async ( id, dataViewSpec, savedSearch, - isTextBasedQuery, + isEsqlMode, }: { id?: string; dataViewSpec?: DataViewSpec; savedSearch?: SavedSearch; - isTextBasedQuery?: boolean; + isEsqlMode?: boolean; }, { internalStateContainer, @@ -198,7 +198,7 @@ export const loadAndResolveDataView = async ( nextDataViewData, savedSearch, services.toastNotifications, - isTextBasedQuery + isEsqlMode ); return { fallback: !nextDataViewData.stateValFound, dataView: nextDataView }; }; diff --git a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts index ad47bb021ea71..19a5cd6c7fb88 100644 --- a/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts +++ b/src/plugins/discover/public/application/main/state_management/utils/update_saved_search.ts @@ -5,13 +5,14 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; import { cloneDeep } from 'lodash'; -import { isTextBasedQuery } from '../../utils/is_text_based_query'; import type { DiscoverAppState } from '../discover_app_state_container'; import type { DiscoverServices } from '../../../../build_services'; import type { DiscoverGlobalStateContainer } from '../discover_global_state_container'; +import { DataSourceType, isDataSourceType } from '../../../../../common/data_sources'; /** * Updates the saved search with a given data view & Appstate @@ -76,11 +77,11 @@ export function updateSavedSearch({ savedSearch.breakdownField = state.breakdownField || undefined; // `undefined` instead of an empty string savedSearch.hideAggregatedPreview = state.hideAggregatedPreview; - // add a flag here to identify text based language queries + // add a flag here to identify ES|QL queries // these should be filtered out from the visualize editor - const isTextBasedQueryResult = isTextBasedQuery(state.query); - if (savedSearch.isTextBasedQuery || isTextBasedQueryResult) { - savedSearch.isTextBasedQuery = isTextBasedQueryResult; + const isEsqlMode = isDataSourceType(state.dataSource, DataSourceType.Esql); + if (savedSearch.isTextBasedQuery || isEsqlMode) { + savedSearch.isTextBasedQuery = isEsqlMode; } } diff --git a/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts b/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts deleted file mode 100644 index 9626c2d1caad1..0000000000000 --- a/src/plugins/discover/public/application/main/utils/get_raw_record_type.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 { RecordRawType } from '../state_management/discover_data_state_container'; -import { getRawRecordType } from './get_raw_record_type'; - -describe('getRawRecordType', () => { - it('returns empty string for Query type query', () => { - const mode = getRawRecordType({ query: '', language: 'lucene' }); - expect(mode).toEqual(RecordRawType.DOCUMENT); - }); - - it('returns esql for Query type query', () => { - const mode = getRawRecordType({ esql: 'from foo' }); - - expect(mode).toEqual(RecordRawType.PLAIN); - }); -}); diff --git a/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts b/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts deleted file mode 100644 index 3a96973adfbac..0000000000000 --- a/src/plugins/discover/public/application/main/utils/get_raw_record_type.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 { AggregateQuery, Query, isOfAggregateQueryType } from '@kbn/es-query'; -import { RecordRawType } from '../state_management/discover_data_state_container'; - -export function getRawRecordType(query?: Query | AggregateQuery) { - if (query && isOfAggregateQueryType(query)) { - return RecordRawType.PLAIN; - } - - return RecordRawType.DOCUMENT; -} diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts index 33431ba09b7c5..51530926defbf 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.test.ts @@ -14,44 +14,44 @@ describe('getValidViewMode', () => { expect( getValidViewMode({ viewMode: undefined, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBeUndefined(); expect( getValidViewMode({ viewMode: VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); expect( getValidViewMode({ viewMode: VIEW_MODE.AGGREGATED_LEVEL, - isTextBasedQueryMode: false, + isEsqlMode: false, }) ).toBe(VIEW_MODE.AGGREGATED_LEVEL); }); - test('should work correctly for text-based mode', () => { + test('should work correctly for ES|QL mode', () => { expect( getValidViewMode({ viewMode: undefined, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBeUndefined(); expect( getValidViewMode({ viewMode: VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); expect( getValidViewMode({ viewMode: VIEW_MODE.AGGREGATED_LEVEL, - isTextBasedQueryMode: true, + isEsqlMode: true, }) ).toBe(VIEW_MODE.DOCUMENT_LEVEL); }); diff --git a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts index 4d6d660c28eef..eab9677f083b2 100644 --- a/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts +++ b/src/plugins/discover/public/application/main/utils/get_valid_view_mode.ts @@ -11,17 +11,17 @@ import { VIEW_MODE } from '@kbn/saved-search-plugin/public'; /** * Returns a valid view mode * @param viewMode - * @param isTextBasedQueryMode + * @param isEsqlMode */ export const getValidViewMode = ({ viewMode, - isTextBasedQueryMode, + isEsqlMode, }: { viewMode?: VIEW_MODE; - isTextBasedQueryMode: boolean; + isEsqlMode: boolean; }): VIEW_MODE | undefined => { - if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isTextBasedQueryMode) { - // only this mode is supported for text-based languages + if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isEsqlMode) { + // only this mode is supported for ES|QL languages return VIEW_MODE.DOCUMENT_LEVEL; } diff --git a/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts b/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts deleted file mode 100644 index 130007c55fc71..0000000000000 --- a/src/plugins/discover/public/application/main/utils/is_text_based_query.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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 { isTextBasedQuery } from './is_text_based_query'; - -describe('isTextBasedQuery', () => { - it('should work correctly', () => { - expect(isTextBasedQuery({ query: '', language: 'lucene' })).toEqual(false); - expect(isTextBasedQuery({ esql: 'from foo' })).toEqual(true); - expect(isTextBasedQuery()).toEqual(false); - }); -}); diff --git a/src/plugins/discover/public/application/main/utils/is_text_based_query.ts b/src/plugins/discover/public/application/main/utils/is_text_based_query.ts deleted file mode 100644 index d92433307aaa0..0000000000000 --- a/src/plugins/discover/public/application/main/utils/is_text_based_query.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 type { AggregateQuery, Query } from '@kbn/es-query'; -import { RecordRawType } from '../state_management/discover_data_state_container'; -import { getRawRecordType } from './get_raw_record_type'; - -/** - * Checks if the query is of AggregateQuery type - * @param query - */ -export function isTextBasedQuery(query?: Query | AggregateQuery): query is AggregateQuery { - return getRawRecordType(query) === RecordRawType.PLAIN; -} diff --git a/src/plugins/discover/public/application/types.ts b/src/plugins/discover/public/application/types.ts index 2b5c85db1e73a..6fb55945861eb 100644 --- a/src/plugins/discover/public/application/types.ts +++ b/src/plugins/discover/public/application/types.ts @@ -21,8 +21,8 @@ export enum FetchStatus { export interface RecordsFetchResponse { records: DataTableRecord[]; - textBasedQueryColumns?: DatatableColumn[]; - textBasedHeaderWarning?: string; + esqlQueryColumns?: DatatableColumn[]; + esqlHeaderWarning?: string; interceptedWarnings?: SearchResponseWarning[]; } diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx index 668f4090b3bdd..3907bc4999232 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx @@ -252,7 +252,7 @@ describe('Discover flyout', function () { expect(props.setExpandedDoc).not.toHaveBeenCalled(); }); - it('should not render single/surrounding views for text based', async () => { + it('should not render single/surrounding views for ES|QL', async () => { const { component } = await mountComponent({ query: { esql: 'FROM indexpattern' }, }); diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx index 012f3674f48f4..5fc88c8442a1a 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.tsx @@ -27,14 +27,13 @@ import { useEuiTheme, useIsWithinMinBreakpoint, } from '@elastic/eui'; -import type { Filter, Query, AggregateQuery } from '@kbn/es-query'; +import { Filter, Query, AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import type { DataTableColumnsMeta } from '@kbn/unified-data-table'; import { UnifiedDocViewer } from '@kbn/unified-doc-viewer-plugin/public'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { useDiscoverServices } from '../../hooks/use_discover_services'; -import { isTextBasedQuery } from '../../application/main/utils/is_text_based_query'; import { useFlyoutActions } from './use_flyout_actions'; import { useDiscoverCustomization } from '../../customizations'; import { DiscoverGridFlyoutActions } from './discover_grid_flyout_actions'; @@ -89,8 +88,7 @@ export function DiscoverGridFlyout({ const [flyoutWidth, setFlyoutWidth] = useLocalStorage(FLYOUT_WIDTH_KEY, defaultWidth); const minWidth = euiTheme.base * 24; const maxWidth = euiTheme.breakpoint.xl; - - const isPlainRecord = isTextBasedQuery(query); + const isEsqlQuery = isOfAggregateQueryType(query); // Get actual hit with updated highlighted searches const actualHit = useMemo(() => hits?.find(({ id }) => id === hit?.id) || hit, [hit, hits]); const pageCount = useMemo(() => (hits ? hits.length : 0), [hits]); @@ -173,7 +171,7 @@ export function DiscoverGridFlyout({ hit={actualHit} onAddColumn={addColumn} onRemoveColumn={removeColumn} - textBasedHits={isPlainRecord ? hits : undefined} + textBasedHits={isEsqlQuery ? hits : undefined} docViewsRegistry={flyoutCustomization?.docViewsRegistry} /> ), @@ -184,7 +182,7 @@ export function DiscoverGridFlyout({ columnsMeta, dataView, hits, - isPlainRecord, + isEsqlQuery, onFilter, removeColumn, flyoutCustomization?.docViewsRegistry, @@ -210,8 +208,8 @@ export function DiscoverGridFlyout({ renderDefaultContent() ); - const defaultFlyoutTitle = isPlainRecord - ? i18n.translate('discover.grid.tableRow.docViewerTextBasedDetailHeading', { + const defaultFlyoutTitle = isEsqlQuery + ? i18n.translate('discover.grid.tableRow.docViewerEsqlDetailHeading', { defaultMessage: 'Result', }) : i18n.translate('discover.grid.tableRow.docViewerDetailHeading', { @@ -272,7 +270,7 @@ export function DiscoverGridFlyout({ )} - {isPlainRecord || !flyoutActions.length ? null : ( + {isEsqlQuery || !flyoutActions.length ? null : ( <> diff --git a/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx b/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx index 4bdbeaa663818..5c58201bbf563 100644 --- a/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx +++ b/src/plugins/discover/public/components/discover_tour/discover_tour.test.tsx @@ -14,12 +14,16 @@ import { discoverServiceMock } from '../../__mocks__/services'; import { DiscoverTourProvider } from './discover_tour_provider'; import { useDiscoverTourContext } from './discover_tour_context'; import { DISCOVER_TOUR_STEP_ANCHORS } from './discover_tour_anchors'; +import { DiscoverMainProvider } from '../../application/main/state_management/discover_state_provider'; +import { getDiscoverStateMock } from '../../__mocks__/discover_state.mock'; describe('Discover tour', () => { const mountComponent = (innerContent: JSX.Element) => { return mountWithIntl( - {innerContent} + + {innerContent} + ); }; diff --git a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx index 45a870fbc2806..71ed5b85e5ee3 100644 --- a/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx +++ b/src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx @@ -29,6 +29,7 @@ import { PLUGIN_ID } from '../../../common'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import { DiscoverTourContext, DiscoverTourContextProps } from './discover_tour_context'; import { DISCOVER_TOUR_STEP_ANCHORS } from './discover_tour_anchors'; +import { useIsEsqlMode } from '../../application/main/hooks/use_is_esql_mode'; const MAX_WIDTH = 350; @@ -198,14 +199,9 @@ const tourConfig: EuiTourState = { tourSubtitle: '', }; -export const DiscoverTourProvider = ({ - children, - isPlainRecord, -}: { - children: ReactElement; - isPlainRecord: boolean; -}) => { +export const DiscoverTourProvider = ({ children }: { children: ReactElement }) => { const services = useDiscoverServices(); + const isEsqlMode = useIsEsqlMode(); const prependToBasePath = services.core.http.basePath.prepend; const getAssetPath = useCallback( (imageName: string) => { @@ -215,13 +211,13 @@ export const DiscoverTourProvider = ({ ); const tourSteps = useMemo( () => - isPlainRecord + isEsqlMode ? prepareTourSteps( [ADD_FIELDS_STEP, ORDER_TABLE_COLUMNS_STEP, CHANGE_ROW_HEIGHT_STEP], getAssetPath ) : prepareTourSteps(tourStepDefinitions, getAssetPath), - [getAssetPath, isPlainRecord] + [getAssetPath, isEsqlMode] ); const [steps, actions, reducerState] = useEuiTour(tourSteps, tourConfig); const currentTourStep = reducerState.currentTourStep; diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx index c4e53c265311d..ad8b1c5a2f547 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row.test.tsx @@ -125,10 +125,10 @@ describe('Doc table row component', () => { expect(findTestSubject(component, 'docTableRowDetailsTitle').exists()).toBeTruthy(); }); - it('should hide the single/surrounding views for text based languages', () => { + it('should hide the single/surrounding views for ES|QL mode', () => { const props = { ...defaultProps, - isPlainRecord: true, + isEsqlMode: true, }; const component = mountComponent(props); const toggleButton = findTestSubject(component, 'docTableExpandToggleColumn'); diff --git a/src/plugins/discover/public/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/components/doc_table/components/table_row.tsx index 7c9d55e7049bf..18790ace55289 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row.tsx @@ -34,7 +34,7 @@ export interface TableRowProps { columns: string[]; filter?: DocViewFilterFn; filters?: Filter[]; - isPlainRecord?: boolean; + isEsqlMode?: boolean; savedSearchId?: string; row: DataTableRecord; rows: DataTableRecord[]; @@ -47,7 +47,7 @@ export interface TableRowProps { export const TableRow = ({ filters, - isPlainRecord, + isEsqlMode, columns, filter, savedSearchId, @@ -219,7 +219,7 @@ export const TableRow = ({ columns={columns} filters={filters} savedSearchId={savedSearchId} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} > )} diff --git a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx b/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx index 5a0aea52de3f4..cd203091e30f7 100644 --- a/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx +++ b/src/plugins/discover/public/components/doc_table/components/table_row_details.tsx @@ -23,7 +23,7 @@ interface TableRowDetailsProps { dataView: DataView; filters?: Filter[]; savedSearchId?: string; - isPlainRecord?: boolean; + isEsqlMode?: boolean; } export const TableRowDetails = ({ @@ -36,7 +36,7 @@ export const TableRowDetails = ({ columns, filters, savedSearchId, - isPlainRecord, + isEsqlMode, }: TableRowDetailsProps) => { const { singleDocHref, contextViewHref, onOpenSingleDoc, onOpenContextView } = useNavigationProps( { @@ -60,13 +60,13 @@ export const TableRowDetails = ({

- {isPlainRecord && ( + {isEsqlMode && ( )} - {!isPlainRecord && ( + {!isEsqlMode && ( - {!isPlainRecord && ( + {!isEsqlMode && ( diff --git a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx index 491bdd1166ec7..54622ac546fa3 100644 --- a/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/create_doc_table_embeddable.tsx @@ -30,7 +30,7 @@ export function DiscoverDocTableEmbeddable(renderProps: DocTableEmbeddableProps) searchDescription={renderProps.searchDescription} sharedItemTitle={renderProps.sharedItemTitle} isLoading={renderProps.isLoading} - isPlainRecord={renderProps.isPlainRecord} + isEsqlMode={renderProps.isEsqlMode} interceptedWarnings={renderProps.interceptedWarnings} /> ); diff --git a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx index 0843d3baec295..58659b5d4b66a 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_embeddable.tsx @@ -29,7 +29,7 @@ export interface DocTableEmbeddableProps extends Omit; const DocTableWrapperMemoized = memo(DocTableWrapper); diff --git a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx index 5ad815d7c1cc6..3c9d2fc96ae0d 100644 --- a/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx +++ b/src/plugins/discover/public/components/doc_table/doc_table_wrapper.tsx @@ -62,9 +62,9 @@ export interface DocTableProps { filters?: Filter[]; /** * Flag which identifies if Discover operates - * in text based mode (ESQL) + * in ES|QL mode */ - isPlainRecord?: boolean; + isEsqlMode?: boolean; /** * Saved search id */ @@ -114,7 +114,7 @@ export const DocTableWrapper = forwardRef( render, columns, filters, - isPlainRecord, + isEsqlMode, savedSearchId, rows, dataView, @@ -186,7 +186,7 @@ export const DocTableWrapper = forwardRef( shouldShowFieldHandler={shouldShowFieldHandler} onAddColumn={onAddColumn} onRemoveColumn={onRemoveColumn} - isPlainRecord={isPlainRecord} + isEsqlMode={isEsqlMode} rows={rows} /> )); @@ -201,7 +201,7 @@ export const DocTableWrapper = forwardRef( shouldShowFieldHandler, onAddColumn, onRemoveColumn, - isPlainRecord, + isEsqlMode, rows, ] ); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx index 371ea1f0d06e2..afc59b6e7d0c6 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.test.tsx @@ -23,7 +23,7 @@ describe('Document view mode toggle component', () => { const mountComponent = ({ showFieldStatistics = true, viewMode = VIEW_MODE.DOCUMENT_LEVEL, - isTextBasedQuery = false, + isEsqlMode = false, setDiscoverViewMode = jest.fn(), } = {}) => { const services = { @@ -43,7 +43,7 @@ describe('Document view mode toggle component', () => { @@ -63,8 +63,8 @@ describe('Document view mode toggle component', () => { expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); }); - it('should not render if text-based', () => { - const component = mountComponent({ isTextBasedQuery: true }); + it('should not render if ES|QL', () => { + const component = mountComponent({ isEsqlMode: true }); expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); }); diff --git a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx index d96249827c300..b9b036e266b4e 100644 --- a/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx +++ b/src/plugins/discover/public/components/view_mode_toggle/view_mode_toggle.tsx @@ -18,13 +18,13 @@ import { HitsCounter, HitsCounterMode } from '../hits_counter'; export const DocumentViewModeToggle = ({ viewMode, - isTextBasedQuery, + isEsqlMode, prepend, stateContainer, setDiscoverViewMode, }: { viewMode: VIEW_MODE; - isTextBasedQuery: boolean; + isEsqlMode: boolean; prepend?: ReactElement; stateContainer: DiscoverStateContainer; setDiscoverViewMode: (viewMode: VIEW_MODE) => void; @@ -32,8 +32,8 @@ export const DocumentViewModeToggle = ({ const { euiTheme } = useEuiTheme(); const { uiSettings, dataVisualizer: dataVisualizerService } = useDiscoverServices(); const isLegacy = useMemo( - () => isLegacyTableEnabled({ uiSettings, isTextBasedQueryMode: isTextBasedQuery }), - [uiSettings, isTextBasedQuery] + () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), + [uiSettings, isEsqlMode] ); const includesNormalTabsStyle = viewMode === VIEW_MODE.AGGREGATED_LEVEL || isLegacy; @@ -72,7 +72,7 @@ export const DocumentViewModeToggle = ({ )} - {isTextBasedQuery || !showViewModeToggle ? ( + {isEsqlMode || !showViewModeToggle ? ( ) : ( diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx index d5aac2dc246a8..06aeaa42c1376 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable.tsx @@ -13,6 +13,7 @@ import { Query, TimeRange, FilterStateStore, + isOfAggregateQueryType, } from '@kbn/es-query'; import React from 'react'; import ReactDOM, { unmountComponentAtNode } from 'react-dom'; @@ -66,8 +67,7 @@ import { SavedSearchEmbeddableComponent } from './saved_search_embeddable_compon import { handleSourceColumnState } from '../utils/state_helpers'; import { updateSearchSource } from './utils/update_search_source'; import { FieldStatisticsTable } from '../application/main/components/field_stats_table'; -import { fetchTextBased } from '../application/main/data_fetching/fetch_text_based'; -import { isTextBasedQuery } from '../application/main/utils/is_text_based_query'; +import { fetchEsql } from '../application/main/data_fetching/fetch_esql'; import { getValidViewMode } from '../application/main/utils/get_valid_view_mode'; import { ADHOC_DATA_VIEW_RENDER_EVENT } from '../constants'; import { getDiscoverLocatorParams } from './get_discover_locator_params'; @@ -229,9 +229,9 @@ export class SavedSearchEmbeddable return true; } - private isTextBasedSearch = (savedSearch: SavedSearch): boolean => { + private isEsqlMode = (savedSearch: SavedSearch): boolean => { const query = savedSearch.searchSource.getField('query'); - return isTextBasedQuery(query); + return isOfAggregateQueryType(query); }; private getFetchedSampleSize = (searchProps: SearchProps): number => { @@ -302,12 +302,12 @@ export class SavedSearchEmbeddable const query = savedSearch.searchSource.getField('query'); const dataView = savedSearch.searchSource.getField('index')!; - const useTextBased = this.isTextBasedSearch(savedSearch); + const isEsqlMode = this.isEsqlMode(savedSearch); try { - // Request text based data - if (useTextBased && query) { - const result = await fetchTextBased( + // Request ES|QL data + if (isEsqlMode && query) { + const result = await fetchEsql( savedSearch.searchSource.getField('query')!, dataView, this.services.data, @@ -323,8 +323,8 @@ export class SavedSearchEmbeddable loading: false, }); - searchProps.columnsMeta = result.textBasedQueryColumns - ? getTextBasedColumnsMeta(result.textBasedQueryColumns) + searchProps.columnsMeta = result.esqlQueryColumns + ? getTextBasedColumnsMeta(result.esqlQueryColumns) : undefined; searchProps.rows = result.records; searchProps.totalHitCount = result.records.length; @@ -392,9 +392,9 @@ export class SavedSearchEmbeddable private getSort( sort: SortPair[] | undefined, dataView: DataView | undefined, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ) { - return getSortForEmbeddable(sort, dataView, this.services.uiSettings, isTextBasedQueryMode); + return getSortForEmbeddable(sort, dataView, this.services.uiSettings, isEsqlMode); } private initializeSearchEmbeddableProps() { @@ -421,7 +421,7 @@ export class SavedSearchEmbeddable filters: savedSearch.searchSource.getField('filter') as Filter[], dataView, isLoading: false, - sort: this.getSort(savedSearch.sort, dataView, this.isTextBasedSearch(savedSearch)), + sort: this.getSort(savedSearch.sort, dataView, this.isEsqlMode(savedSearch)), rows: [], searchDescription: savedSearch.description, description: savedSearch.description, @@ -460,7 +460,7 @@ export class SavedSearchEmbeddable this.updateInput({ sort: sortOrderArr }); }, // I don't want to create filters when is embedded - ...(!this.isTextBasedSearch(savedSearch) && { + ...(!this.isEsqlMode(savedSearch) && { onFilter: async (field, value, operator) => { let filters = generateFilters( this.services.filterManager, @@ -583,7 +583,7 @@ export class SavedSearchEmbeddable searchProps.sort = this.getSort( this.input.sort || savedSearch.sort, searchProps?.dataView, - this.isTextBasedSearch(savedSearch) + this.isEsqlMode(savedSearch) ); searchProps.sharedItemTitle = this.panelTitleInternal; searchProps.searchTitle = this.panelTitleInternal; @@ -640,10 +640,10 @@ export class SavedSearchEmbeddable return; } - const isTextBasedQueryMode = this.isTextBasedSearch(savedSearch); + const isEsqlMode = this.isEsqlMode(savedSearch); const viewMode = getValidViewMode({ viewMode: savedSearch.viewMode, - isTextBasedQueryMode, + isEsqlMode, }); if ( @@ -680,7 +680,7 @@ export class SavedSearchEmbeddable const useLegacyTable = isLegacyTableEnabled({ uiSettings: this.services.uiSettings, - isTextBasedQueryMode, + isEsqlMode, }); const query = savedSearch.searchSource.getField('query'); const props = { diff --git a/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx b/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx index 7c938db1018ac..6114adb414568 100644 --- a/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx +++ b/src/plugins/discover/public/embeddable/saved_search_embeddable_component.tsx @@ -7,11 +7,10 @@ */ import React from 'react'; -import { AggregateQuery, Query } from '@kbn/es-query'; +import { AggregateQuery, isOfAggregateQueryType, Query } from '@kbn/es-query'; import { DataLoadingState } from '@kbn/unified-data-table'; import { DiscoverGridEmbeddable } from './saved_search_grid'; import { DiscoverDocTableEmbeddable } from '../components/doc_table/create_doc_table_embeddable'; -import { isTextBasedQuery } from '../application/main/utils/is_text_based_query'; import type { EmbeddableComponentSearchProps } from './types'; interface SavedSearchEmbeddableComponentProps { @@ -31,15 +30,15 @@ export function SavedSearchEmbeddableComponent({ query, }: SavedSearchEmbeddableComponentProps) { if (useLegacyTable) { - const isPlainRecord = isTextBasedQuery(query); return ( ); } + return ( { `); }); - test('fields do not have prepended timeField for text based languages', async () => { + test('fields do not have prepended timeField for ES|QL', async () => { const index = { ...dataViewMock } as DataView; index.timeFieldName = 'cool-timefield'; diff --git a/src/plugins/discover/public/utils/get_sharing_data.ts b/src/plugins/discover/public/utils/get_sharing_data.ts index b32f64cf79fb5..68e889c9b3cb0 100644 --- a/src/plugins/discover/public/utils/get_sharing_data.ts +++ b/src/plugins/discover/public/utils/get_sharing_data.ts @@ -34,7 +34,7 @@ export async function getSharingData( currentSearchSource: ISearchSource, state: DiscoverAppState | SavedSearch, services: { uiSettings: IUiSettingsClient; data: DataPublicPluginStart }, - isPlainRecord?: boolean + isEsqlMode?: boolean ) { const { uiSettings, data } = services; const searchSource = currentSearchSource.createCopy(); @@ -61,7 +61,7 @@ export async function getSharingData( // conditionally add the time field column: let timeFieldName: string | undefined; const hideTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING); - if (!hideTimeColumn && index && index.timeFieldName && !isPlainRecord) { + if (!hideTimeColumn && index && index.timeFieldName && !isEsqlMode) { timeFieldName = index.timeFieldName; } if (timeFieldName && !columns.includes(timeFieldName)) { diff --git a/src/plugins/discover/public/utils/sorting/get_sort.ts b/src/plugins/discover/public/utils/sorting/get_sort.ts index 74d7fe4b7fb1e..3c6be442a3c6e 100644 --- a/src/plugins/discover/public/utils/sorting/get_sort.ts +++ b/src/plugins/discover/public/utils/sorting/get_sort.ts @@ -19,7 +19,7 @@ export function getSortForEmbeddable( sort: SortInput | undefined, dataView: DataView | undefined, uiSettings: IUiSettingsClient | undefined, - isTextBasedQueryMode: boolean + isEsqlMode: boolean ): SortOrder[] { if (!sort || !sort.length || !dataView) { if (!uiSettings) { @@ -27,7 +27,7 @@ export function getSortForEmbeddable( } const defaultSortOrder = uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc'); const hidingTimeColumn = uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING, false); - return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn, isTextBasedQueryMode); + return getDefaultSort(dataView, defaultSortOrder, hidingTimeColumn, isEsqlMode); } - return getSortArray(sort, dataView, isTextBasedQueryMode); + return getSortArray(sort, dataView, isEsqlMode); } diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx index 740afc99a9c62..5596ca067df5f 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx @@ -56,7 +56,7 @@ export const DocViewerSource = ({ const useNewFieldsApi = !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); const useDocExplorer = !isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: Array.isArray(textBasedHits), + isEsqlMode: Array.isArray(textBasedHits), }); const [requestState, hit] = useEsDocSearch({ id, diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index 1fb690694096d..5d26f43892ae1 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -82,7 +82,7 @@ export class UnifiedDocViewerPublicPlugin const LazyDocView = isLegacyTableEnabled({ uiSettings, - isTextBasedQueryMode: Array.isArray(textBasedHits), + isEsqlMode: Array.isArray(textBasedHits), }) ? LazyDocViewerLegacyTable : LazyDocViewerTable; diff --git a/test/functional/apps/discover/group3/_lens_vis.ts b/test/functional/apps/discover/group3/_lens_vis.ts index a01760da64658..cc48864976811 100644 --- a/test/functional/apps/discover/group3/_lens_vis.ts +++ b/test/functional/apps/discover/group3/_lens_vis.ts @@ -179,7 +179,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should show ESQL histogram for text-based query', async () => { + it('should show ESQL histogram for ES|QL query', async () => { await PageObjects.discover.selectTextBaseLang(); await monacoEditor.setCodeEditorValue('from logstash-* | limit 10'); diff --git a/test/functional/apps/discover/group3/_panels_toggle.ts b/test/functional/apps/discover/group3/_panels_toggle.ts index cbac15a3e45de..8b19fb5cc83c5 100644 --- a/test/functional/apps/discover/group3/_panels_toggle.ts +++ b/test/functional/apps/discover/group3/_panels_toggle.ts @@ -218,7 +218,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: false, totalHits: '14,004' }); }); - describe('text-based with histogram chart', function () { + describe('ES|QL with histogram chart', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); @@ -231,7 +231,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: true, totalHits: '10' }); }); - describe('text-based with aggs chart', function () { + describe('ES|QL with aggs chart', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); @@ -249,7 +249,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { checkPanelsToggle({ isChartAvailable: true, totalHits: '5' }); }); - describe('text-based without a time field', function () { + describe('ES|QL without a time field', function () { before(async function () { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await kibanaServer.uiSettings.update(defaultSettings); diff --git a/test/functional/apps/discover/group6/_sidebar.ts b/test/functional/apps/discover/group6/_sidebar.ts index aac4bd60933f0..9f7b35abc85da 100644 --- a/test/functional/apps/discover/group6/_sidebar.ts +++ b/test/functional/apps/discover/group6/_sidebar.ts @@ -96,7 +96,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should show filters by type in text-based view', async function () { + it('should show filters by type in ES|QL view', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); await PageObjects.unifiedFieldList.openSidebarFieldFilter(); let options = await find.allByCssSelector('[data-test-subj*="typeFilter"]'); @@ -128,7 +128,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should show empty fields in text-based view', async function () { + it('should show empty fields in ES|QL view', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); await PageObjects.discover.selectTextBaseLang(); @@ -427,7 +427,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); }); - it('should show selected and available fields in text-based mode', async function () { + it('should show selected and available fields in ES|QL mode', async function () { await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); expect(await PageObjects.unifiedFieldList.getSidebarAriaDescription()).to.be( diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts index b2526445f75bf..cbeb128036ab6 100644 --- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts +++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts @@ -143,7 +143,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('text based columns', function () { + describe('ES|QL columns', function () { beforeEach(async () => { await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); await PageObjects.common.navigateToApp('discover'); diff --git a/test/functional/apps/discover/group6/_time_field_column.ts b/test/functional/apps/discover/group6/_time_field_column.ts index cbcb37a1294e7..6fe4c244a08b3 100644 --- a/test/functional/apps/discover/group6/_time_field_column.ts +++ b/test/functional/apps/discover/group6/_time_field_column.ts @@ -57,12 +57,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField, hideTimeFieldColumnSetting, savedSearchSuffix, - isTextBased, + isEsqlMode, }: { hasTimeField: boolean; hideTimeFieldColumnSetting: boolean; savedSearchSuffix: string; - isTextBased?: boolean; + isEsqlMode?: boolean; }) { // check in Discover expect(await dataGrid.getHeaderFields()).to.eql( @@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.saveSearch(`${SEARCH_NO_COLUMNS}${savedSearchSuffix}`); await PageObjects.discover.waitUntilSearchingHasFinished(); - const isTimestampUnavailableInSidebar = isTextBased && !hasTimeField; + const isTimestampUnavailableInSidebar = isEsqlMode && !hasTimeField; if (!isTimestampUnavailableInSidebar) { await PageObjects.unifiedFieldList.clickFieldListItemAdd('@timestamp'); await PageObjects.discover.waitUntilSearchingHasFinished(); @@ -137,12 +137,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField, hideTimeFieldColumnSetting, savedSearchSuffix, - isTextBased, + isEsqlMode, }: { hasTimeField: boolean; hideTimeFieldColumnSetting: boolean; savedSearchSuffix: string; - isTextBased?: boolean; + isEsqlMode?: boolean; }) { // check in Discover await PageObjects.unifiedFieldList.clickFieldListItemAdd('bytes'); @@ -150,7 +150,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -183,7 +183,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.unifiedFieldList.clickFieldListItemRemove('@timestamp'); await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -199,7 +199,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await retry.try(async () => { expect(await dataGrid.getHeaderFields()).to.eql( - hideTimeFieldColumnSetting || !hasTimeField || isTextBased + hideTimeFieldColumnSetting || !hasTimeField || isEsqlMode ? ['bytes', 'extension'] : ['@timestamp', 'bytes', 'extension'] ); @@ -292,7 +292,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: true, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQL', - isTextBased: true, + isEsqlMode: true, }); }); @@ -306,7 +306,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: false, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQLdrop', - isTextBased: true, + isEsqlMode: true, }); }); @@ -317,7 +317,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { hasTimeField: true, hideTimeFieldColumnSetting, savedSearchSuffix: savedSearchSuffix + 'ESQL', - isTextBased: true, + isEsqlMode: true, }); }); }); diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 935b75bf61937..eada1ec26f7aa 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -99,7 +99,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('dscViewModeToggle'); }); - it('should not show view mode toggle for text-based searches', async () => { + it('should not show view mode toggle for ES|QL searches', async () => { await testSubjects.click('dscViewModeDocumentButton'); await retry.try(async () => { @@ -118,7 +118,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }); - it('should show text-based columns callout', async () => { + it('should show ES|QL columns callout', async () => { await testSubjects.missingOrFail('dscSelectedColumnsCallout'); await PageObjects.unifiedFieldList.clickFieldListItemAdd('extension'); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 103c47a238357..017acbe370416 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -2278,7 +2278,7 @@ "discover.showingDefaultDataViewWarningDescription": "Affichage de la vue de données par défaut : \"{loadedDataViewTitle}\" ({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "Affichage de la vue de données enregistrée : \"{ownDataViewTitle}\" ({ownDataViewId})", "discover.singleDocRoute.errorMessage": "Aucune donnée correspondante pour l'ID {dataViewId}", - "discover.textBasedMode.selectedColumnsCallout": "Affichage de {selectedColumnsNumber} champs sur {textBasedQueryColumnsNumber}. Ajoutez-en d’autres depuis la liste des champs disponibles.", + "discover.esqlMode.selectedColumnsCallout": "Affichage de {selectedColumnsNumber} champs sur {esqlQueryColumnsNumber}. Ajoutez-en d’autres depuis la liste des champs disponibles.", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal} n'est pas un ID de vue de données configuré", "discover.viewAlert.dataViewErrorText": "Échec de la vue des données de la règle d'alerte avec l'ID {alertId}.", "discover.advancedSettings.context.defaultSizeText": "Le nombre d'entrées connexes à afficher dans la vue contextuelle", @@ -2414,10 +2414,10 @@ "discover.grid.flyout.toastColumnRemoved": "La colonne \"{columnName}\" a été supprimée.", "discover.grid.tableRow.actionsLabel": "Actions", "discover.grid.tableRow.docViewerDetailHeading": "Document", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "Ligne", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "Ligne", "discover.grid.tableRow.mobileFlyoutActionsButton": "Actions", "discover.grid.tableRow.moreFlyoutActionsButton": "Plus d'actions", - "discover.grid.tableRow.textBasedDetailHeading": "Ligne développée", + "discover.grid.tableRow.esqlDetailHeading": "Ligne développée", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "Afficher un seul document", "discover.grid.tableRow.viewSurroundingDocumentsHover": "Inspectez des documents qui ont été créés avant et après ce document. Seuls les filtres épinglés restent actifs dans la vue Documents relatifs.", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "Afficher les documents alentour", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0c0fa7527290e..f03111faf3bd1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2276,7 +2276,7 @@ "discover.showingDefaultDataViewWarningDescription": "デフォルトデータビューを表示しています:\"{loadedDataViewTitle}\" ({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "保存されたデータビューを表示しています:\"{ownDataViewTitle}\" ({ownDataViewId})", "discover.singleDocRoute.errorMessage": "ID {dataViewId}の一致するデータビューが見つかりません", - "discover.textBasedMode.selectedColumnsCallout": "{textBasedQueryColumnsNumber}フィールド中{selectedColumnsNumber}フィールドを表示中です。利用可能なフィールドリストからさらに追加します。", + "discover.esqlMode.selectedColumnsCallout": "{esqlQueryColumnsNumber}フィールド中{selectedColumnsNumber}フィールドを表示中です。利用可能なフィールドリストからさらに追加します。", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal}は設定されたデータビューIDではありません", "discover.viewAlert.dataViewErrorText": "id {alertId}のアラートルールのデータビューのエラー。", "discover.advancedSettings.context.defaultSizeText": "コンテキストビューに表示される周りのエントリーの数", @@ -2412,10 +2412,10 @@ "discover.grid.flyout.toastColumnRemoved": "列'{columnName}'が削除されました", "discover.grid.tableRow.actionsLabel": "アクション", "discover.grid.tableRow.docViewerDetailHeading": "ドキュメント", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "行", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "行", "discover.grid.tableRow.mobileFlyoutActionsButton": "アクション", "discover.grid.tableRow.moreFlyoutActionsButton": "さらにアクションを表示", - "discover.grid.tableRow.textBasedDetailHeading": "展開された行", + "discover.grid.tableRow.esqlDetailHeading": "展開された行", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "単一のドキュメントを表示", "discover.grid.tableRow.viewSurroundingDocumentsHover": "このドキュメントの前後に出現したドキュメントを検査します。周りのドキュメントビューでは、固定されたフィルターのみがアクティブのままです。", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "周りのドキュメントを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b14b4d28e33a9..968e567a714bd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2280,7 +2280,7 @@ "discover.showingDefaultDataViewWarningDescription": "正在显示默认数据视图:“{loadedDataViewTitle}”({loadedDataViewId})", "discover.showingSavedDataViewWarningDescription": "正在显示已保存数据视图:“{ownDataViewTitle}”({ownDataViewId})", "discover.singleDocRoute.errorMessage": "没有与 ID {dataViewId} 相匹配的数据视图", - "discover.textBasedMode.selectedColumnsCallout": "正在显示 {selectedColumnsNumber} 个字段,共 {textBasedQueryColumnsNumber} 个。从可用字段列表中添加更多字段。", + "discover.esqlMode.selectedColumnsCallout": "正在显示 {selectedColumnsNumber} 个字段,共 {esqlQueryColumnsNumber} 个。从可用字段列表中添加更多字段。", "discover.valueIsNotConfiguredDataViewIDWarningTitle": "{stateVal} 不是配置的数据视图 ID", "discover.viewAlert.dataViewErrorText": "ID 为 {alertId} 的告警规则的数据视图失败。", "discover.advancedSettings.context.defaultSizeText": "要在上下文视图中显示的周围条目数目", @@ -2416,10 +2416,10 @@ "discover.grid.flyout.toastColumnRemoved": "已移除列“{columnName}”", "discover.grid.tableRow.actionsLabel": "操作", "discover.grid.tableRow.docViewerDetailHeading": "文档", - "discover.grid.tableRow.docViewerTextBasedDetailHeading": "行", + "discover.grid.tableRow.docViewerEsqlDetailHeading": "行", "discover.grid.tableRow.mobileFlyoutActionsButton": "操作", "discover.grid.tableRow.moreFlyoutActionsButton": "更多操作", - "discover.grid.tableRow.textBasedDetailHeading": "已展开行", + "discover.grid.tableRow.esqlDetailHeading": "已展开行", "discover.grid.tableRow.viewSingleDocumentLinkLabel": "查看单个文档", "discover.grid.tableRow.viewSurroundingDocumentsHover": "检查在此文档之前和之后出现的文档。在周围文档视图中,仅已固定筛选仍处于活动状态。", "discover.grid.tableRow.viewSurroundingDocumentsLinkLabel": "查看周围文档", diff --git a/x-pack/test/functional/apps/discover/visualize_field.ts b/x-pack/test/functional/apps/discover/visualize_field.ts index b1eadf89a5297..bb59bbc7af283 100644 --- a/x-pack/test/functional/apps/discover/visualize_field.ts +++ b/x-pack/test/functional/apps/discover/visualize_field.ts @@ -141,7 +141,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await dataViews.waitForSwitcherToBe('logst*'); }); - it('should visualize correctly text based language queries in Discover', async () => { + it('should visualize correctly ES|QL queries in Discover', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue( @@ -182,7 +182,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { assertMatchesExpectedData(data!); }); - it('should visualize correctly text based language queries in Lens', async () => { + it('should visualize correctly ES|QL queries in Lens', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue( @@ -201,7 +201,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - it('should visualize correctly text based language queries based on index patterns', async () => { + it('should visualize correctly ES|QL queries based on index patterns', async () => { await PageObjects.discover.selectTextBaseLang(); await PageObjects.header.waitUntilLoadingHasFinished(); await monacoEditor.setCodeEditorValue(