From b9814bfb76c046ef930a976f25f342062334a84a Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Thu, 16 Jan 2020 10:32:27 -0500 Subject: [PATCH] [ML] DF Analytics Outlier detection results: improve handling of text fields (#55002) * add keyword suffix to fieldName when both text and keyword * update exploration jest test --- .../exploration/exploration.test.tsx | 6 ++- .../components/exploration/exploration.tsx | 51 +++++++++++++++++-- .../exploration/use_explore_data.ts | 17 +++---- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx index 92f438459128..013ea8ddc78a 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.test.tsx @@ -7,6 +7,8 @@ import { shallow } from 'enzyme'; import React from 'react'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; +import { KibanaContext } from '../../../../../contexts/kibana'; +import { kibanaContextValueMock } from '../../../../../contexts/kibana/__mocks__/kibana_context_value'; jest.mock('../../../../../contexts/ui/use_ui_chrome_context'); jest.mock('ui/new_platform'); @@ -22,7 +24,9 @@ jest.mock('react', () => { describe('Data Frame Analytics: ', () => { test('Minimal initialization', () => { const wrapper = shallow( - + + + ); // Without the jobConfig being loaded, the component will just return empty. expect(wrapper.text()).toMatch(''); diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx index 9691a0706121..098f8f07bee4 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx @@ -55,6 +55,7 @@ import { SEARCH_SIZE, defaultSearchQuery, } from '../../../../common'; +import { isKeywordAndTextType } from '../../../../common/fields'; import { getOutlierScoreFieldName } from './common'; import { useExploreData, TableItem } from './use_explore_data'; @@ -64,6 +65,10 @@ import { } from '../../../analytics_management/components/analytics_list/common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { SavedSearchQuery } from '../../../../../contexts/kibana'; +import { getIndexPatternIdFromName } from '../../../../../util/index_utils'; +import { IIndexPattern } from '../../../../../../../../../../../src/plugins/data/common/index_patterns'; +import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; +import { useKibanaContext } from '../../../../../contexts/kibana'; const FEATURE_INFLUENCE = 'feature_influence'; @@ -110,6 +115,19 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { const [searchError, setSearchError] = useState(undefined); const [searchString, setSearchString] = useState(undefined); + const kibanaContext = useKibanaContext(); + + const initializeJobCapsService = async () => { + if (jobConfig !== undefined) { + const sourceIndex = jobConfig.source.index[0]; + const indexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; + const indexPattern: IIndexPattern = await kibanaContext.indexPatterns.get(indexPatternId); + if (indexPattern !== undefined) { + await newJobCapsService.initializeFromIndexPattern(indexPattern, false, false); + } + } + }; + useEffect(() => { (async function() { const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( @@ -124,6 +142,10 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { })(); }, []); + useEffect(() => { + initializeJobCapsService(); + }, [jobConfig && jobConfig.id]); + const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); @@ -293,10 +315,16 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (jobConfig !== undefined) { const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); + let requiresKeyword = false; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field, direction, searchQuery }); + + if (outlierScoreFieldSelected === false) { + requiresKeyword = isKeywordAndTextType(field); + } + + loadExploreData({ field, direction, searchQuery, requiresKeyword }); } }, [JSON.stringify(searchQuery)]); @@ -307,10 +335,16 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (jobConfig !== undefined && columns.length > 0 && !selectedFields.includes(sortField)) { const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); + let requiresKeyword = false; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field, direction, searchQuery }); + + if (outlierScoreFieldSelected === false) { + requiresKeyword = isKeywordAndTextType(field); + } + + loadExploreData({ field, direction, searchQuery, requiresKeyword }); return; } }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); @@ -334,8 +368,17 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { setPageIndex(index); setPageSize(size); - if (sort.field !== sortField || sort.direction !== sortDirection) { - loadExploreData({ ...sort, searchQuery }); + if ( + (sort.field !== sortField || sort.direction !== sortDirection) && + jobConfig !== undefined + ) { + const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); + let requiresKeyword = false; + + if (outlierScoreFieldName !== sort.field) { + requiresKeyword = isKeywordAndTextType(sort.field); + } + loadExploreData({ ...sort, searchQuery, requiresKeyword }); } }; } diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts index e76cbaa463f1..24cc8d000de7 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts @@ -23,18 +23,12 @@ import { defaultSearchQuery, SearchQuery, } from '../../../../common'; +import { LoadExploreDataArg } from '../../../../common/analytics'; import { getOutlierScoreFieldName } from './common'; -import { SavedSearchQuery } from '../../../../../contexts/kibana'; export type TableItem = Record; -interface LoadExploreDataArg { - field: string; - direction: SortDirection; - searchQuery: SavedSearchQuery; -} - export interface UseExploreDataReturnType { errorMessage: string; loadExploreData: (arg: LoadExploreDataArg) => void; @@ -55,7 +49,12 @@ export const useExploreData = ( const [sortField, setSortField] = useState(''); const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); - const loadExploreData = async ({ field, direction, searchQuery }: LoadExploreDataArg) => { + const loadExploreData = async ({ + field, + direction, + searchQuery, + requiresKeyword, + }: LoadExploreDataArg) => { if (jobConfig !== undefined) { setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); @@ -70,7 +69,7 @@ export const useExploreData = ( if (field !== undefined) { body.sort = [ { - [field]: { + [`${field}${requiresKeyword ? '.keyword' : ''}`]: { order: direction, }, },