From f5fa6c6abb49c9336e9eb47def8ce1e7391411fa Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 23 Oct 2024 16:30:19 -0500 Subject: [PATCH 01/29] Add getFieldStatsWarningMsgForQuery --- .../components/field_stats/field_stats.tsx | 14 + .../src/utils/get_warning_message.ts | 31 + .../view_mode_toggle/view_mode_toggle.tsx | 19 +- .../src/components/date_picker_wrapper.tsx | 48 +- .../data_visualizer_stats_table.tsx | 791 +++++++++--------- .../index_data_visualizer_esql.tsx | 31 +- 6 files changed, 517 insertions(+), 417 deletions(-) create mode 100644 packages/kbn-unified-field-list/src/utils/get_warning_message.ts diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 8eada232cdeaf..be709c0eff70b 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -53,6 +53,7 @@ import { import { FieldSummaryMessage } from './field_summary_message'; import { FieldNumberSummary, isNumberSummaryValid } from './field_number_summary'; import { ErrorBoundary } from '../error_boundary'; +import { getReasonIfQueryUnsupportedByFieldStats } from '../../utils/get_warning_message'; export interface FieldStatsState { isLoading: boolean; @@ -165,6 +166,8 @@ const FieldStatsComponent: React.FC = ({ [changeDataView, isCanceledRef] ); + const warningMsgForQuery = useMemo(() => getReasonIfQueryUnsupportedByFieldStats(query), [query]); + async function fetchData() { if (isCanceledRef.current) { return; @@ -336,6 +339,17 @@ const FieldStatsComponent: React.FC = ({ : messageNoAnalysis; } + const unsupportedReason = getReasonIfQueryUnsupportedByFieldStats(query); + if (unsupportedReason) { + const messageUnsupportedReason = ; + + return overrideMissingContent + ? overrideMissingContent({ + reason: 'unsupported', + element: messageUnsupportedReason, + }) + : messageUnsupportedReason; + } if (canProvideNumberSummaryForField(field, isTextBased) && isNumberSummaryValid(numberSummary)) { title = ( diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts new file mode 100644 index 0000000000000..d3b701c2f800c --- /dev/null +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -0,0 +1,31 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +import type { AggregateQuery, Query } from '@kbn/es-query'; +import { Walker } from '@kbn/esql-ast'; +import { parse } from '@kbn/esql-ast'; +import { isOfAggregateQueryType } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; + +export const getReasonIfQueryUnsupportedByFieldStats = ( + query?: AggregateQuery | Query | { [key: string]: any } +): string | undefined => { + if (isOfAggregateQueryType(query)) { + const { root } = parse(query.esql); + + if (Walker.hasFunction(root, 'match')) { + return i18n.translate( + 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', + { + defaultMessage: + 'Field statistics is unavailable for ES|QL queries containing `MATCH` function', + } + ); + } + } +}; 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 10a92dc8fefa9..06a5d499200a5 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 @@ -8,12 +8,13 @@ */ import React, { useMemo, useEffect, useState, type ReactElement, useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiToolTip, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; import useMountedState from 'react-use/lib/useMountedState'; +import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../application/main/state_management/discover_state'; @@ -46,6 +47,11 @@ export const DocumentViewModeToggle = ({ () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), [uiSettings, isEsqlMode] ); + const state = stateContainer.appState.getState(); + const fieldStatsWarningMsgForQuery = useMemo(() => { + return getReasonIfQueryUnsupportedByFieldStats(state.query); + }, [state.query]); + const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( () => uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, @@ -150,14 +156,17 @@ export const DocumentViewModeToggle = ({ {showFieldStatisticsTab ? ( setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} data-test-subj="dscViewModeFieldStatsButton" > - + + + ) : null} diff --git a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx index 8f42d755144a8..c8792ab3f9d9e 100644 --- a/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx +++ b/x-pack/packages/ml/date_picker/src/components/date_picker_wrapper.tsx @@ -18,6 +18,7 @@ import { EuiFlexItem, EuiSuperDatePicker, type EuiSuperDatePickerProps, + EuiToolTip, } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; @@ -103,6 +104,10 @@ interface DatePickerWrapperProps { * when EuiSuperDatePicker's 'Refresh'|'Update' button is clicked */ onRefresh?: () => void; + /** + * Tooltip message for the update button + */ + tooltipMessage?: string; } /** @@ -122,6 +127,7 @@ export const DatePickerWrapper: FC = (props) => { isDisabled = false, needsUpdate, onRefresh, + tooltipMessage, } = props; const { data, @@ -318,34 +324,40 @@ export const DatePickerWrapper: FC = (props) => { recentlyUsedRanges={recentlyUsedRanges} dateFormat={dateFormat} commonlyUsedRanges={commonlyUsedRanges} + isDisabled={isDisabled} updateButtonProps={{ iconOnly: isWithinLBreakpoint, fill: false, ...(needsUpdate ? { needsUpdate } : {}), }} width={width} - isDisabled={isDisabled} /> {showRefresh === true || !isTimeRangeSelectorEnabled ? ( - - {needsUpdate ? ( - - ) : ( - - )} - + + + {needsUpdate ? ( + + ) : ( + + )} + + ) : null} diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx index 811d69b6bfeb3..a0ad141a57e97 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx @@ -65,444 +65,459 @@ interface DataVisualizerTableProps { error?: Error | string; } -export const DataVisualizerTable = ({ - items, - pageState, - updatePageState, - getItemIdToExpandedRowMap, - extendedColumns, - showPreviewByDefault, - onChange, - loading, - totalCount, - overallStatsRunning, - renderFieldName, - error, -}: DataVisualizerTableProps) => { - const { euiTheme } = useEuiTheme(); - - const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); - const [expandAll, setExpandAll] = useState(false); - - const { onTableChange, pagination, sorting } = useTableSettings( +export const DataVisualizerTable = React.memo( + ({ items, pageState, - updatePageState - ); - const [showDistributions, setShowDistributions] = useState(showPreviewByDefault ?? true); - const [dimensions, setDimensions] = useState(calculateTableColumnsDimensions()); + updatePageState, + getItemIdToExpandedRowMap, + extendedColumns, + showPreviewByDefault, + onChange, + loading, + totalCount, + overallStatsRunning, + renderFieldName, + error, + }: DataVisualizerTableProps) => { + const { euiTheme } = useEuiTheme(); - const toggleExpandAll = useCallback( - (shouldExpandAll: boolean) => { - setExpandedRowItemIds( - shouldExpandAll - ? // Update list of ids in expandedRowIds to include all - (items.map((item) => item.fieldName).filter((id) => id !== undefined) as string[]) - : // Otherwise, reset list of ids in expandedRowIds - [] - ); - setExpandAll(shouldExpandAll); - }, - [items] - ); - const isMounted = useMountedState(); + const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); + const [expandAll, setExpandAll] = useState(false); - // eslint-disable-next-line react-hooks/exhaustive-deps - const resizeHandler = useCallback( - throttle((e: { width: number; height: number }) => { - // When window or table is resized, - // update the column widths and other settings accordingly - if (isMounted()) { - setDimensions(calculateTableColumnsDimensions(e.width)); - } - }, 500), - [] - ); + const { onTableChange, pagination, sorting } = useTableSettings( + items, + pageState, + updatePageState + ); + const [showDistributions, setShowDistributions] = useState( + showPreviewByDefault ?? true + ); + const [dimensions, setDimensions] = useState(calculateTableColumnsDimensions()); - const toggleShowDistribution = useCallback(() => { - setShowDistributions(!showDistributions); - if (onChange) { - onChange({ showDistributions: !showDistributions }); - } - }, [onChange, showDistributions]); + const toggleExpandAll = useCallback( + (shouldExpandAll: boolean) => { + setExpandedRowItemIds( + shouldExpandAll + ? // Update list of ids in expandedRowIds to include all + (items.map((item) => item.fieldName).filter((id) => id !== undefined) as string[]) + : // Otherwise, reset list of ids in expandedRowIds + [] + ); + setExpandAll(shouldExpandAll); + }, + [items] + ); + const isMounted = useMountedState(); - function toggleDetails(item: DataVisualizerTableItem) { - if (item.fieldName === undefined) return; - const index = expandedRowItemIds.indexOf(item.fieldName); - if (index !== -1) { - expandedRowItemIds.splice(index, 1); - } else { - expandedRowItemIds.push(item.fieldName); - } + // eslint-disable-next-line react-hooks/exhaustive-deps + const resizeHandler = useCallback( + throttle((e: { width: number; height: number }) => { + // When window or table is resized, + // update the column widths and other settings accordingly + if (isMounted()) { + setDimensions(calculateTableColumnsDimensions(e.width)); + } + }, 500), + [] + ); - // spread to a new array otherwise the component wouldn't re-render - setExpandedRowItemIds([...expandedRowItemIds]); - } + const toggleShowDistribution = useCallback(() => { + setShowDistributions(!showDistributions); + if (onChange) { + onChange({ showDistributions: !showDistributions }); + } + }, [onChange, showDistributions]); - const columns = useMemo(() => { - const expanderColumn: EuiTableComputedColumnType = { - name: - // EUI will automatically show an expander button when table is mobile view (where width <700) - // so we need to not render any addition button - dimensions.breakPoint !== 'small' ? ( - toggleExpandAll(!expandAll)} - aria-label={ - !expandAll - ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { - defaultMessage: 'Expand details for all fields', - }) - : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { - defaultMessage: 'Collapse details for all fields', - }) - } - iconType={expandAll ? 'arrowDown' : 'arrowRight'} - /> - ) : null, - align: RIGHT_ALIGNMENT, - width: dimensions.expander, - isExpander: true, - render: (item: DataVisualizerTableItem) => { - const displayName = item.displayName ?? item.fieldName; - if (item.fieldName === undefined) return null; - const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowDown' : 'arrowRight'; - return ( - toggleDetails(item)} - aria-label={ - expandedRowItemIds.includes(item.fieldName) - ? i18n.translate('xpack.dataVisualizer.dataGrid.rowCollapse', { - defaultMessage: 'Hide details for {fieldName}', - values: { fieldName: displayName }, - }) - : i18n.translate('xpack.dataVisualizer.dataGrid.rowExpand', { - defaultMessage: 'Show details for {fieldName}', - values: { fieldName: displayName }, - }) - } - iconType={direction} - /> - ); - }, - 'data-test-subj': 'dataVisualizerTableColumnDetailsToggle', - }; + function toggleDetails(item: DataVisualizerTableItem) { + if (item.fieldName === undefined) return; + const index = expandedRowItemIds.indexOf(item.fieldName); + if (index !== -1) { + expandedRowItemIds.splice(index, 1); + } else { + expandedRowItemIds.push(item.fieldName); + } - const baseColumns = [ - expanderColumn, - { - field: 'type', - name: i18n.translate('xpack.dataVisualizer.dataGrid.typeColumnName', { - defaultMessage: 'Type', - }), - render: (fieldType: SupportedFieldType, item: DataVisualizerTableItem) => { - return ; - }, - width: dimensions.type, - sortable: true, - align: CENTER_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnType', - }, - { - field: 'fieldName', - name: i18n.translate('xpack.dataVisualizer.dataGrid.nameColumnName', { - defaultMessage: 'Name', - }), - sortable: true, - truncateText: true, - render: (fieldName: string, item: DataVisualizerTableItem) => { - const displayName = item.displayName ?? item.fieldName; + // spread to a new array otherwise the component wouldn't re-render + setExpandedRowItemIds([...expandedRowItemIds]); + } + const columns = useMemo(() => { + const expanderColumn: EuiTableComputedColumnType = { + name: + // EUI will automatically show an expander button when table is mobile view (where width <700) + // so we need to not render any addition button + dimensions.breakPoint !== 'small' ? ( + toggleExpandAll(!expandAll)} + aria-label={ + !expandAll + ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { + defaultMessage: 'Expand details for all fields', + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { + defaultMessage: 'Collapse details for all fields', + }) + } + iconType={expandAll ? 'arrowDown' : 'arrowRight'} + /> + ) : null, + align: RIGHT_ALIGNMENT, + width: dimensions.expander, + isExpander: true, + render: (item: DataVisualizerTableItem) => { + const displayName = item.displayName ?? item.fieldName; + if (item.fieldName === undefined) return null; + const direction = expandedRowItemIds.includes(item.fieldName) + ? 'arrowDown' + : 'arrowRight'; return ( - - {renderFieldName ? renderFieldName(fieldName, item) : displayName} - + toggleDetails(item)} + aria-label={ + expandedRowItemIds.includes(item.fieldName) + ? i18n.translate('xpack.dataVisualizer.dataGrid.rowCollapse', { + defaultMessage: 'Hide details for {fieldName}', + values: { fieldName: displayName }, + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.rowExpand', { + defaultMessage: 'Show details for {fieldName}', + values: { fieldName: displayName }, + }) + } + iconType={direction} + /> ); }, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnName', - }, - { - field: 'docCount', - name: ( -
- {i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnName', { - defaultMessage: 'Documents (%)', - })} - -
- ), + 'data-test-subj': 'dataVisualizerTableColumnDetailsToggle', + }; + + const baseColumns = [ + expanderColumn, + { + field: 'type', + name: i18n.translate('xpack.dataVisualizer.dataGrid.typeColumnName', { + defaultMessage: 'Type', + }), + render: (fieldType: SupportedFieldType, item: DataVisualizerTableItem) => { + return ; + }, + width: dimensions.type, + sortable: true, + align: CENTER_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnType', + }, + { + field: 'fieldName', + name: i18n.translate('xpack.dataVisualizer.dataGrid.nameColumnName', { + defaultMessage: 'Name', + }), + sortable: true, + truncateText: true, + render: (fieldName: string, item: DataVisualizerTableItem) => { + const displayName = item.displayName ?? item.fieldName; - render: (value: number | undefined, item: DataVisualizerTableItem) => { - if (overallStatsRunning) { return ( - - + + {renderFieldName ? renderFieldName(fieldName, item) : displayName} ); - } - - return ( - - ); + }, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnName', }, - sortable: (item: DataVisualizerTableItem) => item?.stats?.count, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDocumentsCount', - width: dimensions.docCount, - }, - { - field: 'cardinality', - name: i18n.translate('xpack.dataVisualizer.dataGrid.distinctValuesColumnName', { - defaultMessage: 'Distinct values', - }), - render: (_: undefined, item: DataVisualizerTableItem) => { - if (overallStatsRunning) { + { + field: 'docCount', + name: ( +
+ {i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnName', { + defaultMessage: 'Documents (%)', + })} + +
+ ), + + render: (value: number | undefined, item: DataVisualizerTableItem) => { + if (overallStatsRunning) { + return ( + + + + ); + } + return ( - - - + ); - } + }, + sortable: (item: DataVisualizerTableItem) => item?.stats?.count, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDocumentsCount', + width: dimensions.docCount, + }, + { + field: 'cardinality', + name: i18n.translate('xpack.dataVisualizer.dataGrid.distinctValuesColumnName', { + defaultMessage: 'Distinct values', + }), + render: (_: undefined, item: DataVisualizerTableItem) => { + if (overallStatsRunning) { + return ( + + + + ); + } - return ; + return ; + }, + sortable: (item: DataVisualizerTableItem) => item?.stats?.cardinality, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDistinctValues', + width: dimensions.distinctValues, }, - sortable: (item: DataVisualizerTableItem) => item?.stats?.cardinality, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDistinctValues', - width: dimensions.distinctValues, - }, - { - name: ( -
- {dimensions.showIcon ? ( - - ) : null} - {i18n.translate('xpack.dataVisualizer.dataGrid.distributionsColumnName', { - defaultMessage: 'Distributions', - })} - { - - toggleShowDistribution()} - aria-label={ - showDistributions - ? i18n.translate('xpack.dataVisualizer.dataGrid.showDistributionsAriaLabel', { + { + name: ( +
+ {dimensions.showIcon ? ( + + ) : null} + {i18n.translate('xpack.dataVisualizer.dataGrid.distributionsColumnName', { + defaultMessage: 'Distributions', + })} + { + - - } -
- ), - render: (item: DataVisualizerTableItem) => { - if (item === undefined || showDistributions === false) return null; + > + toggleShowDistribution()} + aria-label={ + showDistributions + ? i18n.translate( + 'xpack.dataVisualizer.dataGrid.showDistributionsAriaLabel', + { + defaultMessage: 'Show distributions', + } + ) + : i18n.translate( + 'xpack.dataVisualizer.dataGrid.hideDistributionsAriaLabel', + { + defaultMessage: 'Hide distributions', + } + ) + } + /> +
+ } +
+ ), + render: (item: DataVisualizerTableItem) => { + if (item === undefined || showDistributions === false) return null; - if ('loading' in item && item.loading === true) { - return ( - - - - ); - } + if ('loading' in item && item.loading === true) { + return ( + + + + ); + } - if ( - (item.type === SUPPORTED_FIELD_TYPES.KEYWORD || - item.type === SUPPORTED_FIELD_TYPES.IP) && - item.stats?.topValues !== undefined - ) { - return ; - } + if ( + (item.type === SUPPORTED_FIELD_TYPES.KEYWORD || + item.type === SUPPORTED_FIELD_TYPES.IP) && + item.stats?.topValues !== undefined + ) { + return ; + } - if ( - item.type === SUPPORTED_FIELD_TYPES.NUMBER || - item.secondaryType === SUPPORTED_FIELD_TYPES.NUMBER - ) { - if (isIndexBasedFieldVisConfig(item) && item.stats?.distribution !== undefined) { - // If the cardinality is only low, show the top values instead of a distribution chart - return item.stats?.distribution?.percentiles.length <= 2 ? ( - - ) : ( - - ); - } else { - return ; + if ( + item.type === SUPPORTED_FIELD_TYPES.NUMBER || + item.secondaryType === SUPPORTED_FIELD_TYPES.NUMBER + ) { + if (isIndexBasedFieldVisConfig(item) && item.stats?.distribution !== undefined) { + // If the cardinality is only low, show the top values instead of a distribution chart + return item.stats?.distribution?.percentiles.length <= 2 ? ( + + ) : ( + + ); + } else { + return ; + } } - } - if (item.type === SUPPORTED_FIELD_TYPES.BOOLEAN) { - return ; - } + if (item.type === SUPPORTED_FIELD_TYPES.BOOLEAN) { + return ; + } - return null; + return null; + }, + width: dimensions.distributions, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDistribution', }, - width: dimensions.distributions, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDistribution', - }, - ]; - return extendedColumns ? [...baseColumns, ...extendedColumns] : baseColumns; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - expandAll, - showDistributions, - updatePageState, - extendedColumns, - dimensions.breakPoint, - toggleExpandAll, - overallStatsRunning, - ]); + ]; + return extendedColumns ? [...baseColumns, ...extendedColumns] : baseColumns; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + expandAll, + showDistributions, + updatePageState, + extendedColumns, + dimensions.breakPoint, + toggleExpandAll, + overallStatsRunning, + ]); - const itemIdToExpandedRowMap = useMemo(() => { - const itemIds = expandedRowItemIds; - return getItemIdToExpandedRowMap(itemIds, items); - }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); + const itemIdToExpandedRowMap = useMemo(() => { + const itemIds = expandedRowItemIds; + return getItemIdToExpandedRowMap(itemIds, items); + }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); - const $panelWidthS = `calc(max(20%, 225px))`; - const $panelWidthM = `calc(max(30%, 300px))`; + const $panelWidthS = `calc(max(20%, 225px))`; + const $panelWidthM = `calc(max(30%, 300px))`; - const dvTableCss = css({ - thead: { - position: 'sticky', - insetBlockStart: 0, - zIndex: 1, - backgroundColor: euiTheme.colors.emptyShade, - boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`, - }, - '.euiTableRow > .euiTableRowCell': { - borderTop: 0, - }, - [useEuiMinBreakpoint('s')]: { - '& .columnHeader__title': { - display: 'flex', - alignItems: 'center', + const dvTableCss = css({ + thead: { + position: 'sticky', + insetBlockStart: 0, + zIndex: 1, + backgroundColor: euiTheme.colors.emptyShade, + boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`, }, - '& .columnHeader__icon': { - paddingRight: euiTheme.size.xs, - }, - '& .euiTableRow > .euiTableRowCell': { + '.euiTableRow > .euiTableRowCell': { borderTop: 0, - borderBottom: euiTheme.border.thin, - }, - '& .euiTableCellContent': { - padding: euiTheme.size.xs, }, - '& .euiTableRow-isExpandedRow': { - '.euiTableRowCell': { - backgroundColor: `${euiTheme.colors.emptyShade} !important`, + [useEuiMinBreakpoint('s')]: { + '& .columnHeader__title': { + display: 'flex', + alignItems: 'center', + }, + '& .columnHeader__icon': { + paddingRight: euiTheme.size.xs, + }, + '& .euiTableRow > .euiTableRowCell': { borderTop: 0, borderBottom: euiTheme.border.thin, - '&:hover': { - backgroundColor: `${euiTheme.colors.emptyShade} !important`, - }, }, - }, - '& .dvSummaryTable': { - '.euiTableHeaderCell': { - display: 'none', + '& .euiTableCellContent': { + padding: euiTheme.size.xs, }, - }, - '& .dvSummaryTable__wrapper': { - minWidth: $panelWidthS, - maxWidth: $panelWidthS, - '&.dvPanel__dateSummary': { - minWidth: $panelWidthM, - maxWidth: $panelWidthM, + '& .euiTableRow-isExpandedRow': { + '.euiTableRowCell': { + backgroundColor: `${euiTheme.colors.emptyShade} !important`, + borderTop: 0, + borderBottom: euiTheme.border.thin, + '&:hover': { + backgroundColor: `${euiTheme.colors.emptyShade} !important`, + }, + }, }, - }, - '& .dvTopValues__wrapper': { - minWidth: 'fit-content', - }, - '& .dvPanel__wrapper': { - '&.dvPanel--compressed': { - width: $panelWidthS, + '& .dvSummaryTable': { + '.euiTableHeaderCell': { + display: 'none', + }, }, - '&.dvPanel--uniform': { + '& .dvSummaryTable__wrapper': { minWidth: $panelWidthS, maxWidth: $panelWidthS, + '&.dvPanel__dateSummary': { + minWidth: $panelWidthM, + maxWidth: $panelWidthM, + }, + }, + '& .dvTopValues__wrapper': { + minWidth: 'fit-content', + }, + '& .dvPanel__wrapper': { + '&.dvPanel--compressed': { + width: $panelWidthS, + }, + '&.dvPanel--uniform': { + minWidth: $panelWidthS, + maxWidth: $panelWidthS, + }, + }, + '& .dvPanel__wrapper:not(:last-child)': { + margin: `${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.m} 0`, + }, + '& .dvPanel__wrapper:last-child': { + margin: `${euiTheme.size.xs} 0 ${euiTheme.size.m} 0`, }, - }, - '& .dvPanel__wrapper:not(:last-child)': { - margin: `${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.m} 0`, - }, - '& .dvPanel__wrapper:last-child': { - margin: `${euiTheme.size.xs} 0 ${euiTheme.size.m} 0`, - }, - '& .dvMap__wrapper': { - height: '240px', - }, - '& .dvText__wrapper': { - minWidth: $panelWidthS, + '& .dvMap__wrapper': { + height: '240px', + }, + '& .dvText__wrapper': { + minWidth: $panelWidthS, + }, }, - }, - }); + }); - const message = useMemo(() => { - if (!overallStatsRunning && error) { - return i18n.translate('xpack.dataVisualizer.dataGrid.errorMessage', { - defaultMessage: 'An error occured fetching field statistics', - }); - } + const message = useMemo(() => { + if (!overallStatsRunning && error) { + return i18n.translate('xpack.dataVisualizer.dataGrid.errorMessage', { + defaultMessage: 'An error occured fetching field statistics', + }); + } - if (loading) { - return i18n.translate('xpack.dataVisualizer.dataGrid.searchingMessage', { - defaultMessage: 'Searching', - }); - } - return undefined; - }, [error, loading, overallStatsRunning]); - return ( - - {(resizeRef) => ( -
- - message={message} - css={dvTableCss} - items={items} - itemId={FIELD_NAME} - columns={columns} - pagination={pagination} - sorting={sorting} - itemIdToExpandedRowMap={itemIdToExpandedRowMap} - onTableChange={onTableChange} - data-test-subj={`dataVisualizerTable-${loading ? 'loading' : 'loaded'}`} - rowProps={(item) => ({ - 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, - })} - /> -
- )} -
- ); -}; + if (loading) { + return i18n.translate('xpack.dataVisualizer.dataGrid.searchingMessage', { + defaultMessage: 'Searching', + }); + } + return undefined; + }, [error, loading, overallStatsRunning]); + return ( + + {(resizeRef) => ( +
+ + message={message} + css={dvTableCss} + items={items} + itemId={FIELD_NAME} + columns={columns} + pagination={pagination} + sorting={sorting} + itemIdToExpandedRowMap={itemIdToExpandedRowMap} + onTableChange={onTableChange} + data-test-subj={`dataVisualizerTable-${loading ? 'loading' : 'loaded'}`} + rowProps={(item) => ({ + 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, + })} + /> +
+ )} +
+ ); + } +); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index f2ac83e1d4bfb..254d2b16461fe 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -24,9 +24,13 @@ import { EuiPanel, EuiProgress, EuiSpacer, + EuiCallOut, + EuiIconTip, + EuiIcon, } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; +import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getOrCreateDataViewByIndexPattern } from '../../search_strategy/requests/get_data_view_by_index_pattern'; import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme'; import type { FieldVisConfig } from '../../../common/components/stats_table/types'; @@ -64,6 +68,8 @@ export const IndexDataVisualizerESQL: FC = (dataVi const [query, setQuery] = useState(DEFAULT_ESQL_QUERY); const [currentDataView, setCurrentDataView] = useState(); + const unsupportedReasonForQuery = getReasonIfQueryUnsupportedByFieldStats(localQuery); + const toggleShowEmptyFields = () => { setDataVisualizerListState({ ...dataVisualizerListState, @@ -202,8 +208,11 @@ export const IndexDataVisualizerESQL: FC = (dataVi const onTextLangQuerySubmit = useCallback( async (q: AggregateQuery | undefined) => { if (isESQLQuery(q)) { - resetData(); - setQuery(q); + const isUnsupported = getReasonIfQueryUnsupportedByFieldStats(q) !== undefined; + if (!isUnsupported) { + resetData(); + setQuery(q); + } } }, [resetData] @@ -224,8 +233,16 @@ export const IndexDataVisualizerESQL: FC = (dataVi data-test-subj="dataViewTitleHeader" direction="row" alignItems="center" - css={{ padding: `${euiTheme.euiSizeS} 0`, marginRight: `${euiTheme.euiSize}` }} - /> + css={{ padding: 0, marginRight: 0 }} + > + {unsupportedReasonForQuery ? ( + + + {unsupportedReasonForQuery} + + + ) : null} + {isWithinLargeBreakpoint ? : null} = (dataVi width="full" needsUpdate={queryNeedsUpdate} onRefresh={handleRefresh} - isDisabled={!hasValidTimeField} + isDisabled={unsupportedReasonForQuery !== undefined} + tooltipMessage={unsupportedReasonForQuery} /> @@ -276,6 +294,7 @@ export const IndexDataVisualizerESQL: FC = (dataVi hideRunQueryText={false} isLoading={queryHistoryStatus ?? false} displayDocumentationAsFlyout + disableSubmitAction={unsupportedReasonForQuery !== undefined} /> @@ -312,7 +331,7 @@ export const IndexDataVisualizerESQL: FC = (dataVi - + Date: Wed, 23 Oct 2024 17:06:05 -0500 Subject: [PATCH 02/29] Add messaging for embeddables --- .../src/components/field_stats/field_stats.tsx | 2 -- .../field_stats/field_stats_esql_editor.tsx | 6 ++++-- .../field_stats/field_stats_initializer.tsx | 16 +++++++++++++++- .../grid_embeddable/field_stats_wrapper.tsx | 10 ++++++++-- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index be709c0eff70b..ee571511eeec3 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -166,8 +166,6 @@ const FieldStatsComponent: React.FC = ({ [changeDataView, isCanceledRef] ); - const warningMsgForQuery = useMemo(() => getReasonIfQueryUnsupportedByFieldStats(query), [query]); - async function fetchData() { if (isCanceledRef.current) { return; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx index bde937a778a55..3ebe1fd56f805 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_esql_editor.tsx @@ -14,12 +14,14 @@ interface FieldStatsESQLEditorProps { query: AggregateQuery; setQuery: (query: AggregateQuery) => void; onQuerySubmit: (query: AggregateQuery, abortController?: AbortController) => Promise; + disableSubmitAction?: boolean; } export const FieldStatsESQLEditor = ({ canEditTextBasedQuery = true, query, setQuery, onQuerySubmit, + disableSubmitAction = false, }: FieldStatsESQLEditorProps) => { const prevQuery = useRef(query); const [isVisualizationLoading, setIsVisualizationLoading] = useState(false); @@ -48,8 +50,8 @@ export const FieldStatsESQLEditor = ({ editorIsInline hideRunQueryText onTextLangQuerySubmit={onTextLangQuerySubmit} - isDisabled={false} - allowQueryCancellation + allowQueryCancellation={false} + disableSubmitAction={disableSubmitAction} isLoading={isVisualizationLoading} /> diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index 0e678e328894d..1456b74229b26 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -29,6 +29,7 @@ import { ENABLE_ESQL, getESQLAdHocDataview } from '@kbn/esql-utils'; import type { AggregateQuery } from '@kbn/es-query'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; +import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { useDataVisualizerKibana } from '../../../kibana_context'; import { FieldStatsESQLEditor } from './field_stats_esql_editor'; import type { @@ -94,6 +95,12 @@ export const FieldStatisticsInitializer: FC = ({ }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [dataViewId, viewType, esqlQuery.esql, isEsqlMode]); + + const unsupportedReason = useMemo( + () => getReasonIfQueryUnsupportedByFieldStats(esqlQuery), + [esqlQuery] + ); + const onESQLQuerySubmit = useCallback( async (query: AggregateQuery, abortController?: AbortController) => { const adhocDataView = await getESQLAdHocDataview(query.esql, dataViews); @@ -202,6 +209,7 @@ export const FieldStatisticsInitializer: FC = ({ } /> ) : null} + {initialInput?.viewType === FieldStatsInitializerViewType.ESQL && !isEsqlEnabled ? ( <> @@ -247,8 +255,14 @@ export const FieldStatisticsInitializer: FC = ({ query={esqlQuery} setQuery={setQuery} onQuerySubmit={onESQLQuerySubmit} + disableSubmitAction={!!unsupportedReason} /> ) : null} + {unsupportedReason ? ( + + {unsupportedReason} + + ) : null} @@ -282,7 +296,7 @@ export const FieldStatisticsInitializer: FC = ({ defaultMessage: 'Apply changes', } )} - disabled={!isEsqlFormValid || !isDataViewFormValid} + disabled={!isEsqlFormValid || !isDataViewFormValid || !!unsupportedReason} iconType="check" data-test-subj="applyFlyoutButton" > diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index 03dbb9c7af8c8..4efc879cf2cbe 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useMemo } from 'react'; -import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiCallOut, EuiEmptyPrompt } from '@elastic/eui'; import type { Required } from 'utility-types'; import { FormattedMessage } from '@kbn/i18n-react'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; @@ -16,6 +16,7 @@ import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import type { DatePickerDependencies } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { pick } from 'lodash'; +import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getCoreStart, getPluginsStart } from '../../../../kibana_services'; import type { FieldStatisticTableEmbeddableProps, @@ -41,7 +42,12 @@ function isFieldStatisticTableEmbeddableState( const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps) => { if (isESQLFieldStatisticTableEmbeddableState(props)) { - return ( + const unsupportedReason = getReasonIfQueryUnsupportedByFieldStats(props.esqlQuery); + return unsupportedReason ? ( + + {unsupportedReason} + + ) : ( Date: Thu, 24 Oct 2024 10:29:31 -0500 Subject: [PATCH 03/29] Update messaging and add callout --- .../components/field_stats/field_stats.tsx | 27 +- .../src/utils/get_warning_message.ts | 17 +- .../view_mode_toggle/view_mode_toggle.tsx | 4 +- .../data_visualizer_stats_table.tsx | 805 +++++++++--------- .../index_data_visualizer_esql.tsx | 9 +- .../field_stats/field_stats_initializer.tsx | 4 +- .../grid_embeddable/field_stats_wrapper.tsx | 4 +- 7 files changed, 441 insertions(+), 429 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index ee571511eeec3..84ed543e7100f 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -19,7 +19,14 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import DateMath from '@kbn/datemath'; -import { EuiButtonGroup, EuiLoadingSpinner, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { + EuiButtonGroup, + EuiCallOut, + EuiLoadingSpinner, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { Axis, @@ -53,7 +60,10 @@ import { import { FieldSummaryMessage } from './field_summary_message'; import { FieldNumberSummary, isNumberSummaryValid } from './field_number_summary'; import { ErrorBoundary } from '../error_boundary'; -import { getReasonIfQueryUnsupportedByFieldStats } from '../../utils/get_warning_message'; +import { + FIELD_DATA_LABEL, + getReasonIfFieldStatsUnavailableForQuery, +} from '../../utils/get_warning_message'; export interface FieldStatsState { isLoading: boolean; @@ -337,9 +347,16 @@ const FieldStatsComponent: React.FC = ({ : messageNoAnalysis; } - const unsupportedReason = getReasonIfQueryUnsupportedByFieldStats(query); - if (unsupportedReason) { - const messageUnsupportedReason = ; + const unsupportedReasonForQuery = getReasonIfFieldStatsUnavailableForQuery( + query, + FIELD_DATA_LABEL + ); + if (unsupportedReasonForQuery) { + const messageUnsupportedReason = ( + + {unsupportedReasonForQuery} + + ); return overrideMissingContent ? overrideMissingContent({ diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index d3b701c2f800c..9fbc34cb62ec7 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -12,8 +12,17 @@ import { parse } from '@kbn/esql-ast'; import { isOfAggregateQueryType } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -export const getReasonIfQueryUnsupportedByFieldStats = ( - query?: AggregateQuery | Query | { [key: string]: any } +const FIELD_STATISTICS_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldStatisticsLabel', { + defaultMessage: `Field statistics`, +}); + +export const FIELD_DATA_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldDataLabel', { + defaultMessage: `Field data`, +}); + +export const getReasonIfFieldStatsUnavailableForQuery = ( + query?: AggregateQuery | Query | { [key: string]: any }, + label: string = FIELD_STATISTICS_LABEL ): string | undefined => { if (isOfAggregateQueryType(query)) { const { root } = parse(query.esql); @@ -22,8 +31,8 @@ export const getReasonIfQueryUnsupportedByFieldStats = ( return i18n.translate( 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { - defaultMessage: - 'Field statistics is unavailable for ES|QL queries containing `MATCH` function', + defaultMessage: `{label} is unavailable for ES|QL queries containing 'MATCH' or 'QSTR' functions.`, + values: { label }, } ); } 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 06a5d499200a5..fc32a0dd76777 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 @@ -14,7 +14,7 @@ import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; import useMountedState from 'react-use/lib/useMountedState'; -import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; +import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../application/main/state_management/discover_state'; @@ -49,7 +49,7 @@ export const DocumentViewModeToggle = ({ ); const state = stateContainer.appState.getState(); const fieldStatsWarningMsgForQuery = useMemo(() => { - return getReasonIfQueryUnsupportedByFieldStats(state.query); + return getReasonIfFieldStatsUnavailableForQuery(state.query); }, [state.query]); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx index a0ad141a57e97..3b591a85ff472 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/data_visualizer_stats_table.tsx @@ -65,459 +65,448 @@ interface DataVisualizerTableProps { error?: Error | string; } -export const DataVisualizerTable = React.memo( - ({ +const UnmemoizedDataVisualizerTable = ({ + items, + pageState, + updatePageState, + getItemIdToExpandedRowMap, + extendedColumns, + showPreviewByDefault, + onChange, + loading, + totalCount, + overallStatsRunning, + renderFieldName, + error, +}: DataVisualizerTableProps) => { + const { euiTheme } = useEuiTheme(); + + const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); + const [expandAll, setExpandAll] = useState(false); + + const { onTableChange, pagination, sorting } = useTableSettings( items, pageState, - updatePageState, - getItemIdToExpandedRowMap, - extendedColumns, - showPreviewByDefault, - onChange, - loading, - totalCount, - overallStatsRunning, - renderFieldName, - error, - }: DataVisualizerTableProps) => { - const { euiTheme } = useEuiTheme(); - - const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); - const [expandAll, setExpandAll] = useState(false); - - const { onTableChange, pagination, sorting } = useTableSettings( - items, - pageState, - updatePageState - ); - const [showDistributions, setShowDistributions] = useState( - showPreviewByDefault ?? true - ); - const [dimensions, setDimensions] = useState(calculateTableColumnsDimensions()); - - const toggleExpandAll = useCallback( - (shouldExpandAll: boolean) => { - setExpandedRowItemIds( - shouldExpandAll - ? // Update list of ids in expandedRowIds to include all - (items.map((item) => item.fieldName).filter((id) => id !== undefined) as string[]) - : // Otherwise, reset list of ids in expandedRowIds - [] - ); - setExpandAll(shouldExpandAll); - }, - [items] - ); - const isMounted = useMountedState(); + updatePageState + ); + const [showDistributions, setShowDistributions] = useState(showPreviewByDefault ?? true); + const [dimensions, setDimensions] = useState(calculateTableColumnsDimensions()); - // eslint-disable-next-line react-hooks/exhaustive-deps - const resizeHandler = useCallback( - throttle((e: { width: number; height: number }) => { - // When window or table is resized, - // update the column widths and other settings accordingly - if (isMounted()) { - setDimensions(calculateTableColumnsDimensions(e.width)); - } - }, 500), - [] - ); - - const toggleShowDistribution = useCallback(() => { - setShowDistributions(!showDistributions); - if (onChange) { - onChange({ showDistributions: !showDistributions }); - } - }, [onChange, showDistributions]); - - function toggleDetails(item: DataVisualizerTableItem) { - if (item.fieldName === undefined) return; - const index = expandedRowItemIds.indexOf(item.fieldName); - if (index !== -1) { - expandedRowItemIds.splice(index, 1); - } else { - expandedRowItemIds.push(item.fieldName); + const toggleExpandAll = useCallback( + (shouldExpandAll: boolean) => { + setExpandedRowItemIds( + shouldExpandAll + ? // Update list of ids in expandedRowIds to include all + (items.map((item) => item.fieldName).filter((id) => id !== undefined) as string[]) + : // Otherwise, reset list of ids in expandedRowIds + [] + ); + setExpandAll(shouldExpandAll); + }, + [items] + ); + const isMounted = useMountedState(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const resizeHandler = useCallback( + throttle((e: { width: number; height: number }) => { + // When window or table is resized, + // update the column widths and other settings accordingly + if (isMounted()) { + setDimensions(calculateTableColumnsDimensions(e.width)); } + }, 500), + [] + ); - // spread to a new array otherwise the component wouldn't re-render - setExpandedRowItemIds([...expandedRowItemIds]); + const toggleShowDistribution = useCallback(() => { + setShowDistributions(!showDistributions); + if (onChange) { + onChange({ showDistributions: !showDistributions }); } + }, [onChange, showDistributions]); - const columns = useMemo(() => { - const expanderColumn: EuiTableComputedColumnType = { - name: - // EUI will automatically show an expander button when table is mobile view (where width <700) - // so we need to not render any addition button - dimensions.breakPoint !== 'small' ? ( - toggleExpandAll(!expandAll)} - aria-label={ - !expandAll - ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { - defaultMessage: 'Expand details for all fields', - }) - : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { - defaultMessage: 'Collapse details for all fields', - }) - } - iconType={expandAll ? 'arrowDown' : 'arrowRight'} - /> - ) : null, - align: RIGHT_ALIGNMENT, - width: dimensions.expander, - isExpander: true, - render: (item: DataVisualizerTableItem) => { + function toggleDetails(item: DataVisualizerTableItem) { + if (item.fieldName === undefined) return; + const index = expandedRowItemIds.indexOf(item.fieldName); + if (index !== -1) { + expandedRowItemIds.splice(index, 1); + } else { + expandedRowItemIds.push(item.fieldName); + } + + // spread to a new array otherwise the component wouldn't re-render + setExpandedRowItemIds([...expandedRowItemIds]); + } + + const columns = useMemo(() => { + const expanderColumn: EuiTableComputedColumnType = { + name: + // EUI will automatically show an expander button when table is mobile view (where width <700) + // so we need to not render any addition button + dimensions.breakPoint !== 'small' ? ( + toggleExpandAll(!expandAll)} + aria-label={ + !expandAll + ? i18n.translate('xpack.dataVisualizer.dataGrid.expandDetailsForAllAriaLabel', { + defaultMessage: 'Expand details for all fields', + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.collapseDetailsForAllAriaLabel', { + defaultMessage: 'Collapse details for all fields', + }) + } + iconType={expandAll ? 'arrowDown' : 'arrowRight'} + /> + ) : null, + align: RIGHT_ALIGNMENT, + width: dimensions.expander, + isExpander: true, + render: (item: DataVisualizerTableItem) => { + const displayName = item.displayName ?? item.fieldName; + if (item.fieldName === undefined) return null; + const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowDown' : 'arrowRight'; + return ( + toggleDetails(item)} + aria-label={ + expandedRowItemIds.includes(item.fieldName) + ? i18n.translate('xpack.dataVisualizer.dataGrid.rowCollapse', { + defaultMessage: 'Hide details for {fieldName}', + values: { fieldName: displayName }, + }) + : i18n.translate('xpack.dataVisualizer.dataGrid.rowExpand', { + defaultMessage: 'Show details for {fieldName}', + values: { fieldName: displayName }, + }) + } + iconType={direction} + /> + ); + }, + 'data-test-subj': 'dataVisualizerTableColumnDetailsToggle', + }; + + const baseColumns = [ + expanderColumn, + { + field: 'type', + name: i18n.translate('xpack.dataVisualizer.dataGrid.typeColumnName', { + defaultMessage: 'Type', + }), + render: (fieldType: SupportedFieldType, item: DataVisualizerTableItem) => { + return ; + }, + width: dimensions.type, + sortable: true, + align: CENTER_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnType', + }, + { + field: 'fieldName', + name: i18n.translate('xpack.dataVisualizer.dataGrid.nameColumnName', { + defaultMessage: 'Name', + }), + sortable: true, + truncateText: true, + render: (fieldName: string, item: DataVisualizerTableItem) => { const displayName = item.displayName ?? item.fieldName; - if (item.fieldName === undefined) return null; - const direction = expandedRowItemIds.includes(item.fieldName) - ? 'arrowDown' - : 'arrowRight'; + return ( - toggleDetails(item)} - aria-label={ - expandedRowItemIds.includes(item.fieldName) - ? i18n.translate('xpack.dataVisualizer.dataGrid.rowCollapse', { - defaultMessage: 'Hide details for {fieldName}', - values: { fieldName: displayName }, - }) - : i18n.translate('xpack.dataVisualizer.dataGrid.rowExpand', { - defaultMessage: 'Show details for {fieldName}', - values: { fieldName: displayName }, - }) - } - iconType={direction} - /> + + {renderFieldName ? renderFieldName(fieldName, item) : displayName} + ); }, - 'data-test-subj': 'dataVisualizerTableColumnDetailsToggle', - }; - - const baseColumns = [ - expanderColumn, - { - field: 'type', - name: i18n.translate('xpack.dataVisualizer.dataGrid.typeColumnName', { - defaultMessage: 'Type', - }), - render: (fieldType: SupportedFieldType, item: DataVisualizerTableItem) => { - return ; - }, - width: dimensions.type, - sortable: true, - align: CENTER_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnType', - }, - { - field: 'fieldName', - name: i18n.translate('xpack.dataVisualizer.dataGrid.nameColumnName', { - defaultMessage: 'Name', - }), - sortable: true, - truncateText: true, - render: (fieldName: string, item: DataVisualizerTableItem) => { - const displayName = item.displayName ?? item.fieldName; + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnName', + }, + { + field: 'docCount', + name: ( +
+ {i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnName', { + defaultMessage: 'Documents (%)', + })} + +
+ ), + render: (value: number | undefined, item: DataVisualizerTableItem) => { + if (overallStatsRunning) { return ( - - {renderFieldName ? renderFieldName(fieldName, item) : displayName} + + ); - }, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnName', - }, - { - field: 'docCount', - name: ( -
- {i18n.translate('xpack.dataVisualizer.dataGrid.documentsCountColumnName', { - defaultMessage: 'Documents (%)', - })} - -
- ), - - render: (value: number | undefined, item: DataVisualizerTableItem) => { - if (overallStatsRunning) { - return ( - - - - ); - } + } + return ( + + ); + }, + sortable: (item: DataVisualizerTableItem) => item?.stats?.count, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDocumentsCount', + width: dimensions.docCount, + }, + { + field: 'cardinality', + name: i18n.translate('xpack.dataVisualizer.dataGrid.distinctValuesColumnName', { + defaultMessage: 'Distinct values', + }), + render: (_: undefined, item: DataVisualizerTableItem) => { + if (overallStatsRunning) { return ( - + + + ); - }, - sortable: (item: DataVisualizerTableItem) => item?.stats?.count, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDocumentsCount', - width: dimensions.docCount, - }, - { - field: 'cardinality', - name: i18n.translate('xpack.dataVisualizer.dataGrid.distinctValuesColumnName', { - defaultMessage: 'Distinct values', - }), - render: (_: undefined, item: DataVisualizerTableItem) => { - if (overallStatsRunning) { - return ( - - - - ); - } + } - return ; - }, - sortable: (item: DataVisualizerTableItem) => item?.stats?.cardinality, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDistinctValues', - width: dimensions.distinctValues, + return ; }, - { - name: ( -
- {dimensions.showIcon ? ( - - ) : null} - {i18n.translate('xpack.dataVisualizer.dataGrid.distributionsColumnName', { - defaultMessage: 'Distributions', - })} - { - item?.stats?.cardinality, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDistinctValues', + width: dimensions.distinctValues, + }, + { + name: ( +
+ {dimensions.showIcon ? ( + + ) : null} + {i18n.translate('xpack.dataVisualizer.dataGrid.distributionsColumnName', { + defaultMessage: 'Distributions', + })} + { + + toggleShowDistribution()} + aria-label={ + showDistributions + ? i18n.translate('xpack.dataVisualizer.dataGrid.showDistributionsAriaLabel', { defaultMessage: 'Show distributions', }) - : i18n.translate('xpack.dataVisualizer.dataGrid.hideDistributionsTooltip', { + : i18n.translate('xpack.dataVisualizer.dataGrid.hideDistributionsAriaLabel', { defaultMessage: 'Hide distributions', }) } - > - toggleShowDistribution()} - aria-label={ - showDistributions - ? i18n.translate( - 'xpack.dataVisualizer.dataGrid.showDistributionsAriaLabel', - { - defaultMessage: 'Show distributions', - } - ) - : i18n.translate( - 'xpack.dataVisualizer.dataGrid.hideDistributionsAriaLabel', - { - defaultMessage: 'Hide distributions', - } - ) - } - /> - - } -
- ), - render: (item: DataVisualizerTableItem) => { - if (item === undefined || showDistributions === false) return null; - - if ('loading' in item && item.loading === true) { - return ( - - - - ); + /> +
} +
+ ), + render: (item: DataVisualizerTableItem) => { + if (item === undefined || showDistributions === false) return null; - if ( - (item.type === SUPPORTED_FIELD_TYPES.KEYWORD || - item.type === SUPPORTED_FIELD_TYPES.IP) && - item.stats?.topValues !== undefined - ) { - return ; - } + if ('loading' in item && item.loading === true) { + return ( + + + + ); + } - if ( - item.type === SUPPORTED_FIELD_TYPES.NUMBER || - item.secondaryType === SUPPORTED_FIELD_TYPES.NUMBER - ) { - if (isIndexBasedFieldVisConfig(item) && item.stats?.distribution !== undefined) { - // If the cardinality is only low, show the top values instead of a distribution chart - return item.stats?.distribution?.percentiles.length <= 2 ? ( - - ) : ( - - ); - } else { - return ; - } - } + if ( + (item.type === SUPPORTED_FIELD_TYPES.KEYWORD || + item.type === SUPPORTED_FIELD_TYPES.IP) && + item.stats?.topValues !== undefined + ) { + return ; + } - if (item.type === SUPPORTED_FIELD_TYPES.BOOLEAN) { - return ; + if ( + item.type === SUPPORTED_FIELD_TYPES.NUMBER || + item.secondaryType === SUPPORTED_FIELD_TYPES.NUMBER + ) { + if (isIndexBasedFieldVisConfig(item) && item.stats?.distribution !== undefined) { + // If the cardinality is only low, show the top values instead of a distribution chart + return item.stats?.distribution?.percentiles.length <= 2 ? ( + + ) : ( + + ); + } else { + return ; } + } - return null; - }, - width: dimensions.distributions, - align: LEFT_ALIGNMENT as HorizontalAlignment, - 'data-test-subj': 'dataVisualizerTableColumnDistribution', + if (item.type === SUPPORTED_FIELD_TYPES.BOOLEAN) { + return ; + } + + return null; }, - ]; - return extendedColumns ? [...baseColumns, ...extendedColumns] : baseColumns; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - expandAll, - showDistributions, - updatePageState, - extendedColumns, - dimensions.breakPoint, - toggleExpandAll, - overallStatsRunning, - ]); - - const itemIdToExpandedRowMap = useMemo(() => { - const itemIds = expandedRowItemIds; - return getItemIdToExpandedRowMap(itemIds, items); - }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); - - const $panelWidthS = `calc(max(20%, 225px))`; - const $panelWidthM = `calc(max(30%, 300px))`; - - const dvTableCss = css({ - thead: { - position: 'sticky', - insetBlockStart: 0, - zIndex: 1, - backgroundColor: euiTheme.colors.emptyShade, - boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`, + width: dimensions.distributions, + align: LEFT_ALIGNMENT as HorizontalAlignment, + 'data-test-subj': 'dataVisualizerTableColumnDistribution', }, - '.euiTableRow > .euiTableRowCell': { + ]; + return extendedColumns ? [...baseColumns, ...extendedColumns] : baseColumns; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + expandAll, + showDistributions, + updatePageState, + extendedColumns, + dimensions.breakPoint, + toggleExpandAll, + overallStatsRunning, + ]); + + const itemIdToExpandedRowMap = useMemo(() => { + const itemIds = expandedRowItemIds; + return getItemIdToExpandedRowMap(itemIds, items); + }, [items, expandedRowItemIds, getItemIdToExpandedRowMap]); + + const $panelWidthS = `calc(max(20%, 225px))`; + const $panelWidthM = `calc(max(30%, 300px))`; + + const dvTableCss = css({ + thead: { + position: 'sticky', + insetBlockStart: 0, + zIndex: 1, + backgroundColor: euiTheme.colors.emptyShade, + boxShadow: `inset 0 0px 0, inset 0 -1px 0 ${euiTheme.border.color}`, + }, + '.euiTableRow > .euiTableRowCell': { + borderTop: 0, + }, + [useEuiMinBreakpoint('s')]: { + '& .columnHeader__title': { + display: 'flex', + alignItems: 'center', + }, + '& .columnHeader__icon': { + paddingRight: euiTheme.size.xs, + }, + '& .euiTableRow > .euiTableRowCell': { borderTop: 0, + borderBottom: euiTheme.border.thin, }, - [useEuiMinBreakpoint('s')]: { - '& .columnHeader__title': { - display: 'flex', - alignItems: 'center', - }, - '& .columnHeader__icon': { - paddingRight: euiTheme.size.xs, - }, - '& .euiTableRow > .euiTableRowCell': { + '& .euiTableCellContent': { + padding: euiTheme.size.xs, + }, + '& .euiTableRow-isExpandedRow': { + '.euiTableRowCell': { + backgroundColor: `${euiTheme.colors.emptyShade} !important`, borderTop: 0, borderBottom: euiTheme.border.thin, - }, - '& .euiTableCellContent': { - padding: euiTheme.size.xs, - }, - '& .euiTableRow-isExpandedRow': { - '.euiTableRowCell': { + '&:hover': { backgroundColor: `${euiTheme.colors.emptyShade} !important`, - borderTop: 0, - borderBottom: euiTheme.border.thin, - '&:hover': { - backgroundColor: `${euiTheme.colors.emptyShade} !important`, - }, - }, - }, - '& .dvSummaryTable': { - '.euiTableHeaderCell': { - display: 'none', }, }, - '& .dvSummaryTable__wrapper': { - minWidth: $panelWidthS, - maxWidth: $panelWidthS, - '&.dvPanel__dateSummary': { - minWidth: $panelWidthM, - maxWidth: $panelWidthM, - }, - }, - '& .dvTopValues__wrapper': { - minWidth: 'fit-content', - }, - '& .dvPanel__wrapper': { - '&.dvPanel--compressed': { - width: $panelWidthS, - }, - '&.dvPanel--uniform': { - minWidth: $panelWidthS, - maxWidth: $panelWidthS, - }, - }, - '& .dvPanel__wrapper:not(:last-child)': { - margin: `${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.m} 0`, + }, + '& .dvSummaryTable': { + '.euiTableHeaderCell': { + display: 'none', }, - '& .dvPanel__wrapper:last-child': { - margin: `${euiTheme.size.xs} 0 ${euiTheme.size.m} 0`, + }, + '& .dvSummaryTable__wrapper': { + minWidth: $panelWidthS, + maxWidth: $panelWidthS, + '&.dvPanel__dateSummary': { + minWidth: $panelWidthM, + maxWidth: $panelWidthM, }, - - '& .dvMap__wrapper': { - height: '240px', + }, + '& .dvTopValues__wrapper': { + minWidth: 'fit-content', + }, + '& .dvPanel__wrapper': { + '&.dvPanel--compressed': { + width: $panelWidthS, }, - '& .dvText__wrapper': { + '&.dvPanel--uniform': { minWidth: $panelWidthS, + maxWidth: $panelWidthS, }, }, - }); + '& .dvPanel__wrapper:not(:last-child)': { + margin: `${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.m} 0`, + }, + '& .dvPanel__wrapper:last-child': { + margin: `${euiTheme.size.xs} 0 ${euiTheme.size.m} 0`, + }, - const message = useMemo(() => { - if (!overallStatsRunning && error) { - return i18n.translate('xpack.dataVisualizer.dataGrid.errorMessage', { - defaultMessage: 'An error occured fetching field statistics', - }); - } + '& .dvMap__wrapper': { + height: '240px', + }, + '& .dvText__wrapper': { + minWidth: $panelWidthS, + }, + }, + }); - if (loading) { - return i18n.translate('xpack.dataVisualizer.dataGrid.searchingMessage', { - defaultMessage: 'Searching', - }); - } - return undefined; - }, [error, loading, overallStatsRunning]); - return ( - - {(resizeRef) => ( -
- - message={message} - css={dvTableCss} - items={items} - itemId={FIELD_NAME} - columns={columns} - pagination={pagination} - sorting={sorting} - itemIdToExpandedRowMap={itemIdToExpandedRowMap} - onTableChange={onTableChange} - data-test-subj={`dataVisualizerTable-${loading ? 'loading' : 'loaded'}`} - rowProps={(item) => ({ - 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, - })} - /> -
- )} -
- ); - } -); + const message = useMemo(() => { + if (!overallStatsRunning && error) { + return i18n.translate('xpack.dataVisualizer.dataGrid.errorMessage', { + defaultMessage: 'An error occured fetching field statistics', + }); + } + + if (loading) { + return i18n.translate('xpack.dataVisualizer.dataGrid.searchingMessage', { + defaultMessage: 'Searching', + }); + } + return undefined; + }, [error, loading, overallStatsRunning]); + return ( + + {(resizeRef) => ( +
+ + message={message} + css={dvTableCss} + items={items} + itemId={FIELD_NAME} + columns={columns} + pagination={pagination} + sorting={sorting} + itemIdToExpandedRowMap={itemIdToExpandedRowMap} + onTableChange={onTableChange} + data-test-subj={`dataVisualizerTable-${loading ? 'loading' : 'loaded'}`} + rowProps={(item) => ({ + 'data-test-subj': `dataVisualizerRow row-${item.fieldName}`, + })} + /> +
+ )} +
+ ); +}; + +export const DataVisualizerTable = React.memo( + UnmemoizedDataVisualizerTable +) as typeof UnmemoizedDataVisualizerTable; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index 254d2b16461fe..19afd14686b7a 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -25,15 +25,12 @@ import { EuiProgress, EuiSpacer, EuiCallOut, - EuiIconTip, - EuiIcon, } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; -import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; +import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getOrCreateDataViewByIndexPattern } from '../../search_strategy/requests/get_data_view_by_index_pattern'; import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme'; -import type { FieldVisConfig } from '../../../common/components/stats_table/types'; import { DATA_VISUALIZER_INDEX_VIEWER } from '../../constants/index_data_visualizer_viewer'; import { useDataVisualizerKibana } from '../../../kibana_context'; import type { GetAdditionalLinks } from '../../../common/components/results_links'; @@ -68,7 +65,7 @@ export const IndexDataVisualizerESQL: FC = (dataVi const [query, setQuery] = useState(DEFAULT_ESQL_QUERY); const [currentDataView, setCurrentDataView] = useState(); - const unsupportedReasonForQuery = getReasonIfQueryUnsupportedByFieldStats(localQuery); + const unsupportedReasonForQuery = getReasonIfFieldStatsUnavailableForQuery(localQuery); const toggleShowEmptyFields = () => { setDataVisualizerListState({ @@ -208,7 +205,7 @@ export const IndexDataVisualizerESQL: FC = (dataVi const onTextLangQuerySubmit = useCallback( async (q: AggregateQuery | undefined) => { if (isESQLQuery(q)) { - const isUnsupported = getReasonIfQueryUnsupportedByFieldStats(q) !== undefined; + const isUnsupported = getReasonIfFieldStatsUnavailableForQuery(q) !== undefined; if (!isUnsupported) { resetData(); setQuery(q); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index 1456b74229b26..f6e03177ef8b7 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -29,7 +29,7 @@ import { ENABLE_ESQL, getESQLAdHocDataview } from '@kbn/esql-utils'; import type { AggregateQuery } from '@kbn/es-query'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; +import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { useDataVisualizerKibana } from '../../../kibana_context'; import { FieldStatsESQLEditor } from './field_stats_esql_editor'; import type { @@ -97,7 +97,7 @@ export const FieldStatisticsInitializer: FC = ({ }, [dataViewId, viewType, esqlQuery.esql, isEsqlMode]); const unsupportedReason = useMemo( - () => getReasonIfQueryUnsupportedByFieldStats(esqlQuery), + () => getReasonIfFieldStatsUnavailableForQuery(esqlQuery), [esqlQuery] ); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index 4efc879cf2cbe..ebddfb16db7d3 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -16,7 +16,7 @@ import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import type { DatePickerDependencies } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { pick } from 'lodash'; -import { getReasonIfQueryUnsupportedByFieldStats } from '@kbn/unified-field-list/src/utils/get_warning_message'; +import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getCoreStart, getPluginsStart } from '../../../../kibana_services'; import type { FieldStatisticTableEmbeddableProps, @@ -42,7 +42,7 @@ function isFieldStatisticTableEmbeddableState( const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps) => { if (isESQLFieldStatisticTableEmbeddableState(props)) { - const unsupportedReason = getReasonIfQueryUnsupportedByFieldStats(props.esqlQuery); + const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(props.esqlQuery); return unsupportedReason ? ( {unsupportedReason} From 2957796f56be6f7ec0cbe25864cd8cb142e9b5bc Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 24 Oct 2024 10:35:39 -0500 Subject: [PATCH 04/29] Update callout style with icon --- .../src/components/field_stats/field_stats.tsx | 4 +--- .../kbn-unified-field-list/src/utils/get_warning_message.ts | 2 +- .../embeddables/field_stats/field_stats_initializer.tsx | 4 +--- .../embeddables/grid_embeddable/field_stats_wrapper.tsx | 4 +--- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 84ed543e7100f..08f417e37236d 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -353,9 +353,7 @@ const FieldStatsComponent: React.FC = ({ ); if (unsupportedReasonForQuery) { const messageUnsupportedReason = ( - - {unsupportedReasonForQuery} - + ); return overrideMissingContent diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index 9fbc34cb62ec7..5f63dcdb0787a 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -27,7 +27,7 @@ export const getReasonIfFieldStatsUnavailableForQuery = ( if (isOfAggregateQueryType(query)) { const { root } = parse(query.esql); - if (Walker.hasFunction(root, 'match')) { + if (Walker.hasFunction(root, 'match') || Walker.hasFunction(root, 'qstr')) { return i18n.translate( 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index f6e03177ef8b7..a193448722402 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -259,9 +259,7 @@ export const FieldStatisticsInitializer: FC = ({ /> ) : null} {unsupportedReason ? ( - - {unsupportedReason} - + ) : null} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index ebddfb16db7d3..0a9ffec77e6d8 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -44,9 +44,7 @@ const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps if (isESQLFieldStatisticTableEmbeddableState(props)) { const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(props.esqlQuery); return unsupportedReason ? ( - - {unsupportedReason} - + ) : ( Date: Thu, 24 Oct 2024 15:35:27 -0500 Subject: [PATCH 05/29] Update styling --- .../components/field_stats/field_stats.tsx | 13 +----- .../src/utils/get_warning_message.ts | 6 +-- .../grid_embeddable/embeddable_error_msg.tsx | 44 +++++++++++++++++++ .../grid_embeddable/field_stats_wrapper.tsx | 5 ++- 4 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 08f417e37236d..c43c8e3243348 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -19,14 +19,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import type { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import DateMath from '@kbn/datemath'; -import { - EuiButtonGroup, - EuiCallOut, - EuiLoadingSpinner, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { EuiButtonGroup, EuiLoadingSpinner, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { Axis, @@ -352,9 +345,7 @@ const FieldStatsComponent: React.FC = ({ FIELD_DATA_LABEL ); if (unsupportedReasonForQuery) { - const messageUnsupportedReason = ( - - ); + const messageUnsupportedReason = ; return overrideMissingContent ? overrideMissingContent({ diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index 5f63dcdb0787a..d3568ca87f1f1 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -13,11 +13,11 @@ import { isOfAggregateQueryType } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; const FIELD_STATISTICS_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldStatisticsLabel', { - defaultMessage: `Field statistics`, + defaultMessage: `Field statistics are`, }); export const FIELD_DATA_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldDataLabel', { - defaultMessage: `Field data`, + defaultMessage: `Field data is`, }); export const getReasonIfFieldStatsUnavailableForQuery = ( @@ -31,7 +31,7 @@ export const getReasonIfFieldStatsUnavailableForQuery = ( return i18n.translate( 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { - defaultMessage: `{label} is unavailable for ES|QL queries containing 'MATCH' or 'QSTR' functions.`, + defaultMessage: `{label} not supported when using 'MATCH' or 'QSTR' functions.`, values: { label }, } ); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx new file mode 100644 index 0000000000000..07e336e4baa08 --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -0,0 +1,44 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiEmptyPrompt, EuiIcon, useEuiTheme } from '@elastic/eui'; +import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import type { FieldStatisticTableEmbeddableProps } from './types'; +const FIELD_STATS_UNAVAILABLE_TITLE = i18n.translate( + 'xpack.dataVisualizer.fieldStats.unavailableTitle', + { + defaultMessage: 'Field statistics unavailable', + } +); +export const FieldStatsUnavailableMessage = ({ + id, + content, + title = FIELD_STATS_UNAVAILABLE_TITLE, +}: Pick & { content: string; title?: string }) => { + const { euiTheme } = useEuiTheme(); + if (!content) return null; + + if (id === 'dashboard_embeddable') { + return ; + } + return ( + } + color="plain" + paddingSize="m" + css={css` + margin: ${euiTheme.size.xl} auto; + `} + title={

{title}

} + titleSize="xs" + hasBorder + body={<>{content}} + /> + ); +}; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index 0a9ffec77e6d8..a808012856cf0 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useMemo } from 'react'; -import { EuiCallOut, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiEmptyPrompt } from '@elastic/eui'; import type { Required } from 'utility-types'; import { FormattedMessage } from '@kbn/i18n-react'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; @@ -22,6 +22,7 @@ import type { FieldStatisticTableEmbeddableProps, ESQLDataVisualizerGridEmbeddableState, } from './types'; +import { FieldStatsUnavailableMessage } from './embeddable_error_msg'; const EmbeddableESQLFieldStatsTableWrapper = dynamic( () => import('./embeddable_esql_field_stats_table') @@ -44,7 +45,7 @@ const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps if (isESQLFieldStatisticTableEmbeddableState(props)) { const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(props.esqlQuery); return unsupportedReason ? ( - + ) : ( Date: Thu, 24 Oct 2024 15:49:25 -0500 Subject: [PATCH 06/29] Make it so preview won't happen if not supported --- .../field_stats/field_stats_initializer.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index a193448722402..88fe4ab0310f7 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -108,11 +108,14 @@ export const FieldStatisticsInitializer: FC = ({ setDataViewId(adhocDataView.id); } - await onPreview({ - viewType, - dataViewId: adhocDataView?.id, - query, - }); + const supported = getReasonIfFieldStatsUnavailableForQuery(query) === undefined; + if (supported) { + await onPreview({ + viewType, + dataViewId: adhocDataView?.id, + query, + }); + } }, // eslint-disable-next-line react-hooks/exhaustive-deps [isEsqlMode] From ae086fc635e22eb07fb30c612a0359ef6224b5b8 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 24 Oct 2024 16:47:09 -0500 Subject: [PATCH 07/29] Update messaging --- .../src/utils/get_warning_message.ts | 2 +- .../index_data_visualizer_esql.tsx | 9 ++++++--- .../embeddables/grid_embeddable/embeddable_error_msg.tsx | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index d3568ca87f1f1..0695253c96e98 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -31,7 +31,7 @@ export const getReasonIfFieldStatsUnavailableForQuery = ( return i18n.translate( 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { - defaultMessage: `{label} not supported when using 'MATCH' or 'QSTR' functions.`, + defaultMessage: `{label} not supported for ES|QL queries with 'MATCH' or 'QSTR' functions.`, values: { label }, } ); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index 19afd14686b7a..65a808ebc6ebc 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -234,9 +234,12 @@ export const IndexDataVisualizerESQL: FC = (dataVi > {unsupportedReasonForQuery ? ( - - {unsupportedReasonForQuery} - + ) : null} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 07e336e4baa08..4129da03a0d5a 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -16,6 +16,7 @@ const FIELD_STATS_UNAVAILABLE_TITLE = i18n.translate( defaultMessage: 'Field statistics unavailable', } ); + export const FieldStatsUnavailableMessage = ({ id, content, From 1f9cef49ddd2792db9fb26911cea81dc601b5a70 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 28 Oct 2024 15:40:37 -0500 Subject: [PATCH 08/29] Exit early --- .../src/components/field_stats/field_stats.tsx | 12 ++++++++++++ .../grid_embeddable/embeddable_error_msg.tsx | 16 ++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index c43c8e3243348..2d6b616d48b65 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -191,6 +191,18 @@ const FieldStatsComponent: React.FC = ({ abortControllerRef.current?.abort(); abortControllerRef.current = new AbortController(); + /** + * If the ES|QL query is unsupported, we can exit early + */ + const unsupportedReasonForQuery = isTextBased + ? getReasonIfFieldStatsUnavailableForQuery(query, FIELD_DATA_LABEL) + : undefined; + + if (unsupportedReasonForQuery) { + setState((s) => ({ ...s, isLoading: false })); + return; + } + const results = isTextBased ? await loadFieldStatsTextBased({ services: { data }, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 4129da03a0d5a..9cc60538fa826 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiEmptyPrompt, EuiIcon, useEuiTheme } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiIcon, useEuiTheme, EuiToolTip, EuiFlexItem } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -26,7 +26,19 @@ export const FieldStatsUnavailableMessage = ({ if (!content) return null; if (id === 'dashboard_embeddable') { - return ; + return ( + + + + + + ); } return ( Date: Mon, 28 Oct 2024 16:09:55 -0500 Subject: [PATCH 09/29] Add new util to esql-utils --- packages/kbn-esql-utils/index.ts | 1 + packages/kbn-esql-utils/src/index.ts | 1 + .../src/utils/query_contains_function.ts | 41 +++++++++++++++++++ .../src/utils/get_warning_message.ts | 21 +++------- 4 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 packages/kbn-esql-utils/src/utils/query_contains_function.ts diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index ee47c0321e2e3..4d539592e790c 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -32,6 +32,7 @@ export { isESQLColumnSortable, isESQLColumnGroupable, TextBasedLanguages, + queryCannotBeSampled, } from './src'; export { ENABLE_ESQL, FEEDBACK_LINK } from './constants'; diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index cf530be20d7ae..9062f1cb9fcf3 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -22,6 +22,7 @@ export { retrieveMetadataColumns, getQueryColumnsFromESQLQuery, } from './utils/query_parsing_helpers'; +export { queryCannotBeSampled } from './utils/query_contains_function'; export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query'; export { getESQLQueryColumns, diff --git a/packages/kbn-esql-utils/src/utils/query_contains_function.ts b/packages/kbn-esql-utils/src/utils/query_contains_function.ts new file mode 100644 index 0000000000000..6b2b7fab46628 --- /dev/null +++ b/packages/kbn-esql-utils/src/utils/query_contains_function.ts @@ -0,0 +1,41 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +import type { AggregateQuery, Query } from '@kbn/es-query'; +import { Walker } from '@kbn/esql-ast'; +import { parse } from '@kbn/esql-ast'; +import { isOfAggregateQueryType } from '@kbn/es-query'; + +/** + * Check if the query contains any of the function names being passed in + * @param query + * @param functions list of function names to check for + * @returns + */ +export const queryContainsFunction = ( + query: AggregateQuery | Query | { [key: string]: any } | undefined | null, + functions: string[] +): boolean => { + if (query && isOfAggregateQueryType(query)) { + const { root } = parse(query.esql); + return functions.some((f) => Walker.hasFunction(root, f)); + } + return false; +}; + +const UNSAMPLABLE_FUNCTIONS = ['match', 'qstr']; +/** + * Check if the query contains any function that cannot be used after LIMIT clause + * @param query + * @returns + */ +export const queryCannotBeSampled = ( + query: AggregateQuery | Query | { [key: string]: any } | undefined | null +): boolean => { + return queryContainsFunction(query, UNSAMPLABLE_FUNCTIONS); +}; diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index 0695253c96e98..fa8cfad0adc60 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -7,10 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ import type { AggregateQuery, Query } from '@kbn/es-query'; -import { Walker } from '@kbn/esql-ast'; -import { parse } from '@kbn/esql-ast'; -import { isOfAggregateQueryType } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; +import { queryCannotBeSampled } from '@kbn/esql-utils'; const FIELD_STATISTICS_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldStatisticsLabel', { defaultMessage: `Field statistics are`, @@ -24,17 +22,10 @@ export const getReasonIfFieldStatsUnavailableForQuery = ( query?: AggregateQuery | Query | { [key: string]: any }, label: string = FIELD_STATISTICS_LABEL ): string | undefined => { - if (isOfAggregateQueryType(query)) { - const { root } = parse(query.esql); - - if (Walker.hasFunction(root, 'match') || Walker.hasFunction(root, 'qstr')) { - return i18n.translate( - 'unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', - { - defaultMessage: `{label} not supported for ES|QL queries with 'MATCH' or 'QSTR' functions.`, - values: { label }, - } - ); - } + if (queryCannotBeSampled(query)) { + return i18n.translate('unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { + defaultMessage: `{label} not supported for ES|QL queries with 'MATCH' or 'QSTR' functions.`, + values: { label }, + }); } }; From b2b7222b433bed014ea4de477f3285ac9f940542 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 30 Oct 2024 11:01:57 -0500 Subject: [PATCH 10/29] Refactor to have separate messaging for field stats, change from call out to tooltip --- packages/kbn-esql-utils/src/index.ts | 2 +- .../src/utils/query_cannot_be_sampled.test.ts | 46 +++++++++++++++ ...function.ts => query_cannot_be_sampled.ts} | 0 .../components/field_stats/field_stats.tsx | 12 +--- .../src/utils/get_warning_message.ts | 8 +-- .../view_mode_toggle/view_mode_toggle.tsx | 5 +- .../index_data_visualizer_esql.tsx | 2 +- .../field_stats/field_stats_initializer.tsx | 58 +++++++++++-------- .../grid_embeddable/field_stats_wrapper.tsx | 2 +- ...n_fieldstats_unavailable_for_esql_query.ts | 22 +++++++ .../plugins/data_visualizer/public/plugin.ts | 2 + 11 files changed, 115 insertions(+), 44 deletions(-) create mode 100644 packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts rename packages/kbn-esql-utils/src/utils/{query_contains_function.ts => query_cannot_be_sampled.ts} (100%) create mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query.ts diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index 9062f1cb9fcf3..77f3af8999538 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -22,7 +22,7 @@ export { retrieveMetadataColumns, getQueryColumnsFromESQLQuery, } from './utils/query_parsing_helpers'; -export { queryCannotBeSampled } from './utils/query_contains_function'; +export { queryCannotBeSampled } from './utils/query_cannot_be_sampled'; export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query'; export { getESQLQueryColumns, diff --git a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts new file mode 100644 index 0000000000000..6e7514f756295 --- /dev/null +++ b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts @@ -0,0 +1,46 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +import { queryCannotBeSampled } from './query_cannot_be_sampled'; +describe('queryCannotBeSampled', () => { + it('should return true if query contains "match" function', () => { + expect( + queryCannotBeSampled({ esql: 'SELECT * FROM index | where match(field, "value")' }) + ).toBe(true); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where match()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where MATCH()' })).toBe(true); + }); + + it('should return true if query contains "qstr" function', () => { + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where qstr(field, "value")' })).toBe( + true + ); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where qstr()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where QSTR()' })).toBe(true); + }); + + it('should return false if query contains names', () => { + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval match =' })).toBe(false); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval MATCH =' })).toBe(false); + expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval qstr =' })).toBe(false); + }); + + it('should return false if query does not contain unsamplable functions', () => { + expect( + queryCannotBeSampled({ esql: 'SELECT * FROM index | eval otherFunction(field, "value")' }) + ).toBe(false); + expect( + queryCannotBeSampled({ esql: 'SELECT * FROM index | where otherFunction(field, "value")' }) + ).toBe(false); + }); + + it('should return false if query is undefined', () => { + expect(queryCannotBeSampled(undefined)).toBe(false); + expect(queryCannotBeSampled(null)).toBe(false); + }); +}); diff --git a/packages/kbn-esql-utils/src/utils/query_contains_function.ts b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts similarity index 100% rename from packages/kbn-esql-utils/src/utils/query_contains_function.ts rename to packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 2d6b616d48b65..d92a97d4202da 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -53,10 +53,7 @@ import { import { FieldSummaryMessage } from './field_summary_message'; import { FieldNumberSummary, isNumberSummaryValid } from './field_number_summary'; import { ErrorBoundary } from '../error_boundary'; -import { - FIELD_DATA_LABEL, - getReasonIfFieldStatsUnavailableForQuery, -} from '../../utils/get_warning_message'; +import { getReasonIfFieldDataUnavailableForQuery } from '../../utils/get_warning_message'; export interface FieldStatsState { isLoading: boolean; @@ -195,7 +192,7 @@ const FieldStatsComponent: React.FC = ({ * If the ES|QL query is unsupported, we can exit early */ const unsupportedReasonForQuery = isTextBased - ? getReasonIfFieldStatsUnavailableForQuery(query, FIELD_DATA_LABEL) + ? getReasonIfFieldDataUnavailableForQuery(query) : undefined; if (unsupportedReasonForQuery) { @@ -352,10 +349,7 @@ const FieldStatsComponent: React.FC = ({ : messageNoAnalysis; } - const unsupportedReasonForQuery = getReasonIfFieldStatsUnavailableForQuery( - query, - FIELD_DATA_LABEL - ); + const unsupportedReasonForQuery = getReasonIfFieldDataUnavailableForQuery(query); if (unsupportedReasonForQuery) { const messageUnsupportedReason = ; diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index fa8cfad0adc60..52859a26f53ba 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -18,14 +18,12 @@ export const FIELD_DATA_LABEL = i18n.translate('unifiedFieldList.fieldStats.fiel defaultMessage: `Field data is`, }); -export const getReasonIfFieldStatsUnavailableForQuery = ( - query?: AggregateQuery | Query | { [key: string]: any }, - label: string = FIELD_STATISTICS_LABEL +export const getReasonIfFieldDataUnavailableForQuery = ( + query?: AggregateQuery | Query | { [key: string]: any } ): string | undefined => { if (queryCannotBeSampled(query)) { return i18n.translate('unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { - defaultMessage: `{label} not supported for ES|QL queries with 'MATCH' or 'QSTR' functions.`, - values: { label }, + defaultMessage: `Field data is not available for ES|QL queries with 'MATCH' or 'QSTR' functions.`, }); } }; 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 78e4c643615ef..d5b7514634f3f 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 @@ -14,7 +14,6 @@ import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; import type { DataView } from '@kbn/data-views-plugin/common'; import useMountedState from 'react-use/lib/useMountedState'; -import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { VIEW_MODE } from '../../../common/constants'; import { useDiscoverServices } from '../../hooks/use_discover_services'; import type { DiscoverStateContainer } from '../../application/main/state_management/discover_state'; @@ -49,8 +48,8 @@ export const DocumentViewModeToggle = ({ ); const state = stateContainer.appState.getState(); const fieldStatsWarningMsgForQuery = useMemo(() => { - return getReasonIfFieldStatsUnavailableForQuery(state.query); - }, [state.query]); + return dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query); + }, [state.query, dataVisualizerService]); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index 65a808ebc6ebc..c6190c87bcae5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -28,7 +28,6 @@ import { } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/common'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; -import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getOrCreateDataViewByIndexPattern } from '../../search_strategy/requests/get_data_view_by_index_pattern'; import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme'; import { DATA_VISUALIZER_INDEX_VIEWER } from '../../constants/index_data_visualizer_viewer'; @@ -50,6 +49,7 @@ import type { import type { ESQLQuery } from '../../search_strategy/requests/esql_utils'; import { isESQLQuery } from '../../search_strategy/requests/esql_utils'; import { FieldStatsComponentType } from '../../constants/field_stats_component_type'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; export interface IndexDataVisualizerESQLProps { getAdditionalLinks?: GetAdditionalLinks; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index 88fe4ab0310f7..c9428f1861b10 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -29,7 +29,6 @@ import { ENABLE_ESQL, getESQLAdHocDataview } from '@kbn/esql-utils'; import type { AggregateQuery } from '@kbn/es-query'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; -import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { useDataVisualizerKibana } from '../../../kibana_context'; import { FieldStatsESQLEditor } from './field_stats_esql_editor'; import type { @@ -39,6 +38,7 @@ import type { import { FieldStatsInitializerViewType } from '../grid_embeddable/types'; import { isESQLQuery } from '../../search_strategy/requests/esql_utils'; import { DataSourceTypeSelector } from './field_stats_initializer_view_type'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; export interface FieldStatsInitializerProps { initialInput?: Partial; @@ -261,9 +261,6 @@ export const FieldStatisticsInitializer: FC = ({ disableSubmitAction={!!unsupportedReason} /> ) : null} - {unsupportedReason ? ( - - ) : null} @@ -287,26 +284,39 @@ export const FieldStatisticsInitializer: FC = ({ /> - - - - - + + {unsupportedReason ? ( + + + + ) : null} + + + + + + + diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index a808012856cf0..cabc020d67c4e 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -16,13 +16,13 @@ import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import type { DatePickerDependencies } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { pick } from 'lodash'; -import { getReasonIfFieldStatsUnavailableForQuery } from '@kbn/unified-field-list/src/utils/get_warning_message'; import { getCoreStart, getPluginsStart } from '../../../../kibana_services'; import type { FieldStatisticTableEmbeddableProps, ESQLDataVisualizerGridEmbeddableState, } from './types'; import { FieldStatsUnavailableMessage } from './embeddable_error_msg'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; const EmbeddableESQLFieldStatsTableWrapper = dynamic( () => import('./embeddable_esql_field_stats_table') diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query.ts new file mode 100644 index 0000000000000..f6a9996aed42e --- /dev/null +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query.ts @@ -0,0 +1,22 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AggregateQuery } from '@kbn/es-query'; + +import type { Query } from '@kbn/es-query'; +import { queryCannotBeSampled } from '@kbn/esql-utils'; +import { i18n } from '@kbn/i18n'; + +export const getReasonIfFieldStatsUnavailableForQuery = ( + query?: AggregateQuery | Query | { [key: string]: any } +): string | undefined => { + if (queryCannotBeSampled(query)) { + return i18n.translate('xpack.dataVisualizer.fieldStats.unavailableForESQLQueryDescription', { + defaultMessage: `Field statistics are not available for ES|QL queries with 'MATCH' or 'QSTR' functions.`, + }); + } +}; diff --git a/x-pack/plugins/data_visualizer/public/plugin.ts b/x-pack/plugins/data_visualizer/public/plugin.ts index deaf4bdc99678..4c543d356a3c7 100644 --- a/x-pack/plugins/data_visualizer/public/plugin.ts +++ b/x-pack/plugins/data_visualizer/public/plugin.ts @@ -22,6 +22,7 @@ import type { } from './application/common/types/data_visualizer_plugin'; import { registerEmbeddables } from './application/index_data_visualizer/embeddables/field_stats'; import { registerDataVisualizerUiActions } from './application/index_data_visualizer/ui_actions'; +import { getReasonIfFieldStatsUnavailableForQuery } from './application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query'; export type DataVisualizerPluginSetup = ReturnType; export type DataVisualizerPluginStart = ReturnType; @@ -84,6 +85,7 @@ export class DataVisualizerPlugin getIndexDataVisualizerComponent, getDataDriftComponent, getMaxBytesFormatted, + getReasonIfFieldStatsUnavailableForQuery, FieldStatisticsTable: dynamic( async () => import( From 7321cf5da14462c2dbb15808e1dc146c1bf9dc36 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 31 Oct 2024 09:04:00 -0500 Subject: [PATCH 11/29] Remove unused constants --- .../src/utils/get_warning_message.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts index 52859a26f53ba..f63db4b7d614c 100644 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts @@ -10,14 +10,6 @@ import type { AggregateQuery, Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { queryCannotBeSampled } from '@kbn/esql-utils'; -const FIELD_STATISTICS_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldStatisticsLabel', { - defaultMessage: `Field statistics are`, -}); - -export const FIELD_DATA_LABEL = i18n.translate('unifiedFieldList.fieldStats.fieldDataLabel', { - defaultMessage: `Field data is`, -}); - export const getReasonIfFieldDataUnavailableForQuery = ( query?: AggregateQuery | Query | { [key: string]: any } ): string | undefined => { From 4215c9c5c7a50cf6d4970f44c3e506846b6aa76d Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 31 Oct 2024 09:12:58 -0500 Subject: [PATCH 12/29] Refactor --- .../src/components/field_stats/field_stats.tsx | 12 ------------ .../load_field_stats_text_based.ts | 10 ++++++++++ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index d92a97d4202da..481d9f71a7279 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -188,18 +188,6 @@ const FieldStatsComponent: React.FC = ({ abortControllerRef.current?.abort(); abortControllerRef.current = new AbortController(); - /** - * If the ES|QL query is unsupported, we can exit early - */ - const unsupportedReasonForQuery = isTextBased - ? getReasonIfFieldDataUnavailableForQuery(query) - : undefined; - - if (unsupportedReasonForQuery) { - setState((s) => ({ ...s, isLoading: false })); - return; - } - const results = isTextBased ? await loadFieldStatsTextBased({ services: { data }, diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts index 5f77f15906896..7a0b1dbc544d4 100644 --- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts +++ b/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts @@ -18,6 +18,7 @@ import { fetchAndCalculateFieldStats, } from './field_stats_utils_text_based'; import { ESQL_SAFE_LIMIT } from '../../constants'; +import { getReasonIfFieldDataUnavailableForQuery } from '../../utils/get_warning_message'; interface FetchFieldStatsParamsTextBased { services: { @@ -54,6 +55,15 @@ export const loadFieldStatsTextBased: LoadFieldStatsTextBasedHandler = async ({ baseQuery, abortController, }) => { + /** + * If the ES|QL query is unsupported, we can exit early + */ + const unsupportedReasonForQuery = getReasonIfFieldDataUnavailableForQuery(baseQuery); + + if (unsupportedReasonForQuery) { + return {}; + } + const { data } = services; try { From 989a523ef0823e3d64f0732bba0033ac9f3fbd42 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 31 Oct 2024 09:39:52 -0500 Subject: [PATCH 13/29] Fix test --- .../src/utils/query_cannot_be_sampled.test.ts | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts index 6e7514f756295..f8a6ec2f461ee 100644 --- a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts @@ -9,34 +9,30 @@ import { queryCannotBeSampled } from './query_cannot_be_sampled'; describe('queryCannotBeSampled', () => { it('should return true if query contains "match" function', () => { - expect( - queryCannotBeSampled({ esql: 'SELECT * FROM index | where match(field, "value")' }) - ).toBe(true); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where match()' })).toBe(true); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where MATCH()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where match(field, "value")' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where match()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where MATCH()' })).toBe(true); }); it('should return true if query contains "qstr" function', () => { - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where qstr(field, "value")' })).toBe( - true - ); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where qstr()' })).toBe(true); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | where QSTR()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where qstr(field, "value")' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where qstr()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where QSTR()' })).toBe(true); }); it('should return false if query contains names', () => { - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval match =' })).toBe(false); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval MATCH =' })).toBe(false); - expect(queryCannotBeSampled({ esql: 'SELECT * FROM index | eval qstr =' })).toBe(false); + expect(queryCannotBeSampled({ esql: 'FROM index | eval match =' })).toBe(false); + expect(queryCannotBeSampled({ esql: 'FROM index | eval MATCH =' })).toBe(false); + expect(queryCannotBeSampled({ esql: 'FROM index | eval qstr =' })).toBe(false); }); it('should return false if query does not contain unsamplable functions', () => { - expect( - queryCannotBeSampled({ esql: 'SELECT * FROM index | eval otherFunction(field, "value")' }) - ).toBe(false); - expect( - queryCannotBeSampled({ esql: 'SELECT * FROM index | where otherFunction(field, "value")' }) - ).toBe(false); + expect(queryCannotBeSampled({ esql: 'FROM index | eval otherFunction(field, "value")' })).toBe( + false + ); + expect(queryCannotBeSampled({ esql: 'FROM index | where otherFunction(field, "value")' })).toBe( + false + ); }); it('should return false if query is undefined', () => { From 2dd74e972dd8aadfa1b034048585bc6460496a9c Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Thu, 31 Oct 2024 11:08:43 -0500 Subject: [PATCH 14/29] Fix logic for detecting function --- .../src/utils/query_cannot_be_sampled.test.ts | 2 ++ .../kbn-esql-utils/src/utils/query_cannot_be_sampled.ts | 7 ++++++- src/plugins/discover/public/__mocks__/services.ts | 1 + .../embeddables/field_stats/field_stats_initializer.tsx | 2 +- .../hooks/esql/use_data_visualizer_esql_data.tsx | 5 ++++- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts index f8a6ec2f461ee..e0ff5d01be411 100644 --- a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.test.ts @@ -12,6 +12,8 @@ describe('queryCannotBeSampled', () => { expect(queryCannotBeSampled({ esql: 'FROM index | where match(field, "value")' })).toBe(true); expect(queryCannotBeSampled({ esql: 'FROM index | where match()' })).toBe(true); expect(queryCannotBeSampled({ esql: 'FROM index | where MATCH()' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where MATCH(fieldName,)' })).toBe(true); + expect(queryCannotBeSampled({ esql: 'FROM index | where MATCH(,)' })).toBe(true); }); it('should return true if query contains "qstr" function', () => { diff --git a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts index 6b2b7fab46628..c4cbd34e75371 100644 --- a/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts +++ b/packages/kbn-esql-utils/src/utils/query_cannot_be_sampled.ts @@ -23,7 +23,12 @@ export const queryContainsFunction = ( ): boolean => { if (query && isOfAggregateQueryType(query)) { const { root } = parse(query.esql); - return functions.some((f) => Walker.hasFunction(root, f)); + return functions.some( + (f) => + Walker.hasFunction(root, f) || + // Walker API expects valid queries so we need to do additional check for partial matches + root.commands.some((c) => c.text.toLowerCase().includes(`${f}(`)) + ); } return false; }; diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index f00d105444630..50bbf34ff7a95 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -163,6 +163,7 @@ export function createDiscoverServicesMock(): DiscoverServices { data: dataPlugin, dataVisualizer: { FieldStatisticsTable: jest.fn(() => createElement('div')), + getReasonIfFieldStatsUnavailableForQuery: jest.fn().mockReturnValue(undefined), }, aiops: { getPatternAnalysisAvailable: jest.fn().mockResolvedValue(jest.fn().mockResolvedValue(true)), diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx index c9428f1861b10..eb829e9a20cd8 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/field_stats/field_stats_initializer.tsx @@ -292,7 +292,7 @@ export const FieldStatisticsInitializer: FC = ({ > {unsupportedReason ? ( - + ) : null} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx index def3e5e4b75bb..ed30df57fca2c 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx @@ -44,6 +44,7 @@ import type { } from '../../embeddables/grid_embeddable/types'; import { getDefaultPageState } from '../../constants/index_data_visualizer_viewer'; import { DEFAULT_ESQL_LIMIT } from '../../constants/esql_constants'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; type AnyQuery = Query | AggregateQuery; @@ -161,8 +162,10 @@ export const useESQLDataVisualizerData = ( const tf = timefilter; if (!buckets || !tf || (isESQLQuery(query) && query.esql === '')) return; - const activeBounds = tf.getActiveBounds(); + const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(query); + if (unsupportedReason) return; + const activeBounds = tf.getActiveBounds(); let earliest: number | undefined; let latest: number | undefined; if (activeBounds !== undefined && currentDataView?.timeFieldName !== undefined) { From 3ec82635f084e8f25c5c216b66db3019b0b8ab72 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 4 Nov 2024 09:28:20 -0600 Subject: [PATCH 15/29] Update tests --- .../src/components/field_stats/field_stats.tsx | 11 +++++++++-- .../field_stats/field_summary_message.tsx | 12 ++++++++++-- .../view_mode_toggle/view_mode_toggle.tsx | 6 ++++-- .../discover/group6/_sidebar_field_stats.ts | 17 +++++++++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 481d9f71a7279..5b71001278770 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -337,9 +337,16 @@ const FieldStatsComponent: React.FC = ({ : messageNoAnalysis; } - const unsupportedReasonForQuery = getReasonIfFieldDataUnavailableForQuery(query); + const unsupportedReasonForQuery = isTextBased + ? getReasonIfFieldDataUnavailableForQuery(query) + : undefined; if (unsupportedReasonForQuery) { - const messageUnsupportedReason = ; + const messageUnsupportedReason = ( + + ); return overrideMissingContent ? overrideMissingContent({ diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_summary_message.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_summary_message.tsx index aab65e08f3a6f..2ed392abf1884 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_summary_message.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_summary_message.tsx @@ -12,8 +12,16 @@ import { EuiText } from '@elastic/eui'; export interface FieldSummaryMessageProps { message: string; + dataTestSubj?: string; } -export const FieldSummaryMessage: React.FC = ({ message }) => { - return {message}; +export const FieldSummaryMessage: React.FC = ({ + message, + dataTestSubj, +}) => { + return ( + + {message} + + ); }; 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 d5b7514634f3f..8f6c32b30069e 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 @@ -48,8 +48,10 @@ export const DocumentViewModeToggle = ({ ); const state = stateContainer.appState.getState(); const fieldStatsWarningMsgForQuery = useMemo(() => { - return dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query); - }, [state.query, dataVisualizerService]); + return ( + isEsqlMode && dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query) + ); + }, [state.query, dataVisualizerService, isEsqlMode]); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts index 325adb313ed6c..947b355fcfc76 100644 --- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts +++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts @@ -338,6 +338,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(editorValue).to.eql(`row enabled = true\n| WHERE \`enabled\`!=true`); await unifiedFieldList.closeFieldPopover(); }); + + it('should show a reason if field data is unavailable for the query', async () => { + const testQuery = `from logstash-* METADATA _index, _id | sort @timestamp desc | where match(extension.raw,"css")`; + + await monacoEditor.setCodeEditorValue(testQuery); + await testSubjects.click('querySubmitButton'); + await header.waitUntilLoadingHasFinished(); + await unifiedFieldList.waitUntilSidebarHasLoaded(); + + await unifiedFieldList.clickFieldListItem('@message'); + await testSubjects.existOrFail('unifiedFieldList.fieldStats.unsupportedReason'); + expect( + await testSubjects.getVisibleText('unifiedFieldList.fieldStats.unsupportedReason') + ).to.contain( + `Field data is not available for ES|QL queries with 'MATCH' or 'QSTR' functions.` + ); + }); }); }); } From 2c4f7400fccd1d125bae9cbe334c8de8d8c153a6 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 4 Nov 2024 11:28:35 -0600 Subject: [PATCH 16/29] Fix undefined --- .../public/components/view_mode_toggle/view_mode_toggle.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 8f6c32b30069e..7b587c95c0ccc 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 @@ -48,9 +48,9 @@ export const DocumentViewModeToggle = ({ ); const state = stateContainer.appState.getState(); const fieldStatsWarningMsgForQuery = useMemo(() => { - return ( - isEsqlMode && dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query) - ); + return isEsqlMode + ? dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query) + : undefined; }, [state.query, dataVisualizerService, isEsqlMode]); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); From 0dfc1165fe16743cd581899d7aa875030e75bacb Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 4 Nov 2024 16:37:48 -0600 Subject: [PATCH 17/29] Remove field stats tab in ES|QL mode for Discover & dashboard, redirect if that mode is chosen --- .../field_stats_table/field_stats_tab.tsx | 35 +++- .../view_mode_toggle/view_mode_toggle.tsx | 13 +- ...embeddable_field_stats_table_component.tsx | 27 ++- x-pack/plugins/data_visualizer/kibana.jsonc | 3 +- .../grid_embeddable/embeddable_error_msg.tsx | 8 +- .../grid_embeddable/field_stats_wrapper.tsx | 14 +- .../ui_actions/create_field_stats_table.tsx | 195 ------------------ .../index_data_visualizer/ui_actions/index.ts | 21 -- .../plugins/data_visualizer/public/plugin.ts | 7 - .../translations/translations/zh-CN.json | 1 - 10 files changed, 83 insertions(+), 241 deletions(-) delete mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/create_field_stats_table.tsx delete mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx index c9ac877474e87..a8f0552777a50 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx @@ -9,9 +9,14 @@ import React from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; -import { FieldStatisticsTable, type FieldStatisticsTableProps } from './field_stats_table'; -import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiIcon, useEuiTheme } from '@elastic/eui'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { css } from '@emotion/react'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { FieldStatisticsTable, type FieldStatisticsTableProps } from './field_stats_table'; +import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; export const FieldStatisticsTab: React.FC> = React.memo((props) => { @@ -20,8 +25,34 @@ export const FieldStatisticsTab: React.FC} + color="plain" + paddingSize="m" + css={css` + margin: ${euiTheme.size.xl} auto; + `} + title={ +

+ +

+ } + titleSize="xs" + hasBorder + /> + ); + } + return ( (null); const showFieldStatisticsTab = useMemo( - () => uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, - [dataVisualizerService, uiSettings] + () => + !isEsqlMode && uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, + [dataVisualizerService, uiSettings, isEsqlMode] ); const isMounted = useMountedState(); @@ -107,6 +108,12 @@ export const DocumentViewModeToggle = ({ } `; + useEffect(() => { + if (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isEsqlMode) { + setDiscoverViewMode(VIEW_MODE.DOCUMENT_LEVEL); + } + }, [viewMode, isEsqlMode, setDiscoverViewMode]); + return ( setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} data-test-subj="dscViewModeFieldStatsButton" diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx index ede93bf6b6f72..76b99e33002a4 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx @@ -14,6 +14,9 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { FetchContext, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; +import { EuiEmptyPrompt, EuiIcon, useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { FormattedMessage } from '@kbn/i18n-react'; import { FieldStatisticsTable } from '../../application/main/components/field_stats_table'; import { isEsqlMode } from '../initialize_fetch'; import type { SearchEmbeddableApi, SearchEmbeddableStateManager } from '../types'; @@ -37,8 +40,30 @@ export function SearchEmbeddablFieldStatsTableComponent({ api.fetchContext$, api.savedSearch$ ); - + const { euiTheme } = useEuiTheme(); const isEsql = useMemo(() => isEsqlMode(savedSearch), [savedSearch]); + if (isEsql) { + return ( + } + color="plain" + paddingSize="m" + css={css` + margin: ${euiTheme.size.xl} auto; + `} + title={ +

+ +

+ } + titleSize="xs" + hasBorder + /> + ); + } return ( - - - + ); } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index cabc020d67c4e..715182c67aa9e 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -16,13 +16,14 @@ import { DatePickerContextProvider } from '@kbn/ml-date-picker'; import type { DatePickerDependencies } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { pick } from 'lodash'; +import { isOfAggregateQueryType } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; import { getCoreStart, getPluginsStart } from '../../../../kibana_services'; import type { FieldStatisticTableEmbeddableProps, ESQLDataVisualizerGridEmbeddableState, } from './types'; import { FieldStatsUnavailableMessage } from './embeddable_error_msg'; -import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; const EmbeddableESQLFieldStatsTableWrapper = dynamic( () => import('./embeddable_esql_field_stats_table') @@ -43,9 +44,14 @@ function isFieldStatisticTableEmbeddableState( const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps) => { if (isESQLFieldStatisticTableEmbeddableState(props)) { - const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(props.esqlQuery); - return unsupportedReason ? ( - + const isEsql = props.esqlQuery && isOfAggregateQueryType(props.esqlQuery); + return isEsql ? ( + ) : ( => { - const { apiIsPresentationContainer } = await import('@kbn/presentation-containers'); - // we cannot have an async type check, so return the casted parentApi rather than a boolean - return apiIsPresentationContainer(parentApi) ? (parentApi as PresentationContainer) : undefined; -}; - -interface FieldStatsActionContext extends EmbeddableApiContext { - embeddable: FieldStatisticsTableEmbeddableApi; -} - -async function updatePanelFromFlyoutEdits({ - api, - isNewPanel, - deletePanel, - coreStart, - pluginStart, - initialState, -}: { - api: FieldStatisticsTableEmbeddableApi; - isNewPanel: boolean; - deletePanel?: () => void; - coreStart: CoreStart; - pluginStart: DataVisualizerStartDependencies; - initialState: FieldStatsInitialState; - fieldStatsControlsApi?: FieldStatsControlsApi; -}) { - const parentApi = api.parentApi; - const overlayTracker = tracksOverlays(parentApi) ? parentApi : undefined; - const services = { - ...coreStart, - ...pluginStart, - }; - let hasChanged = false; - const cancelChanges = () => { - // Reset to initialState in case user has changed the preview state - if (hasChanged && api && initialState) { - api.updateUserInput(initialState); - } - - if (isNewPanel && deletePanel) { - deletePanel(); - } - flyoutSession.close(); - overlayTracker?.clearOverlays(); - }; - - const update = async (nextUpdate: FieldStatsInitialState) => { - const esqlQuery = nextUpdate?.query?.esql; - if (isDefined(esqlQuery)) { - const dv = await getOrCreateDataViewByIndexPattern( - pluginStart.data.dataViews, - esqlQuery, - undefined - ); - if (dv?.id && nextUpdate.dataViewId !== dv.id) { - nextUpdate.dataViewId = dv.id; - } - } - if (api) { - api.updateUserInput(nextUpdate); - } - - flyoutSession.close(); - overlayTracker?.clearOverlays(); - }; - const flyoutSession = services.overlays.openFlyout( - toMountPoint( - - { - if (api.updateUserInput) { - api.updateUserInput(nextUpdate); - hasChanged = true; - } - }} - onCreate={update} - onCancel={cancelChanges} - isNewPanel={isNewPanel} - /> - , - coreStart - ), - { - ownFocus: true, - size: 's', - paddingSize: 'm', - hideCloseButton: true, - type: 'push', - 'data-test-subj': 'fieldStatisticsInitializerFlyout', - onClose: cancelChanges, - } - ); - overlayTracker?.openOverlay(flyoutSession, { focusedPanelId: api.uuid }); -} - -export function createAddFieldStatsTableAction( - coreStart: CoreStart, - pluginStart: DataVisualizerStartDependencies -): UiActionsActionDefinition { - return { - id: 'create-field-stats-table', - grouping: COMMON_VISUALIZATION_GROUPING, - order: 10, - getIconType: () => 'fieldStatistics', - getDisplayName: () => - i18n.translate('xpack.dataVisualizer.fieldStatistics.displayName', { - defaultMessage: 'Field statistics', - }), - disabled: !coreStart.uiSettings.get(ENABLE_ESQL), - async isCompatible(context: EmbeddableApiContext) { - return ( - Boolean(await parentApiIsCompatible(context.embeddable)) && - coreStart.uiSettings.get(ENABLE_ESQL) - ); - }, - async execute(context) { - const presentationContainerParent = await parentApiIsCompatible(context.embeddable); - if (!presentationContainerParent) throw new IncompatibleActionError(); - - const isEsqlEnabled = coreStart.uiSettings.get(ENABLE_ESQL); - try { - const defaultIndexPattern = await pluginStart.data.dataViews.getDefault(); - const defaultInitialState: FieldStatsInitialState = isEsqlEnabled - ? { - viewType: FieldStatsInitializerViewType.ESQL, - query: { - // Initial default query - esql: `from ${defaultIndexPattern?.getIndexPattern()} | limit 10`, - }, - } - : { - viewType: FieldStatsInitializerViewType.DATA_VIEW, - }; - const embeddable = await presentationContainerParent.addNewPanel< - object, - FieldStatisticsTableEmbeddableApi - >({ - panelType: FIELD_STATS_EMBEDDABLE_TYPE, - initialState: defaultInitialState, - }); - // open the flyout if embeddable has been created successfully - if (embeddable) { - const deletePanel = () => { - presentationContainerParent.removePanel(embeddable.uuid); - }; - - updatePanelFromFlyoutEdits({ - api: embeddable, - isNewPanel: true, - deletePanel, - coreStart, - pluginStart, - initialState: defaultInitialState, - }); - } - } catch (e) { - return Promise.reject(e); - } - }, - }; -} diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts deleted file mode 100644 index faa8f34bdfbd7..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/ui_actions/index.ts +++ /dev/null @@ -1,21 +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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { CoreStart } from '@kbn/core-lifecycle-browser'; -import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public'; -import type { DataVisualizerStartDependencies } from '../../common/types/data_visualizer_plugin'; - -export function registerDataVisualizerUiActions( - uiActions: UiActionsSetup, - coreStart: CoreStart, - pluginStart: DataVisualizerStartDependencies -) { - import('./create_field_stats_table').then(({ createAddFieldStatsTableAction }) => { - const addFieldStatsAction = createAddFieldStatsTableAction(coreStart, pluginStart); - uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addFieldStatsAction); - }); -} diff --git a/x-pack/plugins/data_visualizer/public/plugin.ts b/x-pack/plugins/data_visualizer/public/plugin.ts index 4c543d356a3c7..3bcd83efed102 100644 --- a/x-pack/plugins/data_visualizer/public/plugin.ts +++ b/x-pack/plugins/data_visualizer/public/plugin.ts @@ -21,7 +21,6 @@ import type { DataVisualizerStartDependencies, } from './application/common/types/data_visualizer_plugin'; import { registerEmbeddables } from './application/index_data_visualizer/embeddables/field_stats'; -import { registerDataVisualizerUiActions } from './application/index_data_visualizer/ui_actions'; import { getReasonIfFieldStatsUnavailableForQuery } from './application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query'; export type DataVisualizerPluginSetup = ReturnType; export type DataVisualizerPluginStart = ReturnType; @@ -58,12 +57,6 @@ export class DataVisualizerPlugin registerEmbeddables(plugins.embeddable, core); } - const [coreStart, pluginStart] = await core.getStartServices(); - - if (plugins.uiActions) { - registerDataVisualizerUiActions(plugins.uiActions, coreStart, pluginStart); - } - if (plugins.home) { registerHomeAddData(plugins.home, this.resultsLinks); registerHomeFeatureCatalogue(plugins.home); diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5fbe2d35b72a6..08f0534ac133c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6846,7 +6846,6 @@ "securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications": "受信任的应用程序", "securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications.description": "帮助减少与其他软件(通常指其他防病毒或终端安全应用程序)的冲突。", "securitySolutionPackages.features.featureRegistry.subFeatures.trustedApplications.privilegesTooltip": "访问受信任的应用程序需要所有工作区。", - "sgcuritySolutionPackages.flyout.shared.errorDescription": "显示 {message} 时出现错误。", "securitySolutionPackages.markdown.insight.upsell": "升级到{requiredLicense}以利用调查指南中的洞见", "securitySolutionPackages.markdown.investigationGuideInteractions.upsell": "升级到 {requiredLicense} 以利用调查指南交互", "securitySolutionPackages.navigation.landingLinks": "安全视图", From 881e57b98b9ce5b249036e0ab38c27858a325055 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 4 Nov 2024 22:55:23 +0000 Subject: [PATCH 18/29] [CI] Auto-commit changed files from 'node scripts/yarn_deduplicate' --- x-pack/plugins/data_visualizer/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/data_visualizer/tsconfig.json b/x-pack/plugins/data_visualizer/tsconfig.json index 7df4f57dc987b..970526cdf464e 100644 --- a/x-pack/plugins/data_visualizer/tsconfig.json +++ b/x-pack/plugins/data_visualizer/tsconfig.json @@ -86,7 +86,6 @@ "@kbn/core-lifecycle-browser", "@kbn/presentation-containers", "@kbn/react-kibana-mount", - "@kbn/visualizations-plugin", "@kbn/core-ui-settings-browser" ], "exclude": [ From 7ea2efbb4e1367cf24bb58fb421f64c1dc39e2b9 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 4 Nov 2024 17:22:10 -0600 Subject: [PATCH 19/29] Update tests --- .../view_mode_toggle/view_mode_toggle.test.tsx | 10 ++-------- test/functional/apps/discover/esql/_esql_view.ts | 2 +- .../apps/discover/group6/_field_stats_table.ts | 5 ++--- .../apps/discover/group6/_view_mode_toggle.ts | 2 +- .../test_suites/common/discover/esql/_esql_view.ts | 2 +- 5 files changed, 7 insertions(+), 14 deletions(-) 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 7d88f9ad1fef4..eff7f4e4da042 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 @@ -107,15 +107,9 @@ describe('Document view mode toggle component', () => { expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(false); }); - it('should show document and field stats view if ES|QL', async () => { + it('should not show document and field stats view if ES|QL', async () => { const component = await mountComponent({ isEsqlMode: true }); - expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(true); - expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); - - expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(true); - expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(false); - expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(true); - expect(findTestSubject(component, 'dscViewModeDocumentButton').text()).toBe('Results (10)'); + expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); }); it('should set the view mode to VIEW_MODE.DOCUMENT_LEVEL when dscViewModeDocumentButton is clicked', async () => { diff --git a/test/functional/apps/discover/esql/_esql_view.ts b/test/functional/apps/discover/esql/_esql_view.ts index 31e89ac42f3ea..00ec096d24e18 100644 --- a/test/functional/apps/discover/esql/_esql_view.ts +++ b/test/functional/apps/discover/esql/_esql_view.ts @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await testSubjects.exists('showQueryBarMenu')).to.be(false); expect(await testSubjects.exists('addFilter')).to.be(false); - expect(await testSubjects.exists('dscViewModeDocumentButton')).to.be(true); + expect(await testSubjects.exists('dscViewModeDocumentButton')).to.be(false); // when Lens suggests a table, we render an ESQL based histogram expect(await testSubjects.exists('unifiedHistogramChart')).to.be(true); expect(await testSubjects.exists('discoverQueryHits')).to.be(true); diff --git a/test/functional/apps/discover/group6/_field_stats_table.ts b/test/functional/apps/discover/group6/_field_stats_table.ts index c8381976ffbab..a8463eb74c66a 100644 --- a/test/functional/apps/discover/group6/_field_stats_table.ts +++ b/test/functional/apps/discover/group6/_field_stats_table.ts @@ -64,13 +64,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.existOrFail('discoverDocTable'); }); - it('should show Field Statistics data in ES|QL mode', async () => { + it('should not show Field Statistics data in ES|QL mode', async () => { await discover.selectTextBaseLang(); await discover.waitUntilSearchingHasFinished(); - await testSubjects.click('dscViewModeFieldStatsButton'); + await testSubjects.missingOrFail('dscViewModeFieldStatsButton'); await header.waitUntilLoadingHasFinished(); - await testSubjects.existOrFail('dataVisualizerTableContainer'); }); }); }); diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index aec1cb6f459ab..4b6eb8a652c7d 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -125,7 +125,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.selectTextBaseLang(); - await testSubjects.existOrFail('dscViewModeToggle'); + await testSubjects.missingOrFail('dscViewModeToggle'); if (!useLegacyTable) { await testSubjects.existOrFail('unifiedDataTableToolbar'); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts index c82882623b177..60c095adeadba 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('showQueryBarMenu'); await testSubjects.missingOrFail('addFilter'); - await testSubjects.existOrFail('dscViewModeToggle'); + await testSubjects.missingOrFail('dscViewModeToggle'); await testSubjects.existOrFail('dscViewModeDocumentButton'); // when Lens suggests a table, we render an ESQL based histogram await testSubjects.existOrFail('unifiedHistogramChart'); From b3c356e2041c03552e3d27b65767cab07bac3997 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 5 Nov 2024 09:57:20 -0600 Subject: [PATCH 20/29] Revert field list changes --- .../components/field_stats/field_stats.tsx | 19 ----------------- .../load_field_stats_text_based.ts | 10 --------- .../src/utils/get_warning_message.ts | 21 ------------------- .../discover/group6/_sidebar_field_stats.ts | 17 --------------- .../apps/discover/group6/_view_mode_toggle.ts | 15 ++----------- .../common/discover/esql/_esql_view.ts | 2 +- 6 files changed, 3 insertions(+), 81 deletions(-) delete mode 100644 packages/kbn-unified-field-list/src/utils/get_warning_message.ts diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 5b71001278770..8eada232cdeaf 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -53,7 +53,6 @@ import { import { FieldSummaryMessage } from './field_summary_message'; import { FieldNumberSummary, isNumberSummaryValid } from './field_number_summary'; import { ErrorBoundary } from '../error_boundary'; -import { getReasonIfFieldDataUnavailableForQuery } from '../../utils/get_warning_message'; export interface FieldStatsState { isLoading: boolean; @@ -337,24 +336,6 @@ const FieldStatsComponent: React.FC = ({ : messageNoAnalysis; } - const unsupportedReasonForQuery = isTextBased - ? getReasonIfFieldDataUnavailableForQuery(query) - : undefined; - if (unsupportedReasonForQuery) { - const messageUnsupportedReason = ( - - ); - - return overrideMissingContent - ? overrideMissingContent({ - reason: 'unsupported', - element: messageUnsupportedReason, - }) - : messageUnsupportedReason; - } if (canProvideNumberSummaryForField(field, isTextBased) && isNumberSummaryValid(numberSummary)) { title = ( diff --git a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts b/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts index 7a0b1dbc544d4..5f77f15906896 100644 --- a/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts +++ b/packages/kbn-unified-field-list/src/services/field_stats_text_based/load_field_stats_text_based.ts @@ -18,7 +18,6 @@ import { fetchAndCalculateFieldStats, } from './field_stats_utils_text_based'; import { ESQL_SAFE_LIMIT } from '../../constants'; -import { getReasonIfFieldDataUnavailableForQuery } from '../../utils/get_warning_message'; interface FetchFieldStatsParamsTextBased { services: { @@ -55,15 +54,6 @@ export const loadFieldStatsTextBased: LoadFieldStatsTextBasedHandler = async ({ baseQuery, abortController, }) => { - /** - * If the ES|QL query is unsupported, we can exit early - */ - const unsupportedReasonForQuery = getReasonIfFieldDataUnavailableForQuery(baseQuery); - - if (unsupportedReasonForQuery) { - return {}; - } - const { data } = services; try { diff --git a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts b/packages/kbn-unified-field-list/src/utils/get_warning_message.ts deleted file mode 100644 index f63db4b7d614c..0000000000000 --- a/packages/kbn-unified-field-list/src/utils/get_warning_message.ts +++ /dev/null @@ -1,21 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ -import type { AggregateQuery, Query } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; -import { queryCannotBeSampled } from '@kbn/esql-utils'; - -export const getReasonIfFieldDataUnavailableForQuery = ( - query?: AggregateQuery | Query | { [key: string]: any } -): string | undefined => { - if (queryCannotBeSampled(query)) { - return i18n.translate('unifiedFieldList.fieldStats.notAvailableForMatchESQLQueryDescription', { - defaultMessage: `Field data is not available for ES|QL queries with 'MATCH' or 'QSTR' functions.`, - }); - } -}; diff --git a/test/functional/apps/discover/group6/_sidebar_field_stats.ts b/test/functional/apps/discover/group6/_sidebar_field_stats.ts index 947b355fcfc76..325adb313ed6c 100644 --- a/test/functional/apps/discover/group6/_sidebar_field_stats.ts +++ b/test/functional/apps/discover/group6/_sidebar_field_stats.ts @@ -338,23 +338,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(editorValue).to.eql(`row enabled = true\n| WHERE \`enabled\`!=true`); await unifiedFieldList.closeFieldPopover(); }); - - it('should show a reason if field data is unavailable for the query', async () => { - const testQuery = `from logstash-* METADATA _index, _id | sort @timestamp desc | where match(extension.raw,"css")`; - - await monacoEditor.setCodeEditorValue(testQuery); - await testSubjects.click('querySubmitButton'); - await header.waitUntilLoadingHasFinished(); - await unifiedFieldList.waitUntilSidebarHasLoaded(); - - await unifiedFieldList.clickFieldListItem('@message'); - await testSubjects.existOrFail('unifiedFieldList.fieldStats.unsupportedReason'); - expect( - await testSubjects.getVisibleText('unifiedFieldList.fieldStats.unsupportedReason') - ).to.contain( - `Field data is not available for ES|QL queries with 'MATCH' or 'QSTR' functions.` - ); - }); }); }); } diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 4b6eb8a652c7d..48bbc2fb49115 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -95,25 +95,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not show', async function () { await testSubjects.missingOrFail('dscViewModePatternAnalysisButton'); - await retry.try(async () => { - const documentTab = await testSubjects.find('dscViewModeDocumentButton'); - expect(await documentTab.getAttribute('aria-selected')).to.be('true'); - }); }); }); - it('should show Field Statistics tab', async () => { - await testSubjects.click('dscViewModeFieldStatsButton'); - - await retry.try(async () => { - const fieldStatsTab = await testSubjects.find('dscViewModeFieldStatsButton'); - expect(await fieldStatsTab.getAttribute('aria-selected')).to.be('true'); - }); - + it('should not show Field Statistics tab', async () => { await testSubjects.existOrFail('dscViewModeToggle'); }); - it('should still show view mode toggle for ES|QL searches', async () => { + it('should not show view mode toggle for ES|QL searches', async () => { await testSubjects.click('dscViewModeDocumentButton'); await retry.try(async () => { diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts index 60c095adeadba..5b8efbfb7b69c 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts @@ -89,7 +89,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('showQueryBarMenu'); await testSubjects.missingOrFail('addFilter'); await testSubjects.missingOrFail('dscViewModeToggle'); - await testSubjects.existOrFail('dscViewModeDocumentButton'); + await testSubjects.missingOrFail('dscViewModeDocumentButton'); // when Lens suggests a table, we render an ESQL based histogram await testSubjects.existOrFail('unifiedHistogramChart'); await testSubjects.existOrFail('discoverQueryHits'); From bafff159b8e8cb386a81af6e46d82f770f3a1ad3 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Tue, 5 Nov 2024 10:33:22 -0600 Subject: [PATCH 21/29] Add view mode --- .../view_mode_toggle/view_mode_toggle.tsx | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) 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 8210516b85fc5..38a831b7fb53d 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 @@ -8,7 +8,7 @@ */ import React, { useMemo, useEffect, useState, type ReactElement, useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { isLegacyTableEnabled, SHOW_FIELD_STATISTICS } from '@kbn/discover-utils'; @@ -46,12 +46,6 @@ export const DocumentViewModeToggle = ({ () => isLegacyTableEnabled({ uiSettings, isEsqlMode }), [uiSettings, isEsqlMode] ); - const state = stateContainer.appState.getState(); - const fieldStatsWarningMsgForQuery = useMemo(() => { - return isEsqlMode - ? dataVisualizerService?.getReasonIfFieldStatsUnavailableForQuery(state.query) - : undefined; - }, [state.query, dataVisualizerService, isEsqlMode]); const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( @@ -171,17 +165,15 @@ export const DocumentViewModeToggle = ({ {showFieldStatisticsTab ? ( setDiscoverViewMode(VIEW_MODE.AGGREGATED_LEVEL)} data-test-subj="dscViewModeFieldStatsButton" > - - - + ) : null} From a8cd5be306bf3212f439566ba5f9f4aa77776599 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 6 Nov 2024 10:38:10 -0600 Subject: [PATCH 22/29] Update tests --- .../components/view_mode_toggle/view_mode_toggle.test.tsx | 6 ++++++ 1 file changed, 6 insertions(+) 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 eff7f4e4da042..9cee5a69960a7 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 @@ -110,6 +110,12 @@ describe('Document view mode toggle component', () => { it('should not show document and field stats view if ES|QL', async () => { const component = await mountComponent({ isEsqlMode: true }); expect(findTestSubject(component, 'dscViewModeToggle').exists()).toBe(false); + expect(findTestSubject(component, 'discoverQueryTotalHits').exists()).toBe(true); + + expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(false); + expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(false); + expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(false); + expect(findTestSubject(component, 'dscViewModeDocumentButton').text()).toBe('Results (10)'); }); it('should set the view mode to VIEW_MODE.DOCUMENT_LEVEL when dscViewModeDocumentButton is clicked', async () => { From 6b7ee45158448fd4151c70ce83e9fe791bf0907f Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 6 Nov 2024 10:51:23 -0600 Subject: [PATCH 23/29] Consolidate messaging style --- .../field_stats_table/field_stats_tab.tsx | 31 ++------------ .../field_stats_unavailable.tsx | 40 +++++++++++++++++++ ...embeddable_field_stats_table_component.tsx | 26 ++---------- .../grid_embeddable/embeddable_error_msg.tsx | 32 ++++----------- .../esql/use_data_visualizer_esql_data.tsx | 5 +-- 5 files changed, 55 insertions(+), 79 deletions(-) create mode 100644 src/plugins/discover/public/application/main/components/field_stats_table/field_stats_unavailable.tsx diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx index a8f0552777a50..7026e8d7140ef 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx @@ -9,14 +9,12 @@ import React from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiIcon, useEuiTheme } from '@elastic/eui'; -import { EuiEmptyPrompt } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FieldStatisticsTable, type FieldStatisticsTableProps } from './field_stats_table'; import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; +import { FieldStatsUnavailableMessage } from './field_stats_unavailable'; export const FieldStatisticsTab: React.FC> = React.memo((props) => { @@ -29,30 +27,7 @@ export const FieldStatisticsTab: React.FC} - color="plain" - paddingSize="m" - css={css` - margin: ${euiTheme.size.xl} auto; - `} - title={ -

- -

- } - titleSize="xs" - hasBorder - /> - ); - } - + if (isEsql) return ; return ( { + const { euiTheme } = useEuiTheme(); + + return ( + + + + ); +}; diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx index 76b99e33002a4..dee391018bb42 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx @@ -14,12 +14,11 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { FetchContext, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; -import { EuiEmptyPrompt, EuiIcon, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { useEuiTheme } from '@elastic/eui'; import { FieldStatisticsTable } from '../../application/main/components/field_stats_table'; import { isEsqlMode } from '../initialize_fetch'; import type { SearchEmbeddableApi, SearchEmbeddableStateManager } from '../types'; +import { FieldStatsUnavailableMessage } from '../../application/main/components/field_stats_table/field_stats_unavailable'; interface SavedSearchEmbeddableComponentProps { api: SearchEmbeddableApi & { @@ -43,26 +42,7 @@ export function SearchEmbeddablFieldStatsTableComponent({ const { euiTheme } = useEuiTheme(); const isEsql = useMemo(() => isEsqlMode(savedSearch), [savedSearch]); if (isEsql) { - return ( - } - color="plain" - paddingSize="m" - css={css` - margin: ${euiTheme.size.xl} auto; - `} - title={ -

- -

- } - titleSize="xs" - hasBorder - /> - ); + return ; } return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 92a894db70843..1406a55a6301f 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiEmptyPrompt, EuiIcon, useEuiTheme, EuiFlexItem } from '@elastic/eui'; +import { useEuiTheme, EuiFlexItem } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -25,31 +25,15 @@ export const FieldStatsUnavailableMessage = ({ const { euiTheme } = useEuiTheme(); if (!content) return null; - if (id === 'dashboard_embeddable') { - return ( - - - - ); - } return ( - } - color="plain" - paddingSize="m" + {title}} - titleSize="xs" - hasBorder - body={<>{content}} - /> + > + + ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx index ed30df57fca2c..c878e61c48784 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx @@ -44,7 +44,6 @@ import type { } from '../../embeddables/grid_embeddable/types'; import { getDefaultPageState } from '../../constants/index_data_visualizer_viewer'; import { DEFAULT_ESQL_LIMIT } from '../../constants/esql_constants'; -import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; type AnyQuery = Query | AggregateQuery; @@ -161,9 +160,7 @@ export const useESQLDataVisualizerData = ( const tf = timefilter; - if (!buckets || !tf || (isESQLQuery(query) && query.esql === '')) return; - const unsupportedReason = getReasonIfFieldStatsUnavailableForQuery(query); - if (unsupportedReason) return; + if (!buckets || !tf || isESQLQuery(query)) return; const activeBounds = tf.getActiveBounds(); let earliest: number | undefined; From dc99c68dd97df4e6ccae2f559d012c89ed555103 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 6 Nov 2024 11:33:01 -0600 Subject: [PATCH 24/29] Consolidate messaging --- src/plugins/discover/public/__mocks__/services.ts | 1 - .../field_stats_table/field_stats_tab.tsx | 3 +-- ...rch_embeddable_field_stats_table_component.tsx | 6 ++++-- .../index_data_visualizer_esql.tsx | 1 - .../grid_embeddable/embeddable_error_msg.tsx | 15 ++++++++++----- .../grid_embeddable/field_stats_wrapper.tsx | 2 +- x-pack/plugins/data_visualizer/public/plugin.ts | 8 ++++++-- 7 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 50bbf34ff7a95..f00d105444630 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -163,7 +163,6 @@ export function createDiscoverServicesMock(): DiscoverServices { data: dataPlugin, dataVisualizer: { FieldStatisticsTable: jest.fn(() => createElement('div')), - getReasonIfFieldStatsUnavailableForQuery: jest.fn().mockReturnValue(undefined), }, aiops: { getPatternAnalysisAvailable: jest.fn().mockResolvedValue(jest.fn().mockResolvedValue(true)), diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx index 7026e8d7140ef..fcc58d1b9f3c5 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx @@ -14,7 +14,6 @@ import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_fie import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FieldStatisticsTable, type FieldStatisticsTableProps } from './field_stats_table'; import { useIsEsqlMode } from '../../hooks/use_is_esql_mode'; -import { FieldStatsUnavailableMessage } from './field_stats_unavailable'; export const FieldStatisticsTab: React.FC> = React.memo((props) => { @@ -27,7 +26,7 @@ export const FieldStatisticsTab: React.FC; + return ( isEsqlMode(savedSearch), [savedSearch]); + const services = useDiscoverServices(); + if (isEsql) { - return ; + return ; } return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index c6190c87bcae5..1180d20a9c0fd 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -49,7 +49,6 @@ import type { import type { ESQLQuery } from '../../search_strategy/requests/esql_utils'; import { isESQLQuery } from '../../search_strategy/requests/esql_utils'; import { FieldStatsComponentType } from '../../constants/field_stats_component_type'; -import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; export interface IndexDataVisualizerESQLProps { getAdditionalLinks?: GetAdditionalLinks; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 1406a55a6301f..2f24b27b4b1c7 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -9,7 +9,6 @@ import { useEuiTheme, EuiFlexItem } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; -import type { FieldStatisticTableEmbeddableProps } from './types'; const FIELD_STATS_UNAVAILABLE_TITLE = i18n.translate( 'xpack.dataVisualizer.fieldStats.unavailableTitle', { @@ -17,13 +16,15 @@ const FIELD_STATS_UNAVAILABLE_TITLE = i18n.translate( } ); -export const FieldStatsUnavailableMessage = ({ +const FieldStatsUnavailableMessage = ({ id, - content, title = FIELD_STATS_UNAVAILABLE_TITLE, -}: Pick & { content: string; title?: string }) => { +}: { + id?: string; + content: string; + title?: string; +}) => { const { euiTheme } = useEuiTheme(); - if (!content) return null; return ( ); }; + +// Default export for lazy loading +// eslint-disable-next-line import/no-default-export +export default FieldStatsUnavailableMessage; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index 715182c67aa9e..7d1dd07d28a6d 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -23,7 +23,7 @@ import type { FieldStatisticTableEmbeddableProps, ESQLDataVisualizerGridEmbeddableState, } from './types'; -import { FieldStatsUnavailableMessage } from './embeddable_error_msg'; +import FieldStatsUnavailableMessage from './embeddable_error_msg'; const EmbeddableESQLFieldStatsTableWrapper = dynamic( () => import('./embeddable_esql_field_stats_table') diff --git a/x-pack/plugins/data_visualizer/public/plugin.ts b/x-pack/plugins/data_visualizer/public/plugin.ts index 3bcd83efed102..282ec23e3c303 100644 --- a/x-pack/plugins/data_visualizer/public/plugin.ts +++ b/x-pack/plugins/data_visualizer/public/plugin.ts @@ -21,7 +21,6 @@ import type { DataVisualizerStartDependencies, } from './application/common/types/data_visualizer_plugin'; import { registerEmbeddables } from './application/index_data_visualizer/embeddables/field_stats'; -import { getReasonIfFieldStatsUnavailableForQuery } from './application/index_data_visualizer/utils/get_reason_fieldstats_unavailable_for_esql_query'; export type DataVisualizerPluginSetup = ReturnType; export type DataVisualizerPluginStart = ReturnType; @@ -78,7 +77,12 @@ export class DataVisualizerPlugin getIndexDataVisualizerComponent, getDataDriftComponent, getMaxBytesFormatted, - getReasonIfFieldStatsUnavailableForQuery, + FieldStatsUnavailableMessage: dynamic( + async () => + import( + './application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg' + ) + ), FieldStatisticsTable: dynamic( async () => import( From 9d4f0f1ed6ae7223da5d0efea632cc7f75c93be1 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 6 Nov 2024 11:47:33 -0600 Subject: [PATCH 25/29] Update types --- .../field_stats_table/field_stats_tab.tsx | 7 ++-- .../field_stats_unavailable.tsx | 40 ------------------- ...embeddable_field_stats_table_component.tsx | 6 +-- .../index_data_visualizer_esql.tsx | 1 + .../grid_embeddable/embeddable_error_msg.tsx | 4 +- 5 files changed, 8 insertions(+), 50 deletions(-) delete mode 100644 src/plugins/discover/public/application/main/components/field_stats_table/field_stats_unavailable.tsx diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx index fcc58d1b9f3c5..5a106b50360bb 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_tab.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { useQuerySubscriber } from '@kbn/unified-field-list/src/hooks/use_query_subscriber'; -import { useEuiTheme } from '@elastic/eui'; import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FieldStatisticsTable, type FieldStatisticsTableProps } from './field_stats_table'; @@ -23,9 +22,11 @@ export const FieldStatisticsTab: React.FC; + } return ( { - const { euiTheme } = useEuiTheme(); - - return ( - - - - ); -}; diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx index ad4df78003def..cb1cd2f9eebc3 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_field_stats_table_component.tsx @@ -13,8 +13,6 @@ import { BehaviorSubject } from 'rxjs'; import type { DataView } from '@kbn/data-views-plugin/common'; import { FetchContext, useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; - -import { useEuiTheme } from '@elastic/eui'; import { FieldStatisticsTable } from '../../application/main/components/field_stats_table'; import { isEsqlMode } from '../initialize_fetch'; import type { SearchEmbeddableApi, SearchEmbeddableStateManager } from '../types'; @@ -39,11 +37,11 @@ export function SearchEmbeddablFieldStatsTableComponent({ api.fetchContext$, api.savedSearch$ ); - const { euiTheme } = useEuiTheme(); const isEsql = useMemo(() => isEsqlMode(savedSearch), [savedSearch]); const services = useDiscoverServices(); - if (isEsql) { + // Quit early if we know it's in ES|QL mode + if (isEsql && services.dataVisualizer?.FieldStatsUnavailableMessage) { return ; } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx index 1180d20a9c0fd..c6190c87bcae5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/index_data_visualizer_view/index_data_visualizer_esql.tsx @@ -49,6 +49,7 @@ import type { import type { ESQLQuery } from '../../search_strategy/requests/esql_utils'; import { isESQLQuery } from '../../search_strategy/requests/esql_utils'; import { FieldStatsComponentType } from '../../constants/field_stats_component_type'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; export interface IndexDataVisualizerESQLProps { getAdditionalLinks?: GetAdditionalLinks; diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 2f24b27b4b1c7..623a8e171f3db 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { useEuiTheme, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexItem } from '@elastic/eui'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; @@ -24,8 +24,6 @@ const FieldStatsUnavailableMessage = ({ content: string; title?: string; }) => { - const { euiTheme } = useEuiTheme(); - return ( Date: Wed, 6 Nov 2024 11:53:43 -0600 Subject: [PATCH 26/29] Update logic for toggle when opening saved search --- .../components/view_mode_toggle/view_mode_toggle.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 38a831b7fb53d..43960f98ea2b8 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 @@ -50,8 +50,12 @@ export const DocumentViewModeToggle = ({ const [showPatternAnalysisTab, setShowPatternAnalysisTab] = useState(null); const showFieldStatisticsTab = useMemo( () => - !isEsqlMode && uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined, - [dataVisualizerService, uiSettings, isEsqlMode] + // If user opens saved search with field stats in ES|QL, + // we show the toggle with the mode disabled so user can switch to document view + // instead of auto-directing + (viewMode === VIEW_MODE.AGGREGATED_LEVEL && isEsqlMode) || + (!isEsqlMode && uiSettings.get(SHOW_FIELD_STATISTICS) && dataVisualizerService !== undefined), + [dataVisualizerService, uiSettings, isEsqlMode, viewMode] ); const isMounted = useMountedState(); From 5ccbdf7c30e88fabf8ece949f4f3448b4bd8f150 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Wed, 6 Nov 2024 12:06:32 -0600 Subject: [PATCH 27/29] More test updates --- .../functional/apps/discover/group6/_field_stats_table.ts | 3 +-- test/functional/apps/discover/group6/_view_mode_toggle.ts | 1 + .../embeddables/grid_embeddable/embeddable_error_msg.tsx | 1 - .../embeddables/grid_embeddable/field_stats_wrapper.tsx | 8 +------- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/test/functional/apps/discover/group6/_field_stats_table.ts b/test/functional/apps/discover/group6/_field_stats_table.ts index a8463eb74c66a..4d295ef5ca95c 100644 --- a/test/functional/apps/discover/group6/_field_stats_table.ts +++ b/test/functional/apps/discover/group6/_field_stats_table.ts @@ -66,10 +66,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not show Field Statistics data in ES|QL mode', async () => { await discover.selectTextBaseLang(); + await header.waitUntilLoadingHasFinished(); await discover.waitUntilSearchingHasFinished(); - await testSubjects.missingOrFail('dscViewModeFieldStatsButton'); - await header.waitUntilLoadingHasFinished(); }); }); }); diff --git a/test/functional/apps/discover/group6/_view_mode_toggle.ts b/test/functional/apps/discover/group6/_view_mode_toggle.ts index 48bbc2fb49115..2591716c87d00 100644 --- a/test/functional/apps/discover/group6/_view_mode_toggle.ts +++ b/test/functional/apps/discover/group6/_view_mode_toggle.ts @@ -115,6 +115,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await discover.selectTextBaseLang(); await testSubjects.missingOrFail('dscViewModeToggle'); + await testSubjects.existOrFail('discoverQueryTotalHits'); if (!useLegacyTable) { await testSubjects.existOrFail('unifiedDataTableToolbar'); diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx index 623a8e171f3db..9e49570ab6bfa 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/embeddable_error_msg.tsx @@ -21,7 +21,6 @@ const FieldStatsUnavailableMessage = ({ title = FIELD_STATS_UNAVAILABLE_TITLE, }: { id?: string; - content: string; title?: string; }) => { return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx index 7d1dd07d28a6d..d991f02fb3958 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/embeddables/grid_embeddable/field_stats_wrapper.tsx @@ -17,7 +17,6 @@ import type { DatePickerDependencies } from '@kbn/ml-date-picker'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { pick } from 'lodash'; import { isOfAggregateQueryType } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; import { getCoreStart, getPluginsStart } from '../../../../kibana_services'; import type { FieldStatisticTableEmbeddableProps, @@ -46,12 +45,7 @@ const FieldStatisticsWrapperContent = (props: FieldStatisticTableEmbeddableProps if (isESQLFieldStatisticTableEmbeddableState(props)) { const isEsql = props.esqlQuery && isOfAggregateQueryType(props.esqlQuery); return isEsql ? ( - + ) : ( Date: Mon, 11 Nov 2024 09:33:14 -0600 Subject: [PATCH 28/29] Update jest test --- .../components/view_mode_toggle/view_mode_toggle.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9cee5a69960a7..3899e405420ef 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 @@ -115,7 +115,7 @@ describe('Document view mode toggle component', () => { expect(findTestSubject(component, 'dscViewModeDocumentButton').exists()).toBe(false); expect(findTestSubject(component, 'dscViewModePatternAnalysisButton').exists()).toBe(false); expect(findTestSubject(component, 'dscViewModeFieldStatsButton').exists()).toBe(false); - expect(findTestSubject(component, 'dscViewModeDocumentButton').text()).toBe('Results (10)'); + expect(findTestSubject(component, 'discoverQueryHits').text()).toBe('10'); }); it('should set the view mode to VIEW_MODE.DOCUMENT_LEVEL when dscViewModeDocumentButton is clicked', async () => { From ed826d07177039ed84a9d70db22ab6ef69e7c251 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen Date: Mon, 11 Nov 2024 09:38:33 -0600 Subject: [PATCH 29/29] Fix data visualizer page --- .../hooks/esql/use_data_visualizer_esql_data.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx index c878e61c48784..2323b231d67f7 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/esql/use_data_visualizer_esql_data.tsx @@ -44,6 +44,7 @@ import type { } from '../../embeddables/grid_embeddable/types'; import { getDefaultPageState } from '../../constants/index_data_visualizer_viewer'; import { DEFAULT_ESQL_LIMIT } from '../../constants/esql_constants'; +import { getReasonIfFieldStatsUnavailableForQuery } from '../../utils/get_reason_fieldstats_unavailable_for_esql_query'; type AnyQuery = Query | AggregateQuery; @@ -160,7 +161,15 @@ export const useESQLDataVisualizerData = ( const tf = timefilter; - if (!buckets || !tf || isESQLQuery(query)) return; + if (!buckets || !tf || query.esql === '') return; + + // Safeguard to not ever run query if not supported + if (isESQLQuery(query)) { + const unsupportedReasonForQuery = getReasonIfFieldStatsUnavailableForQuery(query); + if (unsupportedReasonForQuery) { + return; + } + } const activeBounds = tf.getActiveBounds(); let earliest: number | undefined;