From 083803cd002906cf12edc4fce13260f48beee1c8 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 20 Nov 2019 15:52:18 -0500 Subject: [PATCH 1/4] add search bar to outlierDetection results table --- .../data_frame_analytics/common/analytics.ts | 9 ++ .../data_frame_analytics/common/index.ts | 2 + .../components/exploration/exploration.tsx | 100 +++++++++++++++--- .../exploration/use_explore_data.ts | 33 ++++-- .../regression_exploration/results_table.tsx | 3 +- .../use_explore_data.ts | 12 +-- 6 files changed, 120 insertions(+), 39 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts index b1eedc1378d43..989751e450997 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts @@ -33,6 +33,15 @@ interface RegressionAnalysis { export const SEARCH_SIZE = 1000; +export const defaultSearchQuery = { + match_all: {}, +}; + +export interface SearchQuery { + query: SavedSearchQuery; + sort?: any; +} + export enum INDEX_STATUS { UNUSED, LOADING, diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts index 112f828f9897e..02a1c30259cce 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/index.ts @@ -24,6 +24,8 @@ export { getPredictedFieldName, INDEX_STATUS, SEARCH_SIZE, + defaultSearchQuery, + SearchQuery, } from './analytics'; export { diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx index 11bb62dec1624..95deefaf8fc1d 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, useEffect, useState } from 'react'; +import React, { FC, Fragment, useEffect, useState } from 'react'; import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; @@ -18,13 +18,16 @@ import { EuiCheckbox, EuiFlexGroup, EuiFlexItem, + EuiFormRow, EuiPanel, EuiPopover, EuiPopoverTitle, EuiProgress, + EuiSpacer, EuiText, EuiTitle, EuiToolTip, + Query, } from '@elastic/eui'; import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json'; @@ -51,12 +54,18 @@ import { EsDoc, MAX_COLUMNS, INDEX_STATUS, + SEARCH_SIZE, + defaultSearchQuery, } from '../../../../common'; import { getOutlierScoreFieldName } from './common'; import { useExploreData } from './use_explore_data'; -import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; +import { + DATA_FRAME_TASK_STATE, + Query as QueryType, +} from '../../../analytics_management/components/analytics_list/common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; +import { SavedSearchQuery } from '../../../../../contexts/kibana'; const customColorScaleFactory = (n: number) => (t: number) => { if (t < 1 / n) { @@ -99,6 +108,10 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(25); + const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); + const [searchError, setSearchError] = useState(undefined); + const [searchString, setSearchString] = useState(undefined); + useEffect(() => { (async function() { const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( @@ -309,6 +322,17 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { ); } + useEffect(() => { + if (jobConfig !== undefined) { + const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); + const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); + + const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; + const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; + loadExploreData({ field, direction, searchQuery }); + } + }, [JSON.stringify(searchQuery)]); + useEffect(() => { // by default set the sorting to descending on the `outlier_score` field. // if that's not available sort ascending on the first column. @@ -319,7 +343,7 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field, direction }); + loadExploreData({ field, direction, searchQuery }); return; } }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); @@ -345,7 +369,7 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (sort.field !== sortField || sort.direction !== sortDirection) { setClearTable(true); - loadExploreData(sort); + loadExploreData({ ...sort, searchQuery }); } }; } @@ -358,6 +382,32 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { hidePerPageOptions: false, }; + const onQueryChange = ({ query, error }: { query: QueryType; error: any }) => { + if (error) { + setSearchError(error.message); + } else { + try { + const esQueryDsl = Query.toESQuery(query); + setSearchQuery(esQueryDsl); + setSearchString(query.text); + setSearchError(undefined); + } catch (e) { + setSearchError(e.toString()); + } + } + }; + + const search = { + onChange: onQueryChange, + defaultQuery: searchString, + box: { + incremental: false, + placeholder: i18n.translate('xpack.ml.dataframe.analytics.exploration.searchBoxPlaceholder', { + defaultMessage: 'E.g. avg>0.5', + }), + }, + }; + if (jobConfig === undefined) { return null; } @@ -484,19 +534,35 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { )} {clearTable === false && columns.length > 0 && sortField !== '' && ( - + + + + + + + )} ); diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts index 2a07bc1251a31..a0728e0bae446 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts @@ -20,16 +20,21 @@ import { EsFieldName, INDEX_STATUS, SEARCH_SIZE, + defaultSearchQuery, + SearchQuery, } from '../../../../common'; import { getOutlierScoreFieldName } from './common'; +import { SavedSearchQuery } from '../../../../../contexts/kibana'; type TableItem = Record; interface LoadExploreDataArg { field: string; direction: SortDirection; + searchQuery: SavedSearchQuery; } + export interface UseExploreDataReturnType { errorMessage: string; loadExploreData: (arg: LoadExploreDataArg) => void; @@ -50,7 +55,7 @@ export const useExploreData = ( const [sortField, setSortField] = useState(''); const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); - const loadExploreData = async ({ field, direction }: LoadExploreDataArg) => { + const loadExploreData = async ({ field, direction, searchQuery }: LoadExploreDataArg) => { if (jobConfig !== undefined) { setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); @@ -58,19 +63,24 @@ export const useExploreData = ( try { const resultsField = jobConfig.dest.results_field; + const body: SearchQuery = { + query: searchQuery, + }; + + if (field !== undefined) { + body.sort = [ + { + [field]: { + order: direction, + }, + }, + ]; + } + const resp: SearchResponse = await ml.esSearch({ index: jobConfig.dest.index, size: SEARCH_SIZE, - body: { - query: { match_all: {} }, - sort: [ - { - [field]: { - order: direction, - }, - }, - ], - }, + body, }); setSortField(field); @@ -135,6 +145,7 @@ export const useExploreData = ( loadExploreData({ field: getOutlierScoreFieldName(jobConfig), direction: SORT_DIRECTION.DESC, + searchQuery: defaultSearchQuery, }); } }, [jobConfig && jobConfig.id]); 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 a1d4261d2cf32..ec504492e0a5e 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 @@ -50,11 +50,12 @@ import { getPredictedFieldName, INDEX_STATUS, SEARCH_SIZE, + defaultSearchQuery, } 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 { useExploreData } from './use_explore_data'; import { ExplorationTitle } from './regression_exploration'; const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts index 332451c6e4d7a..bf3565abd8de4 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts @@ -27,12 +27,10 @@ import { getPredictedFieldName, INDEX_STATUS, SEARCH_SIZE, + defaultSearchQuery, + SearchQuery, } from '../../../../common'; -export const defaultSearchQuery = { - match_all: {}, -}; - type TableItem = Record; interface LoadExploreDataArg { @@ -49,12 +47,6 @@ export interface UseExploreDataReturnType { tableItems: TableItem[]; } -export interface SearchQuery { - track_total_hits?: boolean; - query: SavedSearchQuery; - sort?: any; -} - export const useExploreData = ( jobConfig: DataFrameAnalyticsConfig | undefined, selectedFields: EsFieldName[], From ce6a73378a8d696db3a16bc160ce507d9b517051 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Wed, 20 Nov 2019 16:35:10 -0500 Subject: [PATCH 2/4] show empty results error message in table so user can retry query --- .../components/exploration/exploration.tsx | 84 ++++++------------- 1 file changed, 27 insertions(+), 57 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx index 95deefaf8fc1d..469ae164cef37 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx @@ -132,23 +132,9 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { ? euiThemeDark : euiThemeLight; - const [clearTable, setClearTable] = useState(false); - const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); - // 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); } @@ -159,7 +145,6 @@ export const Exploration: FC = React.memo(({ jobId, 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)]); } @@ -368,7 +353,6 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { setPageSize(size); if (sort.field !== sortField || sort.direction !== sortDirection) { - setClearTable(true); loadExploreData({ ...sort, searchQuery }); } }; @@ -411,8 +395,8 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (jobConfig === undefined) { return null; } - - if (status === INDEX_STATUS.ERROR) { + // if it's a searchBar syntax error leave the table visible so they can try again + if (status === INDEX_STATUS.ERROR && !errorMessage.includes('parsing_exception')) { return ( @@ -429,32 +413,16 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { ); } - if (status === INDEX_STATUS.LOADED && tableItems.length === 0) { - return ( - - - - - - - {getTaskStateBadge(jobStatus)} - - - -

- {i18n.translate('xpack.ml.dataframe.analytics.exploration.noDataCalloutBody', { - defaultMessage: - 'The query for the index returned no results. Please make sure the index contains documents and your query is not too restrictive.', - })} -

-
-
- ); + let tableError = + status === INDEX_STATUS.ERROR && errorMessage.includes('parsing_exception') + ? errorMessage + : searchError; + + if (status === INDEX_STATUS.LOADED && tableItems.length === 0 && tableError === undefined) { + tableError = i18n.translate('xpack.ml.dataframe.analytics.exploration.noDataCalloutBody', { + defaultMessage: + 'The query for the index returned no results. Please make sure the index contains documents and your query is not too restrictive.', + }); } return ( @@ -533,19 +501,21 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { {status !== INDEX_STATUS.LOADING && ( )} - {clearTable === false && columns.length > 0 && sortField !== '' && ( + {(columns.length > 0 || searchQuery !== defaultSearchQuery) && sortField !== '' && ( - - - + {tableItems.length === SEARCH_SIZE && ( + + + + )} = React.memo(({ jobId, jobStatus }) => { responsive={false} sorting={sorting} search={search} - error={searchError} + error={tableError} /> )} From 8028da405d05c24b0439bb70554ce93cce0f27f8 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 21 Nov 2019 08:09:41 -0500 Subject: [PATCH 3/4] remove unused translation --- x-pack/plugins/translations/translations/zh-CN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index cb4f0790e8310..fdb9c40d3c606 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7750,7 +7750,6 @@ "xpack.ml.dataframe.analytics.exploration.indexObjectToolTipContent": "无法显示此基于对象的列的完整内容。", "xpack.ml.dataframe.analytics.exploration.jobIdTitle": "作业 ID {jobId}", "xpack.ml.dataframe.analytics.exploration.noDataCalloutBody": "该索引的查询未返回结果。请确保索引包含文档且您的查询限制不过于严格。", - "xpack.ml.dataframe.analytics.exploration.noDataCalloutTitle": "空的索引查询结果。", "xpack.ml.dataframe.analytics.exploration.selectColumnsAriaLabel": "选择列", "xpack.ml.dataframe.analytics.exploration.selectFieldsPopoverTitle": "选择字段", "xpack.ml.dataframe.analytics.exploration.title": "分析浏览", From 19a8d9a48f9b6f66580477276938c0e9efd0ee47 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 21 Nov 2019 15:48:07 -0500 Subject: [PATCH 4/4] type updates after branch update --- .../plugins/ml/public/data_frame_analytics/common/analytics.ts | 1 + .../components/regression_exploration/evaluate_panel.tsx | 2 +- .../regression_exploration/regression_exploration.tsx | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts index 989751e450997..f910b8ea8a233 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/common/analytics.ts @@ -38,6 +38,7 @@ export const defaultSearchQuery = { }; export interface SearchQuery { + track_total_hits?: boolean; query: SavedSearchQuery; sort?: any; } 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 8bb44da74087c..d877ed40e587d 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 @@ -25,8 +25,8 @@ import { getEvalQueryBody, isRegressionResultsSearchBoolQuery, RegressionResultsSearchQuery, + SearchQuery, } from '../../../../common/analytics'; -import { SearchQuery } from './use_explore_data'; interface Props { jobConfig: DataFrameAnalyticsConfig; 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 b188334934ae0..1fdababda6466 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 @@ -12,8 +12,7 @@ import { DataFrameAnalyticsConfig } from '../../../../common'; import { EvaluatePanel } from './evaluate_panel'; import { ResultsTable } from './results_table'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; -import { defaultSearchQuery } from './use_explore_data'; -import { RegressionResultsSearchQuery } from '../../../../common/analytics'; +import { RegressionResultsSearchQuery, defaultSearchQuery } from '../../../../common/analytics'; interface GetDataFrameAnalyticsResponse { count: number;