[ML] Transform: Adds ability to create index pattern time field when creating transform (#68842) (#68931)

This commit is contained in:
Quynh Nguyen 2020-06-11 15:44:31 -05:00 committed by GitHub
parent 0d78bfcf22
commit 0d0ea7030c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 9 deletions

View file

@ -59,11 +59,12 @@ interface Props {
transformId: string;
transformConfig: any;
overrides: StepDetailsExposedState;
timeFieldName?: string | undefined;
onChange(s: StepDetailsExposedState): void;
}
export const StepCreateForm: FC<Props> = React.memo(
({ createIndexPattern, transformConfig, transformId, onChange, overrides }) => {
({ createIndexPattern, transformConfig, transformId, onChange, overrides, timeFieldName }) => {
const defaults = { ...getDefaultStepCreateState(), ...overrides };
const [redirectToTransformManagement, setRedirectToTransformManagement] = useState(false);
@ -187,8 +188,8 @@ export const StepCreateForm: FC<Props> = React.memo(
Object.assign(newIndexPattern, {
id: '',
title: indexPatternName,
timeFieldName,
});
const id = await newIndexPattern.create();
await indexPatterns.clearCache();

View file

@ -23,10 +23,17 @@ import { ToastNotificationText } from '../../../../components';
import { useDocumentationLinks } from '../../../../hooks/use_documentation_links';
import { SearchItems } from '../../../../hooks/use_search_items';
import { useApi } from '../../../../hooks/use_api';
import { isTransformIdValid, TransformPivotConfig } from '../../../../common';
import { StepDetailsTimeField } from './step_details_time_field';
import {
getPivotQuery,
getPreviewRequestBody,
isTransformIdValid,
TransformPivotConfig,
} from '../../../../common';
import { EsIndexName, IndexPatternTitle } from './common';
import { delayValidator } from '../../../../common/validators';
import { StepDefineExposedState } from '../step_define/common';
import { dictionaryToArray } from '../../../../../../common/types/common';
export interface StepDetailsExposedState {
continuousModeDateField: string;
@ -38,6 +45,7 @@ export interface StepDetailsExposedState {
transformId: TransformId;
transformDescription: string;
valid: boolean;
indexPatternDateField?: string | undefined;
}
export function getDefaultStepDetailsState(): StepDetailsExposedState {
@ -51,6 +59,7 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState {
destinationIndex: '',
touched: false,
valid: false,
indexPatternDateField: undefined,
};
}
@ -74,10 +83,11 @@ interface Props {
overrides?: StepDetailsExposedState;
onChange(s: StepDetailsExposedState): void;
searchItems: SearchItems;
stepDefineState: StepDefineExposedState;
}
export const StepDetailsForm: FC<Props> = React.memo(
({ overrides = {}, onChange, searchItems }) => {
({ overrides = {}, onChange, searchItems, stepDefineState }) => {
const deps = useAppDependencies();
const toastNotifications = useToastNotifications();
const { esIndicesCreateIndex } = useDocumentationLinks();
@ -93,8 +103,28 @@ export const StepDetailsForm: FC<Props> = React.memo(
);
const [transformIds, setTransformIds] = useState<TransformId[]>([]);
const [indexNames, setIndexNames] = useState<EsIndexName[]>([]);
// Index pattern state
const [indexPatternTitles, setIndexPatternTitles] = useState<IndexPatternTitle[]>([]);
const [createIndexPattern, setCreateIndexPattern] = useState(defaults.createIndexPattern);
const [previewDateColumns, setPreviewDateColumns] = useState<string[]>([]);
const [indexPatternDateField, setIndexPatternDateField] = useState<string | undefined>();
const onTimeFieldChanged = React.useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const value = e.target.value;
// If the value is an empty string, it's not a valid selection
if (value === '') {
return;
}
// Find the time field based on the selected value
// this is to account for undefined when user chooses not to use a date field
const timeField = previewDateColumns.find((col) => col === value);
setIndexPatternDateField(timeField);
},
[setIndexPatternDateField, previewDateColumns]
);
// Continuous mode state
const [isContinuousModeEnabled, setContinuousModeEnabled] = useState(
@ -107,6 +137,37 @@ export const StepDetailsForm: FC<Props> = React.memo(
useEffect(() => {
// use an IIFE to avoid returning a Promise to useEffect.
(async function () {
try {
const { searchQuery, groupByList, aggList } = stepDefineState;
const pivotAggsArr = dictionaryToArray(aggList);
const pivotGroupByArr = dictionaryToArray(groupByList);
const pivotQuery = getPivotQuery(searchQuery);
const previewRequest = getPreviewRequestBody(
searchItems.indexPattern.title,
pivotQuery,
pivotGroupByArr,
pivotAggsArr
);
const transformPreview = await api.getTransformsPreview(previewRequest);
const properties = transformPreview.generated_dest_index.mappings.properties;
const datetimeColumns: string[] = Object.keys(properties).filter(
(col) => properties[col].type === 'date'
);
setPreviewDateColumns(datetimeColumns);
setIndexPatternDateField(datetimeColumns[0]);
} catch (e) {
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformPreview', {
defaultMessage: 'An error occurred getting transform preview',
}),
text: toMountPoint(
<ToastNotificationText overlays={deps.overlays} text={getErrorMessage(e)} />
),
});
}
try {
setTransformIds(
(await api.getTransforms()).transforms.map(
@ -198,6 +259,7 @@ export const StepDetailsForm: FC<Props> = React.memo(
destinationIndex,
touched: true,
valid,
indexPatternDateField,
});
// custom comparison
/* eslint-disable react-hooks/exhaustive-deps */
@ -210,6 +272,7 @@ export const StepDetailsForm: FC<Props> = React.memo(
transformDescription,
destinationIndex,
valid,
indexPatternDateField,
/* eslint-enable react-hooks/exhaustive-deps */
]);
@ -318,6 +381,7 @@ export const StepDetailsForm: FC<Props> = React.memo(
data-test-subj="transformDestinationIndexInput"
/>
</EuiFormRow>
<EuiFormRow
isInvalid={createIndexPattern && indexPatternTitleExists}
error={
@ -339,6 +403,13 @@ export const StepDetailsForm: FC<Props> = React.memo(
data-test-subj="transformCreateIndexPatternSwitch"
/>
</EuiFormRow>
{createIndexPattern && !indexPatternTitleExists && previewDateColumns.length > 0 && (
<StepDetailsTimeField
previewDateColumns={previewDateColumns}
indexPatternDateField={indexPatternDateField}
onTimeFieldChanged={onTimeFieldChanged}
/>
)}
<EuiFormRow
helpText={
isContinuousModeAvailable === false
@ -378,7 +449,7 @@ export const StepDetailsForm: FC<Props> = React.memo(
)}
>
<EuiSelect
options={dateFieldNames.map((text) => ({ text }))}
options={dateFieldNames.map((text: string) => ({ text }))}
value={continuousModeDateField}
onChange={(e) => setContinuousModeDateField(e.target.value)}
data-test-subj="transformContinuousDateFieldSelect"

View file

@ -8,7 +8,7 @@ import React, { FC } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFieldText, EuiFormRow } from '@elastic/eui';
import { EuiFieldText, EuiFormRow, EuiSelect } from '@elastic/eui';
import { StepDetailsExposedState } from './step_details_form';
@ -21,6 +21,7 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo(
transformDescription,
destinationIndex,
touched,
indexPatternDateField,
}) => {
if (touched === false) {
return null;
@ -56,6 +57,21 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo(
>
<EuiFieldText defaultValue={destinationIndex} disabled={true} />
</EuiFormRow>
<EuiFormRow
helpText={i18n.translate(
'xpack.transform.stepDetailsSummary.indexPatternTimeFilterLabel',
{
defaultMessage: 'Time filter',
}
)}
>
<EuiSelect
options={[{ text: indexPatternDateField }]}
value={indexPatternDateField}
disabled={true}
/>
</EuiFormRow>
{isContinuousModeEnabled && (
<EuiFormRow
label={i18n.translate(

View file

@ -0,0 +1,68 @@
/*
* 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, { FC } from 'react';
import { EuiFormRow, EuiSelect } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
interface Props {
previewDateColumns: string[];
indexPatternDateField: string | undefined;
onTimeFieldChanged: (e: React.ChangeEvent<HTMLSelectElement>) => void;
}
export const StepDetailsTimeField: FC<Props> = ({
previewDateColumns,
indexPatternDateField,
onTimeFieldChanged,
}) => {
const noTimeFieldLabel = i18n.translate(
'xpack.transform.stepDetailsForm.noTimeFieldOptionLabel',
{
defaultMessage: "I don't want to use the Time Filter",
}
);
const noTimeFieldOption = {
text: noTimeFieldLabel,
value: undefined,
};
const disabledDividerOption = {
disabled: true,
text: '───',
value: '',
};
return (
<EuiFormRow
label={
<FormattedMessage
id="xpack.transform.stepDetailsForm.indexPatternTimeFilterLabel"
defaultMessage="Time Filter field name"
/>
}
helpText={
<FormattedMessage
id="xpack.transform.stepDetailsForm.indexPatternTimeFilterHelpText"
defaultMessage="The Time Filter will use this field to filter your data by time. You can choose not to have a time field, but you will not be able to narrow down your data by a time range."
/>
}
>
<EuiSelect
options={[
...previewDateColumns.map((text) => ({ text })),
disabledDividerOption,
noTimeFieldOption,
]}
value={indexPatternDateField}
onChange={onTimeFieldChanged}
data-test-subj="transformIndexPatternDateFieldSelect"
/>
</EuiFormRow>
);
};

View file

@ -91,6 +91,8 @@ export const CreateTransformWizardContext = createContext<{ indexPattern: IndexP
});
export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems }) => {
const { indexPattern } = searchItems;
// The current WIZARD_STEP
const [currentStep, setCurrentStep] = useState(WIZARD_STEPS.DEFINE);
@ -110,6 +112,7 @@ export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems })
onChange={setStepDetailsState}
overrides={stepDetailsState}
searchItems={searchItems}
stepDefineState={stepDefineState}
/>
) : (
<StepDetailsSummary {...stepDetailsState} />
@ -146,8 +149,6 @@ export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems })
}
}, []);
const { indexPattern } = searchItems;
const transformConfig = getCreateRequestBody(
indexPattern.title,
stepDefineState,
@ -162,6 +163,7 @@ export const Wizard: FC<WizardProps> = React.memo(({ cloneConfig, searchItems })
transformConfig={transformConfig}
onChange={setStepCreateState}
overrides={stepCreateState}
timeFieldName={stepDetailsState.indexPatternDateField}
/>
) : (
<StepCreateSummary />