From 568b8d399245a9d2ae01a169fc9112aa6fa6356b Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Fri, 1 Nov 2019 12:12:01 -0400 Subject: [PATCH] [ML] DF Analytics Regression results: Ensure error handling and table sort works correctly (#49929) * add error handling for regression jobConfig fetch * fix sorting change causing blank table by removing table render timeout * Add label to table for number of docs obtained * parameterize searchSize in documents fetched text --- .../public/data_frame_analytics/_index.scss | 1 - .../data_frame_analytics/common/fields.ts | 18 +----- .../regression_exploration/_index.scss | 1 - .../_regression_exploration.scss | 3 - .../regression_exploration/evaluate_panel.tsx | 3 +- .../regression_exploration.tsx | 60 ++++++++++++++++--- .../regression_exploration/results_table.tsx | 45 +++++--------- 7 files changed, 71 insertions(+), 60 deletions(-) delete mode 100644 x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_index.scss delete mode 100644 x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_regression_exploration.scss diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/_index.scss b/x-pack/legacy/plugins/ml/public/data_frame_analytics/_index.scss index c231c405b5369..4c0ecd8f9ce44 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/_index.scss +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/_index.scss @@ -1,5 +1,4 @@ @import 'pages/analytics_exploration/components/exploration/index'; -@import 'pages/analytics_exploration/components/regression_exploration/index'; @import 'pages/analytics_management/components/analytics_list/index'; @import 'pages/analytics_management/components/create_analytics_form/index'; @import 'pages/analytics_management/components/create_analytics_flyout/index'; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts index b25f4b6ad8b92..5621d77f66469 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/fields.ts @@ -211,14 +211,7 @@ export const getDefaultRegressionFields = ( return false; } - let value = false; - docs.forEach(row => { - const source = row._source; - if (source[k] !== null) { - value = true; - } - }); - return value; + return docs.some(row => row._source[k] !== null); }) .sort((a, b) => sortRegressionResultsFields(a, b, jobConfig)) .slice(0, DEFAULT_REGRESSION_COLUMNS); @@ -239,14 +232,7 @@ export const getDefaultSelectableFields = (docs: EsDoc[], resultsField: string): return false; } - let value = false; - docs.forEach(row => { - const source = row._source; - if (source[k] !== null) { - value = true; - } - }); - return value; + return docs.some(row => row._source[k] !== null); }) .slice(0, MAX_COLUMNS); }; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_index.scss b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_index.scss deleted file mode 100644 index bb948785d3efa..0000000000000 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'regression_exploration'; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_regression_exploration.scss b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_regression_exploration.scss deleted file mode 100644 index 2faa04be0ab65..0000000000000 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/_regression_exploration.scss +++ /dev/null @@ -1,3 +0,0 @@ -.mlRegressionExploration__evaluateLoadingSpinner { - display: inline-block; -} diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx index b9f9c07bc3d5e..20ab6678da820 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx @@ -7,7 +7,6 @@ import React, { FC, Fragment, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer, EuiStat, EuiTitle } from '@elastic/eui'; -import { idx } from '@kbn/elastic-idx'; import { ErrorCallout } from './error_callout'; import { getValuesFromResponse, @@ -45,7 +44,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus }) => { const [isLoadingTraining, setIsLoadingTraining] = useState(false); const [isLoadingGeneralization, setIsLoadingGeneralization] = useState(false); - const index = idx(jobConfig, _ => _.dest.index) as string; + const index = jobConfig.dest.index; const dependentVariable = getDependentVar(jobConfig.analysis); const predictionFieldName = getPredictionFieldName(jobConfig.analysis); // default is 'ml' diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx index 1f7564b2fe213..7beea07f9502d 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx @@ -5,7 +5,8 @@ */ import React, { FC, Fragment, useState, useEffect } from 'react'; -import { EuiSpacer, EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; +import { EuiCallOut, EuiLoadingSpinner, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ml } from '../../../../../services/ml_api_service'; import { DataFrameAnalyticsConfig } from '../../../../common'; import { EvaluatePanel } from './evaluate_panel'; @@ -18,11 +19,22 @@ interface GetDataFrameAnalyticsResponse { } const LoadingPanel: FC = () => ( - - + + ); +export const ExplorationTitle: React.SFC<{ jobId: string }> = ({ jobId }) => ( + + + {i18n.translate('xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle', { + defaultMessage: 'Regression job ID {jobId}', + values: { jobId }, + })} + + +); + interface Props { jobId: string; jobStatus: DATA_FRAME_TASK_STATE; @@ -31,10 +43,11 @@ interface Props { export const RegressionExploration: FC = ({ jobId, jobStatus }) => { const [jobConfig, setJobConfig] = useState(undefined); const [isLoadingJobConfig, setIsLoadingJobConfig] = useState(false); + const [jobConfigErrorMessage, setJobConfigErrorMessage] = useState(undefined); - useEffect(() => { - (async function() { - setIsLoadingJobConfig(true); + const loadJobConfig = async () => { + setIsLoadingJobConfig(true); + try { const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( jobId ); @@ -45,9 +58,42 @@ export const RegressionExploration: FC = ({ jobId, jobStatus }) => { setJobConfig(analyticsConfigs.data_frame_analytics[0]); setIsLoadingJobConfig(false); } - })(); + } catch (e) { + if (e.message !== undefined) { + setJobConfigErrorMessage(e.message); + } else { + setJobConfigErrorMessage(JSON.stringify(e)); + } + setIsLoadingJobConfig(false); + } + }; + + useEffect(() => { + loadJobConfig(); }, []); + if (jobConfigErrorMessage !== undefined) { + return ( + + + + +

{jobConfigErrorMessage}

+
+
+ ); + } + return ( {isLoadingJobConfig === true && jobConfig === undefined && } diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx index fdd6782bba37b..5ba3b8ed45939 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx @@ -15,13 +15,13 @@ import { EuiCheckbox, EuiFlexGroup, EuiFlexItem, + EuiFormRow, EuiPanel, EuiPopover, EuiPopoverTitle, EuiProgress, EuiSpacer, EuiText, - EuiTitle, EuiToolTip, Query, } from '@elastic/eui'; @@ -49,25 +49,16 @@ import { MAX_COLUMNS, getPredictedFieldName, INDEX_STATUS, + SEARCH_SIZE, } from '../../../../common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; import { useExploreData, defaultSearchQuery } from './use_explore_data'; +import { ExplorationTitle } from './regression_exploration'; const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; -const ExplorationTitle: React.SFC<{ jobId: string }> = ({ jobId }) => ( - - - {i18n.translate('xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle', { - defaultMessage: 'Regression job ID {jobId}', - values: { jobId }, - })} - - -); - interface Props { jobConfig: DataFrameAnalyticsConfig; jobStatus: DATA_FRAME_TASK_STATE; @@ -76,25 +67,12 @@ interface Props { export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(25); - const [clearTable, setClearTable] = useState(false); const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); const [searchError, setSearchError] = useState(undefined); const [searchString, setSearchString] = useState(undefined); - // EuiInMemoryTable has an issue with dynamic sortable columns - // and will trigger a full page Kibana error in such a case. - // The following is a workaround until this is solved upstream: - // - If the sortable/columns config changes, - // the table will be unmounted/not rendered. - // This is what setClearTable(true) in toggleColumn() does. - // - After that on next render it gets re-enabled. To make sure React - // doesn't consolidate the state updates, setTimeout is used. - if (clearTable) { - setTimeout(() => setClearTable(false), 0); - } - function toggleColumnsPopover() { setColumnsPopoverVisible(!isColumnsPopoverVisible); } @@ -105,7 +83,6 @@ export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => function toggleColumn(column: EsFieldName) { if (tableItems.length > 0 && jobConfig !== undefined) { - setClearTable(true); // spread to a new array otherwise the component wouldn't re-render setSelectedFields([...toggleSelectedField(selectedFields, column)]); } @@ -240,7 +217,6 @@ export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => const field = predictedFieldSelected ? predictedFieldName : selectedFields[0]; const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; loadExploreData({ field, direction, searchQuery }); - return; } }, [JSON.stringify(searchQuery)]); @@ -258,7 +234,6 @@ export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => const field = predictedFieldSelected ? predictedFieldName : selectedFields[0]; const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; loadExploreData({ field, direction, searchQuery }); - return; } }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); @@ -282,7 +257,6 @@ export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => setPageSize(size); if (sort.field !== sortField || sort.direction !== sortDirection) { - setClearTable(true); loadExploreData({ ...sort, searchQuery }); } }; @@ -458,8 +432,19 @@ export const ResultsTable: FC = React.memo(({ jobConfig, jobStatus }) => {status !== INDEX_STATUS.LOADING && ( )} - {clearTable === false && (columns.length > 0 || searchQuery !== defaultSearchQuery) && ( + {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( + + +