Skip to content

Commit

Permalink
[Lens] Allow last value, min and max on dates, allow last value on ip…
Browse files Browse the repository at this point in the history
…_range, number_range and date_range (#125389)

* allow last value, min and max on dates

* fix tests

* fix typo

* fix tests

* nit

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
flash1293 and kibanamachine authored Feb 16, 2022
1 parent 79a05ed commit 595a6c3
Show file tree
Hide file tree
Showing 18 changed files with 218 additions and 28 deletions.
8 changes: 5 additions & 3 deletions x-pack/plugins/lens/common/expressions/datatable/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ function isValidNumber(value: unknown): boolean {
}

export function isNumericFieldForDatatable(currentData: Datatable | undefined, accessor: string) {
const isNumeric =
currentData?.columns.find((col) => col.id === accessor || getOriginalId(col.id) === accessor)
?.meta.type === 'number';
const column = currentData?.columns.find(
(col) => col.id === accessor || getOriginalId(col.id) === accessor
);
// min and max aggs are reporting as number but are actually dates - work around this by checking for the date formatter until this is fixed at the source
const isNumeric = column?.meta.type === 'number' && column?.meta.params?.id !== 'date';

return (
isNumeric &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ export const getDatatableVisualization = ({

const hasNoSummaryRow = column.summaryRow == null || column.summaryRow === 'none';

const canColor =
datasource!.getOperationForColumnId(column.columnId)?.dataType === 'number';

return {
type: 'expression',
chain: [
Expand All @@ -383,7 +386,7 @@ export const getDatatableVisualization = ({
!datasource!.getOperationForColumnId(column.columnId)?.isBucketed,
],
alignment: typeof column.alignment === 'undefined' ? [] : [column.alignment],
colorMode: [column.colorMode ?? 'none'],
colorMode: [canColor && column.colorMode ? column.colorMode : 'none'],
palette: [paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams)],
summaryRow: hasNoSummaryRow ? [] : [column.summaryRow!],
summaryLabel: hasNoSummaryRow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,35 @@ describe('heatmap suggestions', () => {
table: {
layerId: 'first',
isMultiRow: true,
columns: [],
columns: [
{
columnId: 'date-column-01',
operation: {
isBucketed: true,
dataType: 'date',
scale: 'interval',
label: 'Date',
},
},
{
columnId: 'another-bucket-column',
operation: {
isBucketed: true,
dataType: 'string',
scale: 'ratio',
label: 'Bucket',
},
},
{
columnId: 'metric-column',
operation: {
isBucketed: false,
dataType: 'number',
scale: 'ratio',
label: 'Metric',
},
},
],
changeType: 'initial',
},
state: {
Expand All @@ -208,7 +236,35 @@ describe('heatmap suggestions', () => {
table: {
layerId: 'first',
isMultiRow: true,
columns: [],
columns: [
{
columnId: 'date-column-01',
operation: {
isBucketed: true,
dataType: 'date',
scale: 'interval',
label: 'Date',
},
},
{
columnId: 'another-bucket-column',
operation: {
isBucketed: true,
dataType: 'string',
scale: 'ratio',
label: 'Bucket',
},
},
{
columnId: 'metric-column',
operation: {
isBucketed: false,
dataType: 'number',
scale: 'ratio',
label: 'Metric',
},
},
],
changeType: 'reduced',
},
state: {
Expand All @@ -223,6 +279,9 @@ describe('heatmap suggestions', () => {
layerId: 'first',
layerType: layerTypes.DATA,
shape: 'heatmap',
valueAccessor: 'metric-column',
xAccessor: 'date-column-01',
yAccessor: 'another-bucket-column',
gridConfig: {
type: HEATMAP_GRID_FUNCTION,
isCellLabelVisible: false,
Expand All @@ -240,7 +299,7 @@ describe('heatmap suggestions', () => {
title: 'Heat map',
hide: true,
previewIcon: 'empty',
score: 0,
score: 0.3,
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export const getSuggestions: Visualization<HeatmapVisualizationState>['getSugges
(state?.shape === CHART_SHAPES.HEATMAP &&
(state.xAccessor || state.yAccessor || state.valueAccessor) &&
table.changeType !== 'extended') ||
table.columns.some((col) => col.operation.isStaticValue)
table.columns.some((col) => col.operation.isStaticValue) ||
// do not use suggestions with non-numeric metrics
table.columns.some((col) => !col.operation.isBucketed && col.operation.dataType !== 'number')
) {
return [];
}
Expand All @@ -44,6 +46,10 @@ export const getSuggestions: Visualization<HeatmapVisualizationState>['getSugges

const [groups, metrics] = partition(table.columns, (col) => col.operation.isBucketed);

if (groups.length === 0 && metrics.length === 0) {
return [];
}

if (groups.length >= 3) {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ const expectedIndexPatterns = {
};

const bytesColumn: GenericIndexPatternColumn = {
label: 'Max of bytes',
label: 'Sum of bytes',
dataType: 'number',
isBucketed: false,

// Private
operationType: 'max',
operationType: 'sum',
sourceField: 'bytes',
params: { format: { id: 'bytes' } },
};
Expand Down Expand Up @@ -529,7 +529,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
columns: {
...state.layers.first.columns,
col1: expect.objectContaining({
operationType: 'max',
operationType: 'sum',
sourceField: 'memory',
params: { format: { id: 'bytes' } },
// Other parts of this don't matter for this test
Expand Down Expand Up @@ -728,7 +728,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
act(() => {
wrapper
.find('input[data-test-subj="indexPattern-label-edit"]')
.simulate('change', { target: { value: 'Maximum of bytes' } });
.simulate('change', { target: { value: 'Sum of bytes' } });
});

expect(setState.mock.calls[0]).toEqual([expect.any(Function)]);
Expand All @@ -740,7 +740,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
columns: {
...state.layers.first.columns,
col1: expect.objectContaining({
label: 'Maximum of bytes',
label: 'Sum of bytes',
customLabel: false,
// Other parts of this don't matter for this test
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
'replace_duplicate_incompatible',
'swap_incompatible',
],
nextLabel: 'Unique count',
nextLabel: 'Minimum',
});
});

Expand Down Expand Up @@ -736,7 +736,7 @@ describe('IndexPatternDimensionEditorPanel', () => {
})
).toEqual({
dropTypes: ['replace_incompatible', 'replace_duplicate_incompatible'],
nextLabel: 'Unique count',
nextLabel: 'Minimum',
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,17 +511,18 @@ describe('last_value', () => {
]);
});
it('shows error message if the sourceField is of unsupported type', () => {
indexPattern.getFieldByName('start_date')!.type = 'unsupported_type';
errorLayer = {
...errorLayer,
columns: {
col1: {
...errorLayer.columns.col1,
sourceField: 'timestamp',
sourceField: 'start_date',
} as LastValueIndexPatternColumn,
},
};
expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([
'Field timestamp is of the wrong type',
'Field start_date is of the wrong type',
]);
});
it('shows error message if the sortField is not date', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,16 @@ function ofName(name: string, timeShift: string | undefined) {
);
}

const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']);
const supportedTypes = new Set([
'string',
'boolean',
'number',
'ip',
'date',
'ip_range',
'number_range',
'date_range',
]);

export function getInvalidSortFieldMessage(sortField: string, indexPattern?: IndexPattern) {
if (!indexPattern) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ function buildMetricOperation<T extends MetricColumn<string>>({
ofName,
priority,
optionalTimeScaling,
supportsDate,
}: {
type: T['operationType'];
displayName: string;
ofName: (name: string) => string;
priority?: number;
optionalTimeScaling?: boolean;
description?: string;
supportsDate?: boolean;
}) {
const labelLookup = (name: string, column?: BaseIndexPatternColumn) => {
const label = ofName(name);
Expand All @@ -76,12 +78,12 @@ function buildMetricOperation<T extends MetricColumn<string>>({
timeScalingMode: optionalTimeScaling ? 'optional' : undefined,
getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => {
if (
supportedTypes.includes(fieldType) &&
(supportedTypes.includes(fieldType) || (supportsDate && fieldType === 'date')) &&
aggregatable &&
(!aggregationRestrictions || aggregationRestrictions[type])
) {
return {
dataType: 'number',
dataType: fieldType === 'date' ? 'date' : 'number',
isBucketed: false,
scale: 'ratio',
};
Expand All @@ -105,7 +107,7 @@ function buildMetricOperation<T extends MetricColumn<string>>({
buildColumn: ({ field, previousColumn }, columnParams) => {
return {
label: labelLookup(field.displayName, previousColumn),
dataType: 'number',
dataType: supportsDate && field.type === 'date' ? 'date' : 'number',
operationType: type,
sourceField: field.name,
isBucketed: false,
Expand All @@ -120,6 +122,7 @@ function buildMetricOperation<T extends MetricColumn<string>>({
return {
...oldColumn,
label: labelLookup(field.displayName, oldColumn),
dataType: field.type,
sourceField: field.name,
};
},
Expand Down Expand Up @@ -186,6 +189,7 @@ export const minOperation = buildMetricOperation<MinIndexPatternColumn>({
defaultMessage:
'A single-value metrics aggregation that returns the minimum value among the numeric values extracted from the aggregated documents.',
}),
supportsDate: true,
});

export const maxOperation = buildMetricOperation<MaxIndexPatternColumn>({
Expand All @@ -202,6 +206,7 @@ export const maxOperation = buildMetricOperation<MaxIndexPatternColumn>({
defaultMessage:
'A single-value metrics aggregation that returns the maximum value among the numeric values extracted from the aggregated documents.',
}),
supportsDate: true,
});

export const averageOperation = buildMetricOperation<AvgIndexPatternColumn>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,30 @@ describe('getOperationTypesForField', () => {
},
],
},
Object {
"operationMetaData": Object {
"dataType": "date",
"isBucketed": false,
"scale": "ratio",
},
"operations": Array [
Object {
"field": "timestamp",
"operationType": "min",
"type": "field",
},
Object {
"field": "timestamp",
"operationType": "max",
"type": "field",
},
Object {
"field": "timestamp",
"operationType": "last_value",
"type": "field",
},
],
},
Object {
"operationMetaData": Object {
"dataType": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ describe('metric_expression', () => {
>
<AutoScale
key="3"
minScale={0.05}
>
<div
className="lnsMetricExpression__value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,13 @@ export function MetricChart({

return (
<VisualizationContainer className="lnsMetricExpression__container" style={color}>
<AutoScale key={value} titlePosition={titlePosition} textAlign={textAlign} size={size}>
<AutoScale
key={value}
titlePosition={titlePosition}
textAlign={textAlign}
size={size}
minScale={mode === 'full' ? undefined : 0.05}
>
{mode === 'full' && (
<div
data-test-subj="lns_metric_title"
Expand Down
Loading

0 comments on commit 595a6c3

Please sign in to comment.