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] Adding ability to override number of sample lines in File Data Visualizer #29214

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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 @@ -29,6 +29,9 @@ export class EditFlyout extends Component {
super(props);

this.applyOverrides = () => {};
this.state = {
overridesValid: true
};
}

applyAndClose = () => {
Expand All @@ -42,10 +45,14 @@ export class EditFlyout extends Component {
unsetApplyOverrides = () => {
this.applyOverrides = () => {};
}
setOverridesValid = (overridesValid) => {
this.setState({ overridesValid });
}

render() {
const { isFlyoutVisible, closeEditFlyout } = this.props;
const {
isFlyoutVisible,
closeEditFlyout,
setOverrides,
overrides,
originalSettings,
Expand Down Expand Up @@ -78,6 +85,7 @@ export class EditFlyout extends Component {
overrides={overrides}
originalSettings={originalSettings}
setApplyOverrides={this.setApplyOverrides}
setOverridesValid={this.setOverridesValid}
fields={fields}
/>

Expand Down Expand Up @@ -105,8 +113,8 @@ export class EditFlyout extends Component {
<EuiFlexItem grow={false}>
<EuiButton
onClick={this.applyAndClose}
isDisabled={(this.state.overridesValid === false)}
fill
// isDisabled={(isValidJobDetails === false) || (isValidJobCustomUrls === false)}
>
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.applyOverrideSettingsButtonLabel"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import React, {
Component,
} from 'react';
Expand All @@ -19,6 +20,7 @@ import {
EuiSpacer,
EuiTitle,
EuiTextArea,
EuiFieldNumber,
} from '@elastic/eui';

import {
Expand All @@ -35,13 +37,24 @@ const delimiterOptions = getDelimiterOptions();
const quoteOptions = getQuoteOptions();
// const charsetOptions = getCharsetOptions();

const LINES_TO_SAMPLE_VALUE_MIN = 3;
const LINES_TO_SAMPLE_VALUE_MAX = 1000000;

export class Overrides extends Component {
constructor(props) {
super(props);

this.state = {};
}

linesToSampleErrors = i18n.translate('xpack.ml.fileDatavisualizer.editFlyout.overrides.linesToSampleErrorMessage', {
defaultMessage: 'Value must be greater than {min} and less than or equal to {max}',
values: {
min: LINES_TO_SAMPLE_VALUE_MIN,
max: LINES_TO_SAMPLE_VALUE_MAX,
}
});

static getDerivedStateFromProps(props, state) {
const { originalSettings } = props;

Expand All @@ -56,6 +69,7 @@ export class Overrides extends Component {
grokPattern,
timestampField,
timestampFormat,
linesToSample,
} = props.overrides;

const {
Expand All @@ -68,22 +82,27 @@ export class Overrides extends Component {
originalColumnNames
} = getColumnNames(columnNames, originalSettings);

const initialState = {
const overrides = {
charset: (charset === undefined) ? originalSettings.charset : charset,
format: (format === undefined) ? originalSettings.format : format,
hasHeaderRow: (hasHeaderRow === undefined) ? originalSettings.hasHeaderRow : hasHeaderRow,
columnNames: newColumnNames,
originalColumnNames,
delimiter: d,
customDelimiter: (customD === undefined) ? '' : customD,
quote: (quote === undefined) ? originalSettings.quote : quote,
shouldTrimFields: (shouldTrimFields === undefined) ? originalSettings.shouldTrimFields : shouldTrimFields,
grokPattern: (grokPattern === undefined) ? originalSettings.grokPattern : grokPattern,
timestampFormat: (timestampFormat === undefined) ? originalSettings.timestampFormat : timestampFormat,
timestampField: (timestampField === undefined) ? originalSettings.timestampField : timestampField,
linesToSample: (linesToSample === undefined) ? originalSettings.linesToSample : +linesToSample,
};

return { ...initialState, ...state };
return {
originalColumnNames,
customDelimiter: (customD === undefined) ? '' : customD,
linesToSampleValid: true,
overrides,
...state,
};
}

componentDidMount() {
Expand All @@ -99,83 +118,122 @@ export class Overrides extends Component {
}

applyOverrides = () => {
const overrides = { ...this.state };
overrides.delimiter = convertDelimiterBack(overrides);
delete overrides.customDelimiter;
delete overrides.originalColumnNames;
const overrides = { ...this.state.overrides };
overrides.delimiter = convertDelimiterBack(overrides.delimiter, this.state.customDelimiter);

this.props.setOverrides(overrides);
}

setOverride(o) {
const overrides = { ...this.state.overrides, ...o };
this.setState({ overrides });
}

onFormatChange = (format) => {
this.setState({ format });
this.setOverride({ format });
}

onTimestampFormatChange = (timestampFormat) => {
this.setState({ timestampFormat });
this.setOverride({ timestampFormat });
}

onTimestampFieldChange = (timestampField) => {
this.setState({ timestampField });
this.setOverride({ timestampField });
}

onDelimiterChange = (delimiter) => {
this.setState({ delimiter });
this.setOverride({ delimiter });
}

onCustomDelimiterChange = (e) => {
this.setState({ customDelimiter: e.target.value });
}

onQuoteChange = (quote) => {
this.setState({ quote });
this.setOverride({ quote });
}

onHasHeaderRowChange = (e) => {
this.setState({ hasHeaderRow: e.target.checked });
this.setOverride({ hasHeaderRow: e.target.checked });
}

onShouldTrimFieldsChange = (e) => {
this.setState({ shouldTrimFields: e.target.checked });
this.setOverride({ shouldTrimFields: e.target.checked });
}

onCharsetChange = (charset) => {
this.setState({ charset });
this.setOverride({ charset });
}

onColumnNameChange = (e, i) => {
const columnNames = this.state.columnNames;
const columnNames = this.state.overrides.columnNames;
columnNames[i] = e.target.value;
this.setState({ columnNames });
this.setOverride({ columnNames });
}

grokPatternChange = (e) => {
this.setState({ grokPattern: e.target.value });
this.setOverride({ grokPattern: e.target.value });
}

onLinesToSampleChange = (e) => {
const linesToSample = +e.target.value;
this.setOverride({ linesToSample });

// check whether the value is valid and set that to state.
const linesToSampleValid = isLinesToSampleValid(linesToSample);
this.setState({ linesToSampleValid });

// set the overrides valid setting in the parent component,
// used to disable the Apply button if any of the overrides are invalid
this.props.setOverridesValid(linesToSampleValid);
}


render() {
const { fields } = this.props;
const {
customDelimiter,
originalColumnNames,
linesToSampleValid,
overrides,
} = this.state;

const {
timestampFormat,
timestampField,
format,
delimiter,
customDelimiter,
quote,
hasHeaderRow,
shouldTrimFields,
// charset,
columnNames,
originalColumnNames,
grokPattern,
} = this.state;
linesToSample,
} = overrides;

const fieldOptions = fields.map(f => ({ value: f, inputDisplay: f }));

return (

<EuiForm>
<EuiFormRow
error={this.linesToSampleErrors}
isInvalid={(linesToSampleValid === false)}
label={
<FormattedMessage
id="xpack.ml.fileDatavisualizer.editFlyout.overrides.linesToSampleFormRowLabel"
defaultMessage="Number of lines to sample"
/>
}
>
<EuiFieldNumber
value={linesToSample}
onChange={this.onLinesToSampleChange}
isInvalid={(linesToSampleValid === false)}
/>
</EuiFormRow>

<EuiFormRow
label={
<FormattedMessage
Expand All @@ -191,7 +249,7 @@ export class Overrides extends Component {
/>
</EuiFormRow>
{
(this.state.format === 'delimited') &&
(format === 'delimited') &&
<React.Fragment>
<EuiFormRow
label={
Expand Down Expand Up @@ -271,7 +329,7 @@ export class Overrides extends Component {
</React.Fragment>
}
{
(this.state.format === 'semi_structured_text') &&
(format === 'semi_structured_text') &&
<React.Fragment>
<EuiFormRow
label={
Expand Down Expand Up @@ -329,7 +387,7 @@ export class Overrides extends Component {
/>
</EuiFormRow> */}
{
(this.state.format === 'delimited' && originalColumnNames.length > 0) &&
(format === 'delimited' && originalColumnNames.length > 0) &&

<React.Fragment>
<EuiSpacer />
Expand Down Expand Up @@ -398,7 +456,7 @@ function convertDelimiter(d) {
}

// Convert the delimiter textual descriptions back to their real characters.
function convertDelimiterBack({ delimiter, customDelimiter }) {
function convertDelimiterBack(delimiter, customDelimiter) {
switch (delimiter) {
case 'comma':
return ',';
Expand Down Expand Up @@ -429,3 +487,7 @@ function getColumnNames(columnNames, originalSettings) {
originalColumnNames,
};
}

function isLinesToSampleValid(linesToSample) {
return (linesToSample > LINES_TO_SAMPLE_VALUE_MIN && linesToSample <= LINES_TO_SAMPLE_VALUE_MAX);
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export class FileDataVisualizerView extends Component {
}

if (serverOverrides === undefined) {
// if no overrides were used, store all the settings returned from the endpoint
this.originalSettings = serverSettings;
} else {
Object.keys(serverOverrides).forEach((o) => {
Expand All @@ -164,7 +165,7 @@ export class FileDataVisualizerView extends Component {
Object.keys(serverSettings).forEach((o) => {
const value = serverSettings[o];
if (
this.overrides[o] === undefined &&
(this.overrides[o] === undefined) &&
(Array.isArray(value) && (isEqual(value, this.originalSettings[o]) === false) ||
(value !== this.originalSettings[o]))
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

export const DEFAULT_LINES_TO_SAMPLE = 1000;

export const overrideDefaults = {
timestampFormat: undefined,
Expand All @@ -16,4 +17,5 @@ export const overrideDefaults = {
columnNames: undefined,
shouldTrimFields: undefined,
grokPattern: undefined,
linesToSample: undefined,
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { overrideDefaults } from './overrides';
import { overrideDefaults, DEFAULT_LINES_TO_SAMPLE } from './overrides';
import { isEqual } from 'lodash';
import { ml } from '../../../services/ml_api_service';

Expand Down Expand Up @@ -86,13 +86,21 @@ export function createUrlOverrides(overrides, originalSettings) {
if (formattedOverrides.grok_pattern !== '') {
formattedOverrides.grok_pattern = encodeURIComponent(formattedOverrides.grok_pattern);
}

if (formattedOverrides.lines_to_sample === '') {
formattedOverrides.lines_to_sample = overrides.linesToSample;
}

return formattedOverrides;
}

export function processResults(results) {
const timestampFormat = (results.joda_timestamp_formats !== undefined && results.joda_timestamp_formats.length) ?
results.joda_timestamp_formats[0] : undefined;

const linesToSample = (results.overrides !== undefined && results.overrides.lines_to_sample !== undefined) ?
results.overrides.lines_to_sample : DEFAULT_LINES_TO_SAMPLE;

return {
format: results.format,
delimiter: results.delimiter,
Expand All @@ -104,6 +112,7 @@ export function processResults(results) {
charset: results.charset,
columnNames: results.column_names,
grokPattern: results.grok_pattern,
linesToSample,
};
}

Expand Down
5 changes: 4 additions & 1 deletion x-pack/plugins/ml/server/client/elasticsearch_ml.js
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
urls: [
{
// eslint-disable-next-line max-len
fmt: '/_ml/find_file_structure?&charset=<%=charset%>&format=<%=format%>&has_header_row=<%=has_header_row%>&column_names=<%=column_names%>&delimiter=<%=delimiter%>&quote=<%=quote%>&should_trim_fields=<%=should_trim_fields%>&grok_pattern=<%=grok_pattern%>&timestamp_field=<%=timestamp_field%>&timestamp_format=<%=timestamp_format%>',
fmt: '/_ml/find_file_structure?&charset=<%=charset%>&format=<%=format%>&has_header_row=<%=has_header_row%>&column_names=<%=column_names%>&delimiter=<%=delimiter%>&quote=<%=quote%>&should_trim_fields=<%=should_trim_fields%>&grok_pattern=<%=grok_pattern%>&timestamp_field=<%=timestamp_field%>&timestamp_format=<%=timestamp_format%>&lines_to_sample=<%=lines_to_sample%>',
req: {
charset: {
type: 'string'
Expand Down Expand Up @@ -602,6 +602,9 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
timestamp_format: {
type: 'string'
},
lines_to_sample: {
type: 'string'
},
}
},
{
Expand Down