Skip to content

Commit ddb1961

Browse files
authored
[ML] Data Frames: Fixes crash of the source table for complex field values. (#39878) (#39999)
The data frame wizard's source index preview table would crash with some field values more complex than strings. This PR fixes it by adding the following "special treatment" for these values: Arrays of strings will be concatenated to a comma separated string in table rows. Arrays of objects will be rendered as a badge with the label array and a tooltip that says that the full value is available in the expanded row. In the expanded row, every value that's not a string will be rendered using JSON.stringify().
1 parent 44983ce commit ddb1961

File tree

4 files changed

+140
-2
lines changed

4 files changed

+140
-2
lines changed

x-pack/legacy/plugins/ml/public/data_frame/components/source_index_preview/__snapshots__/expanded_row.test.tsx.snap

Lines changed: 71 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { shallow } from 'enzyme';
8+
import React from 'react';
9+
10+
import { ExpandedRow } from './expanded_row';
11+
12+
describe('Data Frame: <ExpandedRow />', () => {
13+
test('Test against strings, objects and arrays.', () => {
14+
const props = {
15+
item: {
16+
_id: 'the-id',
17+
_source: {
18+
name: 'the-name',
19+
nested: {
20+
inner1: 'the-inner-1',
21+
inner2: 'the-inner-2',
22+
},
23+
arrayString: ['the-array-string-1', 'the-array-string-2'],
24+
arrayObject: [{ object1: 'the-object-1' }, { object2: 'the-objects-2' }],
25+
},
26+
},
27+
};
28+
29+
// Using a wrapping <div> element because shallow() would fail
30+
// with the Provider being the outer most component.
31+
const wrapper = shallow(<ExpandedRow {...props} />);
32+
33+
expect(wrapper).toMatchSnapshot();
34+
});
35+
});

x-pack/legacy/plugins/ml/public/data_frame/components/source_index_preview/expanded_row.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const ExpandedRow: React.SFC<ExpandedRowProps> = ({ item }) => {
2525
return (
2626
<span key={k}>
2727
<EuiBadge>{k}:</EuiBadge>
28-
<small> {value}&nbsp;&nbsp;</small>
28+
<small> {typeof value === 'string' ? value : JSON.stringify(value)}&nbsp;&nbsp;</small>
2929
</span>
3030
);
3131
});

x-pack/legacy/plugins/ml/public/data_frame/components/source_index_preview/source_index_preview.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import moment from 'moment-timezone';
1010
import { i18n } from '@kbn/i18n';
1111

1212
import {
13+
EuiBadge,
1314
EuiButtonEmpty,
1415
EuiButtonIcon,
1516
EuiCallOut,
@@ -25,6 +26,7 @@ import {
2526
EuiProgress,
2627
EuiText,
2728
EuiTitle,
29+
EuiToolTip,
2830
RIGHT_ALIGNMENT,
2931
} from '@elastic/eui';
3032

@@ -215,12 +217,42 @@ export const SourceIndexPreview: React.SFC<Props> = React.memo(({ cellClick, que
215217
} as Dictionary<any>;
216218

217219
const field = indexPattern.fields.find(f => f.name === k);
218-
const render = (d: string) => {
220+
221+
const formatField = (d: string) => {
219222
return field !== undefined && field.type === KBN_FIELD_TYPES.DATE
220223
? formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000)
221224
: d;
222225
};
223226

227+
const render = (d: any) => {
228+
if (Array.isArray(d) && d.every(item => typeof item === 'string')) {
229+
// If the cells data is an array of strings, return as a comma separated list.
230+
// The list will get limited to 5 items with `…` at the end if there's more in the original array.
231+
return `${d
232+
.map(item => formatField(item))
233+
.slice(0, 5)
234+
.join(', ')}${d.length > 5 ? ', …' : ''}`;
235+
} else if (Array.isArray(d)) {
236+
// If the cells data is an array of e.g. objects, display a 'array' badge with a
237+
// tooltip that explains that this type of field is not supported in this table.
238+
return (
239+
<EuiToolTip
240+
content={i18n.translate(
241+
'xpack.ml.dataframe.sourceIndexPreview.dataFrameSourceIndexArrayToolTipContent',
242+
{
243+
defaultMessage:
244+
'The full content of this array based column is available in the expanded row.',
245+
}
246+
)}
247+
>
248+
<EuiBadge>array</EuiBadge>
249+
</EuiToolTip>
250+
);
251+
}
252+
253+
return formatField(d);
254+
};
255+
224256
column.render = render;
225257

226258
if (CELL_CLICK_ENABLED && cellClick) {

0 commit comments

Comments
 (0)