Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Add better UI support for runtime fields Transforms #90363

Merged
merged 44 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7dc3db9
[ML] Add RT support for transforms from index pattern
qn895 Feb 2, 2021
717eaae
[ML] Add support for cloned transform from api
qn895 Feb 3, 2021
008ceab
[ML] Add support for runtime pivot
qn895 Feb 3, 2021
b35e56a
[ML] Add support for api created runtime
qn895 Feb 4, 2021
52a47c3
[ML] Add preview for expanded row
qn895 Feb 4, 2021
e40e94b
[ML] Add runtime fields to dropdown options
qn895 Feb 4, 2021
f1c8052
[ML] Add runtime fields to latest
qn895 Feb 4, 2021
cc4831c
[ML] Fix duplicate columns
qn895 Feb 4, 2021
7995e66
[ML] Update types and test
qn895 Feb 4, 2021
f84ebf3
[ML] Add runtime mappings to index pattern on creation
qn895 Feb 5, 2021
0e1c429
[ML] Add callout to show unsupported fields in dfa
qn895 Feb 5, 2021
9018492
[ML] Update types to RuntimeField
qn895 Feb 5, 2021
f8f8bd5
Merge remote-tracking branch 'upstream/master' into ml-transforms-df-…
qn895 Feb 5, 2021
3cfed18
[ML] Fix runtime fields, remove runtime mappings, fix copy to console
qn895 Feb 8, 2021
b123f88
Merge remote-tracking branch 'upstream/master' into ml-transforms-df-…
qn895 Feb 8, 2021
5db60f1
[ML] Fix incompatible kbn field type
qn895 Feb 8, 2021
fb94187
[ML] Add advanced mappings editor
qn895 Feb 8, 2021
aee8a23
[ML] Add support for filter terms agg control
qn895 Feb 9, 2021
28281c4
[ML] Fix jest tests hanging
qn895 Feb 9, 2021
4f68ebf
[ML] Fix translations
qn895 Feb 9, 2021
e8bec3e
[ML] Fix over-sized buttons for filter range
qn895 Feb 10, 2021
e4e2fb0
Merge remote-tracking branch 'upstream/master' into ml-transforms-df-…
qn895 Feb 10, 2021
341aabb
[ML] Update runtime mappings schema
qn895 Feb 11, 2021
3d3935b
[ML] Update runtime mappings schema
qn895 Feb 11, 2021
08e635e
[ML] Use isRecord for object checks
qn895 Feb 14, 2021
8adf58f
[ML] Fix and more message
qn895 Feb 14, 2021
29778b2
[ML] Update schema to correctly match types
qn895 Feb 14, 2021
5cd5cd1
[ML] Update schema to correctly match types
qn895 Feb 14, 2021
ba04871
[ML] Fix pivot duplicates
qn895 Feb 14, 2021
211937a
Merge remote-tracking branch 'upstream/master' into ml-transforms-df-…
qn895 Feb 14, 2021
bc43513
[ML] Rename isRecord to isPopulatedObject
qn895 Feb 14, 2021
76c9c79
[ML] Remove fit-content
qn895 Feb 14, 2021
2bd6b3c
[ML] Update runtime field type to prevent potential conflicts
qn895 Feb 14, 2021
ad32ea3
Revert "[ML] Remove fit-content"
qn895 Feb 14, 2021
9323382
[ML] Remove misc comment
qn895 Feb 14, 2021
860c26f
[ML] Fix missing typeof
qn895 Feb 15, 2021
7b91ae0
[ML] Add sorts and constants
qn895 Feb 16, 2021
af2e8e4
Merge upstream/master into branch
qn895 Feb 16, 2021
07563e9
[ML] Add i18n to includedFields description
qn895 Feb 16, 2021
8712080
[ML] fix imports
qn895 Feb 16, 2021
4ef577c
Merge upstream/master into branch
qn895 Feb 16, 2021
04219b2
[ML] Only pass runtime mappings if it's latest
qn895 Feb 16, 2021
f23a747
[ML] Fix functional tests
qn895 Feb 16, 2021
f77f6a6
Merge remote-tracking branch 'upstream/master' into ml-transforms-df-…
qn895 Feb 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { getNestedProperty } from '../../util/object_utils';
import { mlFieldFormatService } from '../../services/field_format_service';

import { DataGridItem, IndexPagination, RenderCellValue } from './types';
import type { RuntimeField } from '../../../../../../../src/plugins/data/common/index_patterns';

export const INIT_MAX_COLUMNS = 10;

Expand Down Expand Up @@ -82,6 +83,37 @@ export const getFieldsFromKibanaIndexPattern = (indexPattern: IndexPattern): str
return indexPatternFields;
};

/**
* Return a map of runtime_mappings for each of the index pattern field provided
* to provide in ES search queries
* @param indexPatternFields
* @param indexPattern
* @param clonedRuntimeMappings
*/
export const getRuntimeFieldsMapping = (
indexPatternFields: string[],
indexPattern: IndexPattern,
clonedRuntimeMappings?: { [key: string]: RuntimeField }
) => {
if (!Array.isArray(indexPatternFields) || indexPattern === undefined) return {};
darnautov marked this conversation as resolved.
Show resolved Hide resolved
const ipRuntimeMappings = indexPattern.getComputedFields().runtimeFields;
let combinedRuntimeMappings: { [ipField: string]: any } = {};
darnautov marked this conversation as resolved.
Show resolved Hide resolved

if (typeof ipRuntimeMappings === 'object' && Object.keys(ipRuntimeMappings).length > 0) {
indexPatternFields.forEach((ipField) => {
if (ipRuntimeMappings.hasOwnProperty(ipField)) {
combinedRuntimeMappings[ipField] = ipRuntimeMappings[ipField];
}
});
}
if (typeof clonedRuntimeMappings === 'object') {
darnautov marked this conversation as resolved.
Show resolved Hide resolved
combinedRuntimeMappings = { ...combinedRuntimeMappings, ...clonedRuntimeMappings };
}
return Object.keys(combinedRuntimeMappings).length > 0
? { runtime_mappings: combinedRuntimeMappings }
: {};
};

export interface FieldTypes {
[key: string]: ES_FIELD_TYPES;
}
Expand Down Expand Up @@ -131,6 +163,45 @@ export const getDataGridSchemasFromFieldTypes = (fieldTypes: FieldTypes, results
};

export const NON_AGGREGATABLE = 'non-aggregatable';

export const getDataGridSchemaFromESFieldType = (
fieldType: ES_FIELD_TYPES | undefined | RuntimeField['type']
): string | undefined => {
// Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json']
// To fall back to the default string schema it needs to be undefined.
let schema;

switch (fieldType) {
case ES_FIELD_TYPES.GEO_POINT:
case ES_FIELD_TYPES.GEO_SHAPE:
schema = 'json';
break;
case ES_FIELD_TYPES.BOOLEAN:
schema = 'boolean';
break;
case ES_FIELD_TYPES.DATE:
case ES_FIELD_TYPES.DATE_NANOS:
schema = 'datetime';
break;
case ES_FIELD_TYPES.BYTE:
case ES_FIELD_TYPES.DOUBLE:
case ES_FIELD_TYPES.FLOAT:
case ES_FIELD_TYPES.HALF_FLOAT:
case ES_FIELD_TYPES.INTEGER:
case ES_FIELD_TYPES.LONG:
case ES_FIELD_TYPES.SCALED_FLOAT:
case ES_FIELD_TYPES.SHORT:
schema = 'numeric';
break;
// keep schema undefined for text based columns
case ES_FIELD_TYPES.KEYWORD:
case ES_FIELD_TYPES.TEXT:
break;
}

return schema;
};

export const getDataGridSchemaFromKibanaFieldType = (
field: IFieldType | undefined
): string | undefined => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

export {
getDataGridSchemasFromFieldTypes,
getDataGridSchemaFromESFieldType,
getDataGridSchemaFromKibanaFieldType,
getFieldsFromKibanaIndexPattern,
getRuntimeFieldsMapping,
multiColumnSortFactory,
showDataGridColumnChartErrorMessageToast,
useRenderCellValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import React, { FC, Fragment, useEffect, useMemo, useRef, useState } from 'react';
import {
EuiBadge,
EuiCallOut,
EuiComboBox,
EuiComboBoxOptionOption,
EuiFormRow,
Expand All @@ -19,6 +20,7 @@ import {
import { i18n } from '@kbn/i18n';
import { debounce } from 'lodash';

import { FormattedMessage } from '@kbn/i18n/react';
import { newJobCapsService } from '../../../../../services/new_job_capabilities_service';
import { useMlContext } from '../../../../../contexts/ml';

Expand Down Expand Up @@ -314,6 +316,15 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
};
}, [jobType, dependentVariable, trainingPercent, JSON.stringify(includes), jobConfigQueryString]);

const unsupportedRuntimeFields = useMemo(
() =>
currentIndexPattern.fields
.getAll()
.filter((f) => f.runtimeField)
.map((f) => `'${f.displayName}'`),
[currentIndexPattern.fields]
);

return (
<Fragment>
<Messages messages={requestMessages} />
Expand Down Expand Up @@ -445,6 +456,26 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
>
<Fragment />
</EuiFormRow>
{unsupportedRuntimeFields && unsupportedRuntimeFields.length > 0 && (
<>
<EuiCallOut size="s" color="warning">
<FormattedMessage
id="xpack.ml.dataframe.analytics.create.unsupportedRuntimeFields"
defaultMessage="The runtime fields {unsupportedRuntimeFields} are not supported for analysis."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case only 1 runtime field is unsupported, the sentence should be alternated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated here 8adf58f

values={{
unsupportedRuntimeFields:
unsupportedRuntimeFields.length > 5
? `${unsupportedRuntimeFields.slice(0, 5).join(', ')} ... (and ${
unsupportedRuntimeFields.length
} more)`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You set a string dynamically here. It should be part of the defaultMessage instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated here 8adf58f

: unsupportedRuntimeFields.join(', '),
}}
/>
</EuiCallOut>
<EuiSpacer />
</>
)}

<AnalysisFieldsTable
dependentVariable={dependentVariable}
includes={includes}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import type { SearchResponse7 } from '../../../../../../common/types/es_client';
import { extractErrorMessage } from '../../../../../../common/util/errors';
import { INDEX_STATUS } from '../../../common/analytics';
import { ml } from '../../../../services/ml_api_service';
import { getRuntimeFieldsMapping } from '../../../../components/data_grid/common';

type IndexSearchResponse = SearchResponse7;

Expand All @@ -38,7 +39,9 @@ export const useIndexData = (
query: any,
toastNotifications: CoreSetup['notifications']['toasts']
): UseIndexDataReturnType => {
const indexPatternFields = getFieldsFromKibanaIndexPattern(indexPattern);
const indexPatternFields = useMemo(() => getFieldsFromKibanaIndexPattern(indexPattern), [
indexPattern,
]);

// EuiDataGrid State
const columns: EuiDataGridColumn[] = [
Expand Down Expand Up @@ -75,7 +78,6 @@ export const useIndexData = (
s[column.id] = { order: column.direction };
return s;
}, {} as EsSorting);

const esSearchRequest = {
index: indexPattern.title,
body: {
Expand All @@ -86,6 +88,7 @@ export const useIndexData = (
fields: ['*'],
_source: false,
...(Object.keys(sort).length > 0 ? { sort } : {}),
...getRuntimeFieldsMapping(indexPatternFields, indexPattern),
},
};

Expand All @@ -105,7 +108,7 @@ export const useIndexData = (
useEffect(() => {
getIndexData();
// custom comparison
}, [indexPattern.title, JSON.stringify([query, pagination, sortingColumns])]);
}, [indexPattern.title, indexPatternFields, JSON.stringify([query, pagination, sortingColumns])]);

const dataLoader = useMemo(() => new DataLoader(indexPattern, toastNotifications), [
indexPattern,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/transform/common/api_schemas/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const settingsSchema = schema.object({
});

export const sourceSchema = schema.object({
runtime_mappings: schema.maybe(schema.recordOf(schema.string(), schema.any())),
darnautov marked this conversation as resolved.
Show resolved Hide resolved
index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]),
query: schema.maybe(schema.recordOf(schema.string(), schema.any())),
});
Expand Down
33 changes: 31 additions & 2 deletions x-pack/plugins/transform/public/app/common/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,45 @@ export function isDefaultQuery(query: PivotQuery): boolean {
return isSimpleQuery(query) && query.query_string.query === '*';
}

export function getCombinedRuntimeMappings(
indexPattern: IndexPattern | undefined,
runtimeMappings?: StepDefineExposedState['runtimeMappings']
): StepDefineExposedState['runtimeMappings'] | undefined {
let combinedRuntimeMappings = {};

// Use runtime field mappings defined inline from API
if (typeof runtimeMappings === 'object') {
combinedRuntimeMappings = { ...combinedRuntimeMappings, ...runtimeMappings };
}
darnautov marked this conversation as resolved.
Show resolved Hide resolved

// And runtime field mappings defined by index pattern
if (indexPattern !== undefined) {
const ipRuntimeMappings = indexPattern.getComputedFields().runtimeFields;
combinedRuntimeMappings = { ...combinedRuntimeMappings, ...ipRuntimeMappings };
}

if (
typeof combinedRuntimeMappings === 'object' &&
Object.keys(combinedRuntimeMappings).length > 0
darnautov marked this conversation as resolved.
Show resolved Hide resolved
) {
return combinedRuntimeMappings;
}
return undefined;
}

export function getPreviewTransformRequestBody(
indexPatternTitle: IndexPattern['title'],
query: PivotQuery,
partialRequest?: StepDefineExposedState['previewRequest'] | undefined
partialRequest?: StepDefineExposedState['previewRequest'] | undefined,
runtimeMappings?: object
): PostTransformsPreviewRequestSchema {
const index = indexPatternTitle.split(',').map((name: string) => name.trim());

return {
source: {
index,
...(!isDefaultQuery(query) && !isMatchAllQuery(query) ? { query } : {}),
...(typeof runtimeMappings === 'object' ? { runtime_mappings: runtimeMappings } : {}),
},
...(partialRequest ?? {}),
};
Expand Down Expand Up @@ -95,7 +123,8 @@ export const getCreateTransformRequestBody = (
...getPreviewTransformRequestBody(
indexPatternTitle,
getPivotQuery(pivotState.searchQuery),
pivotState.previewRequest
pivotState.previewRequest,
pivotState.runtimeMappings
),
// conditionally add optional description
...(transformDetailsState.transformDescription !== ''
Expand Down
61 changes: 49 additions & 12 deletions x-pack/plugins/transform/public/app/hooks/use_index_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { useEffect } from 'react';
import { useEffect, useMemo } from 'react';

import { EuiDataGridColumn } from '@elastic/eui';

Expand All @@ -21,17 +21,20 @@ import { SearchItems } from './use_search_items';
import { useApi } from './use_api';

import { useAppDependencies, useToastNotifications } from '../app_dependencies';
import type { StepDefineExposedState } from '../sections/create_transform/components/step_define/common';

export const useIndexData = (
indexPattern: SearchItems['indexPattern'],
query: PivotQuery
query: PivotQuery,
combinedRuntimeMappings?: StepDefineExposedState['runtimeMappings']
): UseIndexDataReturnType => {
const api = useApi();
const toastNotifications = useToastNotifications();
const {
ml: {
getFieldType,
getDataGridSchemaFromKibanaFieldType,
getDataGridSchemaFromESFieldType,
getFieldsFromKibanaIndexPattern,
showDataGridColumnChartErrorMessageToast,
useDataGrid,
Expand All @@ -41,16 +44,42 @@ export const useIndexData = (
},
} = useAppDependencies();

const indexPatternFields = getFieldsFromKibanaIndexPattern(indexPattern);
const indexPatternFields = useMemo(() => getFieldsFromKibanaIndexPattern(indexPattern), [
indexPattern,
getFieldsFromKibanaIndexPattern,
]);

const columns: EuiDataGridColumn[] = useMemo(() => {
let result: Array<{ id: string; schema: string | undefined }> = [];

// Get the the runtime fields that are defined from API field and index patterns
if (combinedRuntimeMappings !== undefined) {
result = Object.keys(combinedRuntimeMappings).map((fieldName) => {
const field = combinedRuntimeMappings[fieldName];
const schema = getDataGridSchemaFromESFieldType(field.type);
return { id: fieldName, schema };
});
}

// EuiDataGrid State
const columns: EuiDataGridColumn[] = [
...indexPatternFields.map((id) => {
// Combine the runtime field that are defined from API field
indexPatternFields.forEach((id) => {
const field = indexPattern.fields.getByName(id);
const schema = getDataGridSchemaFromKibanaFieldType(field);
return { id, schema };
}),
];
if (!field?.runtimeField) {
const schema = getDataGridSchemaFromKibanaFieldType(field);
result.push({ id, schema });
}
});

return result.sort((a, b) => a.id.localeCompare(b.id));
}, [
indexPatternFields,
indexPattern.fields,
combinedRuntimeMappings,
getDataGridSchemaFromESFieldType,
getDataGridSchemaFromKibanaFieldType,
]);

// EuiDataGrid State

const dataGrid = useDataGrid(columns);

Expand Down Expand Up @@ -92,9 +121,12 @@ export const useIndexData = (
from: pagination.pageIndex * pagination.pageSize,
size: pagination.pageSize,
...(Object.keys(sort).length > 0 ? { sort } : {}),
...(typeof combinedRuntimeMappings === 'object' &&
Object.keys(combinedRuntimeMappings).length > 0
? { runtime_mappings: combinedRuntimeMappings }
: {}),
},
};

const resp = await api.esSearch(esSearchRequest);

if (!isEsSearchResponse(resp)) {
Expand Down Expand Up @@ -134,7 +166,12 @@ export const useIndexData = (
fetchDataGridData();
// custom comparison
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [indexPattern.title, JSON.stringify([query, pagination, sortingColumns])]);
}, [
indexPattern.title,
indexPatternFields,
// eslint-disable-next-line react-hooks/exhaustive-deps
JSON.stringify([query, pagination, sortingColumns, combinedRuntimeMappings]),
]);

useEffect(() => {
if (chartsVisible) {
Expand Down
Loading