[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
This commit is contained in:
Melissa Alvarez 2020-01-16 10:32:27 -05:00 committed by GitHub
parent e60289f611
commit b9814bfb76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 14 deletions

View file

@ -7,6 +7,8 @@
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import React from 'react'; import React from 'react';
import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; 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('../../../../../contexts/ui/use_ui_chrome_context');
jest.mock('ui/new_platform'); jest.mock('ui/new_platform');
@ -22,7 +24,9 @@ jest.mock('react', () => {
describe('Data Frame Analytics: <Exploration />', () => { describe('Data Frame Analytics: <Exploration />', () => {
test('Minimal initialization', () => { test('Minimal initialization', () => {
const wrapper = shallow( const wrapper = shallow(
<Exploration jobId="the-job-id" jobStatus={DATA_FRAME_TASK_STATE.STOPPED} /> <KibanaContext.Provider value={kibanaContextValueMock}>
<Exploration jobId="the-job-id" jobStatus={DATA_FRAME_TASK_STATE.STOPPED} />
</KibanaContext.Provider>
); );
// Without the jobConfig being loaded, the component will just return empty. // Without the jobConfig being loaded, the component will just return empty.
expect(wrapper.text()).toMatch(''); expect(wrapper.text()).toMatch('');

View file

@ -55,6 +55,7 @@ import {
SEARCH_SIZE, SEARCH_SIZE,
defaultSearchQuery, defaultSearchQuery,
} from '../../../../common'; } from '../../../../common';
import { isKeywordAndTextType } from '../../../../common/fields';
import { getOutlierScoreFieldName } from './common'; import { getOutlierScoreFieldName } from './common';
import { useExploreData, TableItem } from './use_explore_data'; import { useExploreData, TableItem } from './use_explore_data';
@ -64,6 +65,10 @@ import {
} from '../../../analytics_management/components/analytics_list/common'; } from '../../../analytics_management/components/analytics_list/common';
import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns';
import { SavedSearchQuery } from '../../../../../contexts/kibana'; 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'; const FEATURE_INFLUENCE = 'feature_influence';
@ -110,6 +115,19 @@ export const Exploration: FC<Props> = React.memo(({ jobId, jobStatus }) => {
const [searchError, setSearchError] = useState<any>(undefined); const [searchError, setSearchError] = useState<any>(undefined);
const [searchString, setSearchString] = useState<string | undefined>(undefined); const [searchString, setSearchString] = useState<string | undefined>(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(() => { useEffect(() => {
(async function() { (async function() {
const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics(
@ -124,6 +142,10 @@ export const Exploration: FC<Props> = React.memo(({ jobId, jobStatus }) => {
})(); })();
}, []); }, []);
useEffect(() => {
initializeJobCapsService();
}, [jobConfig && jobConfig.id]);
const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]);
const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false);
@ -293,10 +315,16 @@ export const Exploration: FC<Props> = React.memo(({ jobId, jobStatus }) => {
if (jobConfig !== undefined) { if (jobConfig !== undefined) {
const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig);
const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName);
let requiresKeyword = false;
const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0];
const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; 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)]); }, [JSON.stringify(searchQuery)]);
@ -307,10 +335,16 @@ export const Exploration: FC<Props> = React.memo(({ jobId, jobStatus }) => {
if (jobConfig !== undefined && columns.length > 0 && !selectedFields.includes(sortField)) { if (jobConfig !== undefined && columns.length > 0 && !selectedFields.includes(sortField)) {
const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig);
const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName);
let requiresKeyword = false;
const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0];
const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; 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; return;
} }
}, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]);
@ -334,8 +368,17 @@ export const Exploration: FC<Props> = React.memo(({ jobId, jobStatus }) => {
setPageIndex(index); setPageIndex(index);
setPageSize(size); setPageSize(size);
if (sort.field !== sortField || sort.direction !== sortDirection) { if (
loadExploreData({ ...sort, searchQuery }); (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 });
} }
}; };
} }

View file

@ -23,18 +23,12 @@ import {
defaultSearchQuery, defaultSearchQuery,
SearchQuery, SearchQuery,
} from '../../../../common'; } from '../../../../common';
import { LoadExploreDataArg } from '../../../../common/analytics';
import { getOutlierScoreFieldName } from './common'; import { getOutlierScoreFieldName } from './common';
import { SavedSearchQuery } from '../../../../../contexts/kibana';
export type TableItem = Record<string, any>; export type TableItem = Record<string, any>;
interface LoadExploreDataArg {
field: string;
direction: SortDirection;
searchQuery: SavedSearchQuery;
}
export interface UseExploreDataReturnType { export interface UseExploreDataReturnType {
errorMessage: string; errorMessage: string;
loadExploreData: (arg: LoadExploreDataArg) => void; loadExploreData: (arg: LoadExploreDataArg) => void;
@ -55,7 +49,12 @@ export const useExploreData = (
const [sortField, setSortField] = useState<string>(''); const [sortField, setSortField] = useState<string>('');
const [sortDirection, setSortDirection] = useState<SortDirection>(SORT_DIRECTION.ASC); const [sortDirection, setSortDirection] = useState<SortDirection>(SORT_DIRECTION.ASC);
const loadExploreData = async ({ field, direction, searchQuery }: LoadExploreDataArg) => { const loadExploreData = async ({
field,
direction,
searchQuery,
requiresKeyword,
}: LoadExploreDataArg) => {
if (jobConfig !== undefined) { if (jobConfig !== undefined) {
setErrorMessage(''); setErrorMessage('');
setStatus(INDEX_STATUS.LOADING); setStatus(INDEX_STATUS.LOADING);
@ -70,7 +69,7 @@ export const useExploreData = (
if (field !== undefined) { if (field !== undefined) {
body.sort = [ body.sort = [
{ {
[field]: { [`${field}${requiresKeyword ? '.keyword' : ''}`]: {
order: direction, order: direction,
}, },
}, },