[ML] DF Analytics Classification exploration: replace table with data grid (#63757)
* update classification result to use datagrid * consider isTraining in docCount fetch * fix translations
This commit is contained in:
parent
32c6fd777f
commit
784b8beb2f
8 changed files with 421 additions and 514 deletions
|
@ -52,7 +52,7 @@ export interface ClassificationAnalysis {
|
|||
classification: Classification;
|
||||
}
|
||||
|
||||
export interface LoadRegressionExploreDataArg {
|
||||
export interface LoadExploreDataArg {
|
||||
filterByIsTraining?: boolean;
|
||||
searchQuery: SavedSearchQuery;
|
||||
}
|
||||
|
@ -409,11 +409,11 @@ export function getEvalQueryBody({
|
|||
ignoreDefaultQuery,
|
||||
}: {
|
||||
resultsField: string;
|
||||
isTraining: boolean;
|
||||
isTraining?: boolean;
|
||||
searchQuery?: ResultsSearchQuery;
|
||||
ignoreDefaultQuery?: boolean;
|
||||
}) {
|
||||
let query;
|
||||
let query: any;
|
||||
|
||||
const trainingQuery: ResultsSearchQuery = {
|
||||
term: { [`${resultsField}.is_training`]: { value: isTraining } },
|
||||
|
@ -426,19 +426,25 @@ export function getEvalQueryBody({
|
|||
searchQueryClone.bool.must = [];
|
||||
}
|
||||
|
||||
searchQueryClone.bool.must.push(trainingQuery);
|
||||
if (isTraining !== undefined) {
|
||||
searchQueryClone.bool.must.push(trainingQuery);
|
||||
}
|
||||
|
||||
query = searchQueryClone;
|
||||
} else if (isQueryStringQuery(searchQueryClone)) {
|
||||
query = {
|
||||
bool: {
|
||||
must: [searchQueryClone, trainingQuery],
|
||||
must: [searchQueryClone],
|
||||
},
|
||||
};
|
||||
if (isTraining !== undefined) {
|
||||
query.bool.must.push(trainingQuery);
|
||||
}
|
||||
} else {
|
||||
// Not a bool or string query so we need to create it so can add the trainingQuery
|
||||
query = {
|
||||
bool: {
|
||||
must: [trainingQuery],
|
||||
must: isTraining !== undefined ? [trainingQuery] : [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -456,7 +462,7 @@ interface EvaluateMetrics {
|
|||
}
|
||||
|
||||
interface LoadEvalDataConfig {
|
||||
isTraining: boolean;
|
||||
isTraining?: boolean;
|
||||
index: string;
|
||||
dependentVariable: string;
|
||||
resultsField: string;
|
||||
|
@ -535,7 +541,7 @@ interface TrackTotalHitsSearchResponse {
|
|||
|
||||
interface LoadDocsCountConfig {
|
||||
ignoreDefaultQuery?: boolean;
|
||||
isTraining: boolean;
|
||||
isTraining?: boolean;
|
||||
searchQuery: SavedSearchQuery;
|
||||
resultsField: string;
|
||||
destIndex: string;
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { EuiDataGrid, EuiDataGridPaginationProps, EuiDataGridSorting } from '@elastic/eui';
|
||||
|
||||
import { euiDataGridStyle, euiDataGridToolbarSettings } from '../../../../common';
|
||||
|
||||
import { mlFieldFormatService } from '../../../../../services/field_format_service';
|
||||
|
||||
import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
|
||||
|
||||
type Pagination = Pick<EuiDataGridPaginationProps, 'pageIndex' | 'pageSize'>;
|
||||
type TableItem = Record<string, any>;
|
||||
|
||||
interface ExplorationDataGridProps {
|
||||
colorRange?: (d: number) => string;
|
||||
columns: any[];
|
||||
indexPattern: IndexPattern;
|
||||
pagination: Pagination;
|
||||
resultsField: string;
|
||||
rowCount: number;
|
||||
selectedFields: string[];
|
||||
setPagination: Dispatch<SetStateAction<Pagination>>;
|
||||
setSelectedFields: Dispatch<SetStateAction<string[]>>;
|
||||
setSortingColumns: Dispatch<SetStateAction<EuiDataGridSorting['columns']>>;
|
||||
sortingColumns: EuiDataGridSorting['columns'];
|
||||
tableItems: TableItem[];
|
||||
}
|
||||
|
||||
export const ClassificationExplorationDataGrid: FC<ExplorationDataGridProps> = ({
|
||||
columns,
|
||||
indexPattern,
|
||||
pagination,
|
||||
resultsField,
|
||||
rowCount,
|
||||
selectedFields,
|
||||
setPagination,
|
||||
setSelectedFields,
|
||||
setSortingColumns,
|
||||
sortingColumns,
|
||||
tableItems,
|
||||
}) => {
|
||||
const renderCellValue = useMemo(() => {
|
||||
return ({ rowIndex, columnId }: { rowIndex: number; columnId: string; setCellProps: any }) => {
|
||||
const adjustedRowIndex = rowIndex - pagination.pageIndex * pagination.pageSize;
|
||||
|
||||
const fullItem = tableItems[adjustedRowIndex];
|
||||
|
||||
if (fullItem === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let format: any;
|
||||
|
||||
if (indexPattern !== undefined) {
|
||||
format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, columnId, '');
|
||||
}
|
||||
|
||||
const cellValue =
|
||||
fullItem.hasOwnProperty(columnId) && fullItem[columnId] !== undefined
|
||||
? fullItem[columnId]
|
||||
: null;
|
||||
|
||||
if (format !== undefined) {
|
||||
return format.convert(cellValue, 'text');
|
||||
}
|
||||
|
||||
if (typeof cellValue === 'string' || cellValue === null) {
|
||||
return cellValue;
|
||||
}
|
||||
|
||||
if (typeof cellValue === 'boolean') {
|
||||
return cellValue ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (typeof cellValue === 'object' && cellValue !== null) {
|
||||
return JSON.stringify(cellValue);
|
||||
}
|
||||
|
||||
return cellValue;
|
||||
};
|
||||
}, [resultsField, rowCount, tableItems, pagination.pageIndex, pagination.pageSize]);
|
||||
|
||||
const onChangeItemsPerPage = useCallback(
|
||||
pageSize => {
|
||||
setPagination(p => {
|
||||
const pageIndex = Math.floor((p.pageSize * p.pageIndex) / pageSize);
|
||||
return { pageIndex, pageSize };
|
||||
});
|
||||
},
|
||||
[setPagination]
|
||||
);
|
||||
|
||||
const onChangePage = useCallback(pageIndex => setPagination(p => ({ ...p, pageIndex })), [
|
||||
setPagination,
|
||||
]);
|
||||
|
||||
const onSort = useCallback(sc => setSortingColumns(sc), [setSortingColumns]);
|
||||
|
||||
return (
|
||||
<EuiDataGrid
|
||||
aria-label={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.classificationExploration.dataGridAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Classification results table',
|
||||
}
|
||||
)}
|
||||
columns={columns}
|
||||
columnVisibility={{
|
||||
visibleColumns: selectedFields,
|
||||
setVisibleColumns: setSelectedFields,
|
||||
}}
|
||||
gridStyle={euiDataGridStyle}
|
||||
rowCount={rowCount}
|
||||
renderCellValue={renderCellValue}
|
||||
sorting={{ columns: sortingColumns, onSort }}
|
||||
toolbarVisibility={euiDataGridToolbarSettings}
|
||||
pagination={{
|
||||
...pagination,
|
||||
pageSizeOptions: PAGE_SIZE_OPTIONS,
|
||||
onChangeItemsPerPage,
|
||||
onChangePage,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -117,13 +117,7 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
|
|||
const resultsField = jobConfig.dest.results_field;
|
||||
let requiresKeyword = false;
|
||||
|
||||
const loadData = async ({
|
||||
isTrainingClause,
|
||||
ignoreDefaultQuery = true,
|
||||
}: {
|
||||
isTrainingClause: { query: string; operator: string };
|
||||
ignoreDefaultQuery?: boolean;
|
||||
}) => {
|
||||
const loadData = async ({ isTraining }: { isTraining: boolean | undefined }) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
|
@ -134,19 +128,18 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
|
|||
}
|
||||
|
||||
const evalData = await loadEvalData({
|
||||
isTraining: false,
|
||||
isTraining,
|
||||
index,
|
||||
dependentVariable,
|
||||
resultsField,
|
||||
predictionFieldName,
|
||||
searchQuery,
|
||||
ignoreDefaultQuery,
|
||||
jobType: ANALYSIS_CONFIG_TYPE.CLASSIFICATION,
|
||||
requiresKeyword,
|
||||
});
|
||||
|
||||
const docsCountResp = await loadDocsCount({
|
||||
isTraining: false,
|
||||
isTraining,
|
||||
searchQuery,
|
||||
resultsField,
|
||||
destIndex: jobConfig.dest.index,
|
||||
|
@ -225,29 +218,46 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
|
|||
}, [confusionMatrixData]);
|
||||
|
||||
useEffect(() => {
|
||||
const hasIsTrainingClause =
|
||||
isResultsSearchBoolQuery(searchQuery) &&
|
||||
searchQuery.bool.must.filter(
|
||||
(clause: any) => clause.match && clause.match[`${resultsField}.is_training`] !== undefined
|
||||
);
|
||||
const isTrainingClause =
|
||||
hasIsTrainingClause &&
|
||||
hasIsTrainingClause[0] &&
|
||||
hasIsTrainingClause[0].match[`${resultsField}.is_training`];
|
||||
let isTraining: boolean | undefined;
|
||||
const query =
|
||||
isResultsSearchBoolQuery(searchQuery) && (searchQuery.bool.should || searchQuery.bool.filter);
|
||||
|
||||
const noTrainingQuery = isTrainingClause === false || isTrainingClause === undefined;
|
||||
if (query !== undefined && query !== false) {
|
||||
for (let i = 0; i < query.length; i++) {
|
||||
const clause = query[i];
|
||||
|
||||
if (noTrainingQuery) {
|
||||
if (clause.match && clause.match[`${resultsField}.is_training`] !== undefined) {
|
||||
isTraining = clause.match[`${resultsField}.is_training`];
|
||||
break;
|
||||
} else if (
|
||||
clause.bool &&
|
||||
(clause.bool.should !== undefined || clause.bool.filter !== undefined)
|
||||
) {
|
||||
const innerQuery = clause.bool.should || clause.bool.filter;
|
||||
if (innerQuery !== undefined) {
|
||||
for (let j = 0; j < innerQuery.length; j++) {
|
||||
const innerClause = innerQuery[j];
|
||||
if (
|
||||
innerClause.match &&
|
||||
innerClause.match[`${resultsField}.is_training`] !== undefined
|
||||
) {
|
||||
isTraining = innerClause.match[`${resultsField}.is_training`];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isTraining === undefined) {
|
||||
setDataSubsetTitle(SUBSET_TITLE.ENTIRE);
|
||||
} else {
|
||||
setDataSubsetTitle(
|
||||
isTrainingClause && isTrainingClause.query === 'true'
|
||||
? SUBSET_TITLE.TRAINING
|
||||
: SUBSET_TITLE.TESTING
|
||||
isTraining && isTraining === true ? SUBSET_TITLE.TRAINING : SUBSET_TITLE.TESTING
|
||||
);
|
||||
}
|
||||
|
||||
loadData({ isTrainingClause });
|
||||
loadData({ isTraining });
|
||||
}, [JSON.stringify(searchQuery)]);
|
||||
|
||||
const renderCellValue = ({
|
||||
|
|
|
@ -4,71 +4,39 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment, FC, useEffect, useState } from 'react';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import React, { Fragment, FC, useEffect } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiBadge,
|
||||
EuiButtonIcon,
|
||||
EuiCallOut,
|
||||
EuiCheckbox,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiPanel,
|
||||
EuiPopover,
|
||||
EuiPopoverTitle,
|
||||
EuiProgress,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
Query,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { Query as QueryType } from '../../../analytics_management/components/analytics_list/common';
|
||||
import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public';
|
||||
import { mlFieldFormatService } from '../../../../../services/field_format_service';
|
||||
import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public';
|
||||
|
||||
import {
|
||||
ColumnType,
|
||||
mlInMemoryTableBasicFactory,
|
||||
OnTableChangeArg,
|
||||
SortingPropType,
|
||||
SORT_DIRECTION,
|
||||
} from '../../../../../components/ml_in_memory_table';
|
||||
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../../../../util/date_utils';
|
||||
import { Field } from '../../../../../../../common/types/fields';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
import {
|
||||
BASIC_NUMERICAL_TYPES,
|
||||
EXTENDED_NUMERICAL_TYPES,
|
||||
isKeywordAndTextType,
|
||||
sortRegressionResultsFields,
|
||||
} from '../../../../common/fields';
|
||||
|
||||
import {
|
||||
toggleSelectedField,
|
||||
EsDoc,
|
||||
DataFrameAnalyticsConfig,
|
||||
EsFieldName,
|
||||
MAX_COLUMNS,
|
||||
getPredictedFieldName,
|
||||
INDEX_STATUS,
|
||||
SEARCH_SIZE,
|
||||
defaultSearchQuery,
|
||||
getDependentVar,
|
||||
} 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, TableItem } from './use_explore_data';
|
||||
import { useExploreData } from './use_explore_data'; // TableItem
|
||||
import { ExplorationTitle } from './classification_exploration';
|
||||
|
||||
const PAGE_SIZE_OPTIONS = [5, 10, 25, 50];
|
||||
|
||||
const MlInMemoryTableBasic = mlInMemoryTableBasicFactory<TableItem>();
|
||||
import { ClassificationExplorationDataGrid } from './classification_exploration_data_grid';
|
||||
import { ExplorationQueryBar } from '../exploration_query_bar';
|
||||
|
||||
const showingDocs = i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.classificationExploration.documentsShownHelpText',
|
||||
|
@ -94,307 +62,65 @@ interface Props {
|
|||
|
||||
export const ResultsTable: FC<Props> = React.memo(
|
||||
({ indexPattern, jobConfig, jobStatus, setEvaluateSearchQuery }) => {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(25);
|
||||
const [selectedFields, setSelectedFields] = useState([] as Field[]);
|
||||
const [docFields, setDocFields] = useState([] as Field[]);
|
||||
const [depVarType, setDepVarType] = useState<ES_FIELD_TYPES | undefined>(undefined);
|
||||
const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState<SavedSearchQuery>(defaultSearchQuery);
|
||||
const [searchError, setSearchError] = useState<any>(undefined);
|
||||
const [searchString, setSearchString] = useState<string | undefined>(undefined);
|
||||
|
||||
const predictedFieldName = getPredictedFieldName(
|
||||
jobConfig.dest.results_field,
|
||||
jobConfig.analysis
|
||||
);
|
||||
|
||||
const dependentVariable = getDependentVar(jobConfig.analysis);
|
||||
|
||||
function toggleColumnsPopover() {
|
||||
setColumnsPopoverVisible(!isColumnsPopoverVisible);
|
||||
}
|
||||
|
||||
function closeColumnsPopover() {
|
||||
setColumnsPopoverVisible(false);
|
||||
}
|
||||
|
||||
function toggleColumn(column: EsFieldName) {
|
||||
if (tableItems.length > 0 && jobConfig !== undefined) {
|
||||
// spread to a new array otherwise the component wouldn't re-render
|
||||
setSelectedFields([
|
||||
...toggleSelectedField(selectedFields, column, jobConfig.dest.results_field, depVarType),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const needsDestIndexFields = indexPattern && indexPattern.title === jobConfig.source.index[0];
|
||||
|
||||
const resultsField = jobConfig.dest.results_field;
|
||||
const {
|
||||
errorMessage,
|
||||
loadExploreData,
|
||||
sortField,
|
||||
sortDirection,
|
||||
status,
|
||||
tableItems,
|
||||
} = useExploreData(
|
||||
jobConfig,
|
||||
needsDestIndexFields,
|
||||
fieldTypes,
|
||||
pagination,
|
||||
searchQuery,
|
||||
selectedFields,
|
||||
rowCount,
|
||||
setPagination,
|
||||
setSearchQuery,
|
||||
setSelectedFields,
|
||||
setDocFields,
|
||||
setDepVarType
|
||||
);
|
||||
setSortingColumns,
|
||||
sortingColumns,
|
||||
status,
|
||||
tableFields,
|
||||
tableItems,
|
||||
} = useExploreData(jobConfig, needsDestIndexFields);
|
||||
|
||||
const columns: Array<ColumnType<TableItem>> = selectedFields
|
||||
.sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig))
|
||||
.map(field => {
|
||||
const { type } = field;
|
||||
let format: any;
|
||||
useEffect(() => {
|
||||
setEvaluateSearchQuery(searchQuery);
|
||||
}, [JSON.stringify(searchQuery)]);
|
||||
|
||||
if (indexPattern !== undefined) {
|
||||
format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, field.id, '');
|
||||
}
|
||||
const columns = tableFields
|
||||
.sort((a: any, b: any) => sortRegressionResultsFields(a, b, jobConfig))
|
||||
.map((field: any) => {
|
||||
// Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json']
|
||||
// To fall back to the default string schema it needs to be undefined.
|
||||
let schema;
|
||||
let isSortable = true;
|
||||
const type = fieldTypes[field];
|
||||
const isNumber =
|
||||
type !== undefined &&
|
||||
(BASIC_NUMERICAL_TYPES.has(type) || EXTENDED_NUMERICAL_TYPES.has(type));
|
||||
|
||||
const column: ColumnType<TableItem> = {
|
||||
field: field.name,
|
||||
name: field.name,
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
};
|
||||
|
||||
const render = (d: any, fullItem: EsDoc) => {
|
||||
if (format !== undefined) {
|
||||
d = format.convert(d, 'text');
|
||||
return d;
|
||||
}
|
||||
|
||||
if (Array.isArray(d) && d.every(item => typeof item === 'string')) {
|
||||
// If the cells data is an array of strings, return as a comma separated list.
|
||||
// The list will get limited to 5 items with `…` at the end if there's more in the original array.
|
||||
return `${d.slice(0, 5).join(', ')}${d.length > 5 ? ', …' : ''}`;
|
||||
} else if (Array.isArray(d)) {
|
||||
// If the cells data is an array of e.g. objects, display a 'array' badge with a
|
||||
// tooltip that explains that this type of field is not supported in this table.
|
||||
return (
|
||||
<EuiToolTip
|
||||
content={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.classificationExploration.indexArrayToolTipContent',
|
||||
{
|
||||
defaultMessage:
|
||||
'The full content of this array based column cannot be displayed.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiBadge>
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent',
|
||||
{
|
||||
defaultMessage: 'array',
|
||||
}
|
||||
)}
|
||||
</EuiBadge>
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
if (isNumber) {
|
||||
column.dataType = 'number';
|
||||
column.render = render;
|
||||
} else if (typeof type !== 'undefined') {
|
||||
switch (type) {
|
||||
case ES_FIELD_TYPES.BOOLEAN:
|
||||
column.dataType = ES_FIELD_TYPES.BOOLEAN;
|
||||
column.render = d => (d ? 'true' : 'false');
|
||||
break;
|
||||
case ES_FIELD_TYPES.DATE:
|
||||
column.align = 'right';
|
||||
if (format !== undefined) {
|
||||
column.render = render;
|
||||
} else {
|
||||
column.render = (d: any) => {
|
||||
if (d !== undefined) {
|
||||
return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
column.render = render;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
column.render = render;
|
||||
schema = 'numeric';
|
||||
}
|
||||
|
||||
return column;
|
||||
switch (type) {
|
||||
case 'date':
|
||||
schema = 'datetime';
|
||||
break;
|
||||
case 'geo_point':
|
||||
schema = 'json';
|
||||
break;
|
||||
case 'boolean':
|
||||
schema = 'boolean';
|
||||
break;
|
||||
}
|
||||
|
||||
if (field === `${resultsField}.feature_importance`) {
|
||||
isSortable = false;
|
||||
}
|
||||
|
||||
return { id: field, schema, isSortable };
|
||||
});
|
||||
|
||||
const docFieldsCount = docFields.length;
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
jobConfig !== undefined &&
|
||||
columns.length > 0 &&
|
||||
selectedFields.length > 0 &&
|
||||
sortField !== undefined &&
|
||||
sortDirection !== undefined &&
|
||||
selectedFields.some(field => field.name === sortField)
|
||||
) {
|
||||
let field = sortField;
|
||||
// If sorting by predictedField use dependentVar type
|
||||
if (predictedFieldName === sortField) {
|
||||
field = dependentVariable;
|
||||
}
|
||||
const requiresKeyword = isKeywordAndTextType(field);
|
||||
|
||||
loadExploreData({
|
||||
field: sortField,
|
||||
direction: sortDirection,
|
||||
searchQuery,
|
||||
requiresKeyword,
|
||||
});
|
||||
}
|
||||
}, [JSON.stringify(searchQuery)]);
|
||||
|
||||
useEffect(() => {
|
||||
// By default set sorting to descending on the prediction field (`<dependent_varible or prediction_field_name>_prediction`).
|
||||
// if that's not available sort ascending on the first column. Check if the current sorting field is still available.
|
||||
if (
|
||||
jobConfig !== undefined &&
|
||||
columns.length > 0 &&
|
||||
selectedFields.length > 0 &&
|
||||
!selectedFields.some(field => field.name === sortField)
|
||||
) {
|
||||
const predictedFieldSelected = selectedFields.some(
|
||||
field => field.name === predictedFieldName
|
||||
);
|
||||
|
||||
// CHECK IF keyword suffix is needed (if predicted field is selected we have to check the dependent variable type)
|
||||
let sortByField = predictedFieldSelected ? dependentVariable : selectedFields[0].name;
|
||||
|
||||
const requiresKeyword = isKeywordAndTextType(sortByField);
|
||||
|
||||
sortByField = predictedFieldSelected ? predictedFieldName : sortByField;
|
||||
|
||||
const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC;
|
||||
loadExploreData({ field: sortByField, direction, searchQuery, requiresKeyword });
|
||||
}
|
||||
}, [
|
||||
jobConfig,
|
||||
columns.length,
|
||||
selectedFields.length,
|
||||
sortField,
|
||||
sortDirection,
|
||||
tableItems.length,
|
||||
]);
|
||||
|
||||
let sorting: SortingPropType = false;
|
||||
let onTableChange;
|
||||
|
||||
if (columns.length > 0 && sortField !== '' && sortField !== undefined) {
|
||||
sorting = {
|
||||
sort: {
|
||||
field: sortField,
|
||||
direction: sortDirection,
|
||||
},
|
||||
};
|
||||
|
||||
onTableChange = ({
|
||||
page = { index: 0, size: 10 },
|
||||
sort = { field: sortField, direction: sortDirection },
|
||||
}: OnTableChangeArg) => {
|
||||
const { index, size } = page;
|
||||
setPageIndex(index);
|
||||
setPageSize(size);
|
||||
|
||||
if (sort.field !== sortField || sort.direction !== sortDirection) {
|
||||
let field = sort.field;
|
||||
// If sorting by predictedField use depVar for type check
|
||||
if (predictedFieldName === sort.field) {
|
||||
field = dependentVariable;
|
||||
}
|
||||
|
||||
loadExploreData({
|
||||
...sort,
|
||||
searchQuery,
|
||||
requiresKeyword: isKeywordAndTextType(field),
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const pagination = {
|
||||
initialPageIndex: pageIndex,
|
||||
initialPageSize: pageSize,
|
||||
totalItemCount: tableItems.length,
|
||||
pageSizeOptions: PAGE_SIZE_OPTIONS,
|
||||
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);
|
||||
// set query for use in evaluate panel
|
||||
setEvaluateSearchQuery(esQueryDsl);
|
||||
} catch (e) {
|
||||
setSearchError(e.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const search = {
|
||||
onChange: onQueryChange,
|
||||
defaultQuery: searchString,
|
||||
box: {
|
||||
incremental: false,
|
||||
placeholder: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder',
|
||||
{
|
||||
defaultMessage: 'E.g. avg>0.5',
|
||||
}
|
||||
),
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
type: 'field_value_toggle_group',
|
||||
field: `${jobConfig.dest.results_field}.is_training`,
|
||||
items: [
|
||||
{
|
||||
value: false,
|
||||
name: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel',
|
||||
{
|
||||
defaultMessage: 'Testing',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
value: true,
|
||||
name: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel',
|
||||
{
|
||||
defaultMessage: 'Training',
|
||||
}
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const docFieldsCount = tableFields.length;
|
||||
|
||||
if (jobConfig === undefined) {
|
||||
return null;
|
||||
|
@ -426,11 +152,6 @@ export const ResultsTable: FC<Props> = React.memo(
|
|||
);
|
||||
}
|
||||
|
||||
const tableError =
|
||||
status === INDEX_STATUS.ERROR && errorMessage.includes('parsing_exception')
|
||||
? errorMessage
|
||||
: searchError;
|
||||
|
||||
return (
|
||||
<EuiPanel
|
||||
grow={false}
|
||||
|
@ -456,7 +177,7 @@ export const ResultsTable: FC<Props> = React.memo(
|
|||
{docFieldsCount > MAX_COLUMNS && (
|
||||
<EuiText size="s">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.fieldSelection',
|
||||
'xpack.ml.dataframe.analytics.classificationExploration.fieldSelection',
|
||||
{
|
||||
defaultMessage:
|
||||
'{selectedFieldsLength, number} of {docFieldsCount, number} {docFieldsCount, plural, one {field} other {fields}} selected',
|
||||
|
@ -466,52 +187,6 @@ export const ResultsTable: FC<Props> = React.memo(
|
|||
</EuiText>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">
|
||||
<EuiPopover
|
||||
id="popover"
|
||||
button={
|
||||
<EuiButtonIcon
|
||||
iconType="gear"
|
||||
onClick={toggleColumnsPopover}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.selectColumnsAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Select columns',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
}
|
||||
isOpen={isColumnsPopoverVisible}
|
||||
closePopover={closeColumnsPopover}
|
||||
ownFocus
|
||||
>
|
||||
<EuiPopoverTitle>
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle',
|
||||
{
|
||||
defaultMessage: 'Select fields',
|
||||
}
|
||||
)}
|
||||
</EuiPopoverTitle>
|
||||
<div style={{ maxHeight: '400px', overflowY: 'scroll' }}>
|
||||
{docFields.map(({ name }) => (
|
||||
<EuiCheckbox
|
||||
key={name}
|
||||
id={name}
|
||||
label={name}
|
||||
checked={selectedFields.some(field => field.name === name)}
|
||||
onChange={() => toggleColumn(name)}
|
||||
disabled={
|
||||
selectedFields.some(field => field.name === name) &&
|
||||
selectedFields.length === 1
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</EuiPopover>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -520,28 +195,39 @@ export const ResultsTable: FC<Props> = React.memo(
|
|||
<EuiProgress size="xs" color="accent" max={1} value={0} />
|
||||
)}
|
||||
{(columns.length > 0 || searchQuery !== defaultSearchQuery) && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
helpText={tableItems.length === SEARCH_SIZE ? showingFirstDocs : showingDocs}
|
||||
>
|
||||
<Fragment />
|
||||
</EuiFormRow>
|
||||
<EuiSpacer />
|
||||
<MlInMemoryTableBasic
|
||||
allowNeutralSort={false}
|
||||
columns={columns}
|
||||
compressed
|
||||
hasActions={false}
|
||||
isSelectable={false}
|
||||
items={tableItems}
|
||||
onTableChange={onTableChange}
|
||||
pagination={pagination}
|
||||
responsive={false}
|
||||
search={search}
|
||||
error={tableError}
|
||||
sorting={sorting}
|
||||
/>
|
||||
</Fragment>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem>
|
||||
<ExplorationQueryBar
|
||||
indexPattern={indexPattern}
|
||||
setSearchQuery={setSearchQuery}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFormRow
|
||||
helpText={tableItems.length === SEARCH_SIZE ? showingFirstDocs : showingDocs}
|
||||
>
|
||||
<Fragment />
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ClassificationExplorationDataGrid
|
||||
columns={columns}
|
||||
indexPattern={indexPattern}
|
||||
pagination={pagination}
|
||||
resultsField={jobConfig.dest.results_field}
|
||||
rowCount={rowCount}
|
||||
selectedFields={selectedFields}
|
||||
setPagination={setPagination}
|
||||
setSelectedFields={setSelectedFields}
|
||||
setSortingColumns={setSortingColumns}
|
||||
sortingColumns={sortingColumns}
|
||||
tableItems={tableItems}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</EuiPanel>
|
||||
);
|
||||
|
|
|
@ -3,113 +3,158 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
||||
import { EuiDataGridPaginationProps, EuiDataGridSorting } from '@elastic/eui';
|
||||
|
||||
import { SearchResponse } from 'elasticsearch';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { SortDirection, SORT_DIRECTION } from '../../../../../components/ml_in_memory_table';
|
||||
import { SORT_DIRECTION } from '../../../../../components/ml_in_memory_table';
|
||||
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { getNestedProperty } from '../../../../../util/object_utils';
|
||||
import { newJobCapsService } from '../../../../../services/new_job_capabilities_service';
|
||||
import { Field } from '../../../../../../../common/types/fields';
|
||||
import { isKeywordAndTextType } from '../../../../common/fields';
|
||||
import { Dictionary } from '../../../../../../../common/types/common';
|
||||
import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public';
|
||||
import {
|
||||
defaultSearchQuery,
|
||||
ResultsSearchQuery,
|
||||
isResultsSearchBoolQuery,
|
||||
LoadExploreDataArg,
|
||||
} from '../../../../common/analytics';
|
||||
|
||||
import {
|
||||
getDefaultFieldsFromJobCaps,
|
||||
getDependentVar,
|
||||
getFlattenedFields,
|
||||
getPredictedFieldName,
|
||||
DataFrameAnalyticsConfig,
|
||||
EsFieldName,
|
||||
INDEX_STATUS,
|
||||
SEARCH_SIZE,
|
||||
SearchQuery,
|
||||
} from '../../../../common';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
|
||||
interface LoadClassificationExploreDataArg {
|
||||
direction: SortDirection;
|
||||
filterByIsTraining?: boolean;
|
||||
field: string;
|
||||
searchQuery: SavedSearchQuery;
|
||||
requiresKeyword?: boolean;
|
||||
pageIndex?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
export type TableItem = Record<string, any>;
|
||||
type Pagination = Pick<EuiDataGridPaginationProps, 'pageIndex' | 'pageSize'>;
|
||||
|
||||
export interface UseExploreDataReturnType {
|
||||
errorMessage: string;
|
||||
loadExploreData: (arg: LoadClassificationExploreDataArg) => void;
|
||||
sortField: EsFieldName;
|
||||
sortDirection: SortDirection;
|
||||
fieldTypes: { [key: string]: ES_FIELD_TYPES };
|
||||
pagination: Pagination;
|
||||
rowCount: number;
|
||||
searchQuery: SavedSearchQuery;
|
||||
selectedFields: EsFieldName[];
|
||||
setFilterByIsTraining: Dispatch<SetStateAction<undefined | boolean>>;
|
||||
setPagination: Dispatch<SetStateAction<Pagination>>;
|
||||
setSearchQuery: Dispatch<SetStateAction<SavedSearchQuery>>;
|
||||
setSelectedFields: Dispatch<SetStateAction<EsFieldName[]>>;
|
||||
setSortingColumns: Dispatch<SetStateAction<EuiDataGridSorting['columns']>>;
|
||||
sortingColumns: EuiDataGridSorting['columns'];
|
||||
status: INDEX_STATUS;
|
||||
tableFields: string[];
|
||||
tableItems: TableItem[];
|
||||
}
|
||||
|
||||
type EsSorting = Dictionary<{
|
||||
order: 'asc' | 'desc';
|
||||
}>;
|
||||
|
||||
// The types specified in `@types/elasticsearch` are out of date and still have `total: number`.
|
||||
interface SearchResponse7 extends SearchResponse<any> {
|
||||
hits: SearchResponse<any>['hits'] & {
|
||||
total: {
|
||||
value: number;
|
||||
relation: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const useExploreData = (
|
||||
jobConfig: DataFrameAnalyticsConfig | undefined,
|
||||
needsDestIndexFields: boolean,
|
||||
selectedFields: Field[],
|
||||
setSelectedFields: React.Dispatch<React.SetStateAction<Field[]>>,
|
||||
setDocFields: React.Dispatch<React.SetStateAction<Field[]>>,
|
||||
setDepVarType: React.Dispatch<React.SetStateAction<ES_FIELD_TYPES | undefined>>
|
||||
jobConfig: DataFrameAnalyticsConfig,
|
||||
needsDestIndexFields: boolean
|
||||
): UseExploreDataReturnType => {
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [status, setStatus] = useState(INDEX_STATUS.UNUSED);
|
||||
|
||||
const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]);
|
||||
const [tableFields, setTableFields] = useState<string[]>([]);
|
||||
const [tableItems, setTableItems] = useState<TableItem[]>([]);
|
||||
const [sortField, setSortField] = useState<string>('');
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>(SORT_DIRECTION.ASC);
|
||||
const [fieldTypes, setFieldTypes] = useState<{ [key: string]: ES_FIELD_TYPES }>({});
|
||||
const [rowCount, setRowCount] = useState(0);
|
||||
|
||||
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 25 });
|
||||
const [searchQuery, setSearchQuery] = useState<SavedSearchQuery>(defaultSearchQuery);
|
||||
const [filterByIsTraining, setFilterByIsTraining] = useState<undefined | boolean>(undefined);
|
||||
const [sortingColumns, setSortingColumns] = useState<EuiDataGridSorting['columns']>([]);
|
||||
|
||||
const predictedFieldName = getPredictedFieldName(
|
||||
jobConfig.dest.results_field,
|
||||
jobConfig.analysis
|
||||
);
|
||||
const dependentVariable = getDependentVar(jobConfig.analysis);
|
||||
|
||||
const getDefaultSelectedFields = () => {
|
||||
const { fields } = newJobCapsService;
|
||||
|
||||
if (selectedFields.length === 0 && jobConfig !== undefined) {
|
||||
const {
|
||||
selectedFields: defaultSelected,
|
||||
docFields,
|
||||
depVarType,
|
||||
} = getDefaultFieldsFromJobCaps(fields, jobConfig, needsDestIndexFields);
|
||||
const { selectedFields: defaultSelected, docFields } = getDefaultFieldsFromJobCaps(
|
||||
fields,
|
||||
jobConfig,
|
||||
needsDestIndexFields
|
||||
);
|
||||
|
||||
setDepVarType(depVarType);
|
||||
setSelectedFields(defaultSelected);
|
||||
setDocFields(docFields);
|
||||
const types: { [key: string]: ES_FIELD_TYPES } = {};
|
||||
const allFields: string[] = [];
|
||||
|
||||
docFields.forEach(field => {
|
||||
types[field.id] = field.type;
|
||||
allFields.push(field.id);
|
||||
});
|
||||
|
||||
setFieldTypes(types);
|
||||
setSelectedFields(defaultSelected.map(field => field.id));
|
||||
setTableFields(allFields);
|
||||
}
|
||||
};
|
||||
|
||||
const loadExploreData = async ({
|
||||
field,
|
||||
direction,
|
||||
searchQuery,
|
||||
requiresKeyword,
|
||||
}: LoadClassificationExploreDataArg) => {
|
||||
filterByIsTraining: isTraining,
|
||||
searchQuery: incomingQuery,
|
||||
}: LoadExploreDataArg) => {
|
||||
if (jobConfig !== undefined) {
|
||||
setErrorMessage('');
|
||||
setStatus(INDEX_STATUS.LOADING);
|
||||
|
||||
try {
|
||||
const resultsField = jobConfig.dest.results_field;
|
||||
const searchQueryClone: ResultsSearchQuery = cloneDeep(searchQuery);
|
||||
const searchQueryClone: ResultsSearchQuery = cloneDeep(incomingQuery);
|
||||
let query: ResultsSearchQuery;
|
||||
const { pageIndex, pageSize } = pagination;
|
||||
// If filterByIsTraining is defined - add that in to the final query
|
||||
const trainingQuery =
|
||||
isTraining !== undefined
|
||||
? {
|
||||
term: { [`${resultsField}.is_training`]: { value: isTraining } },
|
||||
}
|
||||
: undefined;
|
||||
|
||||
if (JSON.stringify(searchQuery) === JSON.stringify(defaultSearchQuery)) {
|
||||
query = {
|
||||
if (JSON.stringify(incomingQuery) === JSON.stringify(defaultSearchQuery)) {
|
||||
const existsQuery = {
|
||||
exists: {
|
||||
field: resultsField,
|
||||
},
|
||||
};
|
||||
|
||||
query = {
|
||||
bool: {
|
||||
must: [existsQuery],
|
||||
},
|
||||
};
|
||||
|
||||
if (trainingQuery !== undefined && isResultsSearchBoolQuery(query)) {
|
||||
query.bool.must.push(trainingQuery);
|
||||
}
|
||||
} else if (isResultsSearchBoolQuery(searchQueryClone)) {
|
||||
if (searchQueryClone.bool.must === undefined) {
|
||||
searchQueryClone.bool.must = [];
|
||||
|
@ -121,33 +166,37 @@ export const useExploreData = (
|
|||
},
|
||||
});
|
||||
|
||||
if (trainingQuery !== undefined) {
|
||||
searchQueryClone.bool.must.push(trainingQuery);
|
||||
}
|
||||
|
||||
query = searchQueryClone;
|
||||
} else {
|
||||
query = searchQueryClone;
|
||||
}
|
||||
|
||||
const body: SearchQuery = {
|
||||
query,
|
||||
};
|
||||
const sort: EsSorting = sortingColumns
|
||||
.map(column => {
|
||||
const { id } = column;
|
||||
column.id = isKeywordAndTextType(id) ? `${id}.keyword` : id;
|
||||
return column;
|
||||
})
|
||||
.reduce((s, column) => {
|
||||
s[column.id] = { order: column.direction };
|
||||
return s;
|
||||
}, {} as EsSorting);
|
||||
|
||||
if (field !== undefined) {
|
||||
body.sort = [
|
||||
{
|
||||
[`${field}${requiresKeyword ? '.keyword' : ''}`]: {
|
||||
order: direction,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const resp: SearchResponse<any> = await ml.esSearch({
|
||||
const resp: SearchResponse7 = await ml.esSearch({
|
||||
index: jobConfig.dest.index,
|
||||
size: SEARCH_SIZE,
|
||||
body,
|
||||
body: {
|
||||
query,
|
||||
from: pageIndex * pageSize,
|
||||
size: pageSize,
|
||||
...(Object.keys(sort).length > 0 ? { sort } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
setSortField(field);
|
||||
setSortDirection(direction);
|
||||
setRowCount(resp.hits.total.value);
|
||||
|
||||
const docs = resp.hits.hits;
|
||||
|
||||
|
@ -199,10 +248,45 @@ export const useExploreData = (
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (jobConfig !== undefined) {
|
||||
getDefaultSelectedFields();
|
||||
}
|
||||
getDefaultSelectedFields();
|
||||
}, [jobConfig && jobConfig.id]);
|
||||
|
||||
return { errorMessage, loadExploreData, sortField, sortDirection, status, tableItems };
|
||||
// By default set sorting to descending on the prediction field (`<dependent_varible or prediction_field_name>_prediction`).
|
||||
useEffect(() => {
|
||||
const sortByField = isKeywordAndTextType(dependentVariable)
|
||||
? `${predictedFieldName}.keyword`
|
||||
: predictedFieldName;
|
||||
const direction = SORT_DIRECTION.DESC;
|
||||
|
||||
setSortingColumns([{ id: sortByField, direction }]);
|
||||
}, [jobConfig && jobConfig.id]);
|
||||
|
||||
useEffect(() => {
|
||||
loadExploreData({ filterByIsTraining, searchQuery });
|
||||
}, [
|
||||
filterByIsTraining,
|
||||
jobConfig && jobConfig.id,
|
||||
pagination,
|
||||
searchQuery,
|
||||
selectedFields,
|
||||
sortingColumns,
|
||||
]);
|
||||
|
||||
return {
|
||||
errorMessage,
|
||||
fieldTypes,
|
||||
pagination,
|
||||
searchQuery,
|
||||
selectedFields,
|
||||
rowCount,
|
||||
setFilterByIsTraining,
|
||||
setPagination,
|
||||
setSelectedFields,
|
||||
setSortingColumns,
|
||||
setSearchQuery,
|
||||
sortingColumns,
|
||||
status,
|
||||
tableItems,
|
||||
tableFields,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ import { Dictionary } from '../../../../../../../common/types/common';
|
|||
import { isKeywordAndTextType } from '../../../../common/fields';
|
||||
import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public';
|
||||
import {
|
||||
LoadRegressionExploreDataArg,
|
||||
LoadExploreDataArg,
|
||||
defaultSearchQuery,
|
||||
ResultsSearchQuery,
|
||||
isResultsSearchBoolQuery,
|
||||
|
@ -120,7 +120,7 @@ export const useExploreData = (
|
|||
const loadExploreData = async ({
|
||||
filterByIsTraining: isTraining,
|
||||
searchQuery: incomingQuery,
|
||||
}: LoadRegressionExploreDataArg) => {
|
||||
}: LoadExploreDataArg) => {
|
||||
if (jobConfig !== undefined) {
|
||||
setErrorMessage('');
|
||||
setStatus(INDEX_STATUS.LOADING);
|
||||
|
|
|
@ -9478,8 +9478,6 @@
|
|||
"xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle": "分類ジョブID {jobId}の評価",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.firstDocumentsShownHelpText": "予測がある最初の{searchSize}のドキュメントを示す",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent": "配列",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.indexArrayToolTipContent": "この配列ベースの列の完全なコンテンツは表示できません。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobCapsFetchError": "結果を取得できません。インデックスのフィールドデータの読み込み中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationFetchError": "結果を取得できません。ジョブ構成データの読み込み中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationNoResultsMessage": "結果が見つかりませんでした。",
|
||||
|
@ -9574,8 +9572,6 @@
|
|||
"xpack.ml.dataframe.analytics.regressionExploration.generalizationDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "一般化エラー",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.indexError": "インデックスデータの読み込み中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel": "テスト",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel": "トレーニング",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.jobCapsFetchError": "結果を取得できません。インデックスのフィールドデータの読み込み中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.jobConfigurationFetchError": "結果を取得できません。ジョブ構成データの読み込み中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "平均二乗エラー",
|
||||
|
@ -9587,9 +9583,6 @@
|
|||
"xpack.ml.dataframe.analytics.regressionExploration.queryParsingErrorMessage": "クエリをパースできません。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R の二乗",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.rSquaredTooltipContent": "適合度を表します。モデルによる観察された結果の複製の効果を測定します。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder": "例: 平均>0.5",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.selectColumnsAriaLabel": "列を選択",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle": "フィールドを選択",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.tableJobIdTitle": "回帰ジョブID {jobId}のデスティネーションインデックス",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.trainingDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "トレーニングエラー",
|
||||
|
|
|
@ -9481,8 +9481,6 @@
|
|||
"xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle": "分类作业 ID {jobId} 的评估",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.firstDocumentsShownHelpText": "正在显示有相关预测存在的前 {searchSize} 个文档",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent": "数组",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.indexArrayToolTipContent": "此基于数组的列的完整内容无法显示。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobCapsFetchError": "无法提取结果。加载索引的字段数据时发生错误。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationFetchError": "无法提取结果。加载作业配置数据时发生错误。",
|
||||
"xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationNoResultsMessage": "未找到结果。",
|
||||
|
@ -9577,8 +9575,6 @@
|
|||
"xpack.ml.dataframe.analytics.regressionExploration.generalizationDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "泛化误差",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.indexError": "加载索引数据时出错。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel": "测试",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel": "培训",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.jobCapsFetchError": "无法提取结果。加载索引的字段数据时发生错误。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.jobConfigurationFetchError": "无法提取结果。加载作业配置数据时发生错误。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "均方误差",
|
||||
|
@ -9590,9 +9586,6 @@
|
|||
"xpack.ml.dataframe.analytics.regressionExploration.queryParsingErrorMessage": "无法解析查询。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R 平方",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.rSquaredTooltipContent": "表示拟合优度。度量模型复制被观察结果的优良性。",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder": "例如 avg>0.5",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.selectColumnsAriaLabel": "选择列",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle": "选择字段",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.tableJobIdTitle": "回归作业 ID {jobId} 的目标索引",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.trainingDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估",
|
||||
"xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "训练误差",
|
||||
|
|
Loading…
Reference in a new issue