From eb9262dca58a82a004ecb03d25759354babe5f1a Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 3 Nov 2021 14:57:23 +0100 Subject: [PATCH] [Lens] Avoid to compute over time suggestion for expensive configurations (#115932) * :zap: Avoid over time suggestion if too expensive * :white_check_mark: Add unit tests * :ok_hand: Integrate feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../indexpattern_suggestions.test.tsx | 58 ++++++++++++++ .../indexpattern_suggestions.ts | 11 ++- .../operations/layer_helpers.test.ts | 76 +++++++++++++++++++ .../operations/layer_helpers.ts | 11 +++ 4 files changed, 154 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 9315b61adcc54..5df02482a2745 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1952,6 +1952,64 @@ describe('IndexPattern Data Source suggestions', () => { suggestions.forEach((suggestion) => expect(suggestion.table.columns.length).toBe(1)); }); + it("should not propose an over time suggestion if there's a top values aggregation with an high size", () => { + const initialState = testInitialState(); + (initialState.layers.first.columns.col1 as { params: { size: number } }).params!.size = 6; + const suggestions = getDatasourceSuggestionsFromCurrentState({ + ...initialState, + indexPatterns: { 1: { ...initialState.indexPatterns['1'], timeFieldName: undefined } }, + }); + suggestions.forEach((suggestion) => expect(suggestion.table.columns.length).toBe(1)); + }); + + it('should not propose an over time suggestion if there are multiple bucket dimensions', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2', 'col3'], + columns: { + ...initialState.layers.first.columns, + col2: { + label: 'My Op', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'average', + sourceField: 'bytes', + scale: 'ratio', + }, + col3: { + label: 'My Op', + customLabel: true, + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + sourceField: 'dest', + params: { + size: 5, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + }, + }, + }, + }, + }, + }; + const suggestions = getDatasourceSuggestionsFromCurrentState({ + ...state, + indexPatterns: { 1: { ...state.indexPatterns['1'], timeFieldName: undefined } }, + }); + suggestions.forEach((suggestion) => { + const firstBucket = suggestion.table.columns.find(({ columnId }) => columnId === 'col1'); + expect(firstBucket?.operation).not.toBe('date'); + }); + }); + it('returns simplified versions of table with more than 2 columns', () => { const initialState = testInitialState(); const fields = [ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index d3c292b7e019b..8b940ec1f05af 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -21,6 +21,7 @@ import { getExistingColumnGroups, isReferenced, getReferencedColumnIds, + hasTermsWithManyBuckets, } from './operations'; import { hasField } from './utils'; import type { @@ -424,7 +425,7 @@ export function getDatasourceSuggestionsFromCurrentState( ); if (!references.length && metrics.length && buckets.length === 0) { - if (timeField) { + if (timeField && buckets.length < 1 && !hasTermsWithManyBuckets(layer)) { // suggest current metric over time if there is a default time field suggestions.push(createSuggestionWithDefaultDateHistogram(state, layerId, timeField)); } @@ -436,7 +437,13 @@ export function getDatasourceSuggestionsFromCurrentState( // base range intervals are of number dataType. // Custom range/intervals have a different dataType so they still receive the Over Time suggestion - if (!timeDimension && timeField && !hasNumericDimension) { + if ( + !timeDimension && + timeField && + buckets.length < 2 && + !hasNumericDimension && + !hasTermsWithManyBuckets(layer) + ) { // suggest current configuration over time if there is a default time field // and no time dimension yet suggestions.push(createSuggestionWithDefaultDateHistogram(state, layerId, timeField)); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 77a2b334a9e20..3dc0677f3b9b6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -15,6 +15,7 @@ import { deleteColumn, updateLayerIndexPattern, getErrorMessages, + hasTermsWithManyBuckets, } from './layer_helpers'; import { operationDefinitionMap, OperationType } from '../operations'; import { TermsIndexPatternColumn } from './definitions/terms'; @@ -3005,4 +3006,79 @@ describe('state_helpers', () => { ); }); }); + + describe('hasTermsWithManyBuckets', () => { + it('should return false for a bucketed non terms operation', () => { + const layer: IndexPatternLayer = { + columnOrder: ['col1'], + columns: { + col1: { + dataType: 'date', + isBucketed: true, + label: '', + operationType: 'date_histogram', + sourceField: 'fieldD', + params: { + interval: 'd', + }, + }, + }, + indexPatternId: 'original', + }; + + expect(hasTermsWithManyBuckets(layer)).toBeFalsy(); + }); + + it('should return false if all terms operation have a lower size', () => { + const layer: IndexPatternLayer = { + columnOrder: ['col1'], + columns: { + col1: { + label: 'My Op', + customLabel: true, + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + sourceField: 'dest', + params: { + size: 5, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + }, + }, + }, + indexPatternId: 'original', + }; + + expect(hasTermsWithManyBuckets(layer)).toBeFalsy(); + }); + + it('should return true if the size is high', () => { + const layer: IndexPatternLayer = { + columnOrder: ['col1'], + columns: { + col1: { + label: 'My Op', + customLabel: true, + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + sourceField: 'dest', + params: { + size: 15, + orderBy: { type: 'alphabetical' }, + orderDirection: 'asc', + }, + }, + }, + indexPatternId: 'original', + }; + + expect(hasTermsWithManyBuckets(layer)).toBeTruthy(); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 1ae17ca9ec2b2..67546ca6009cf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -1369,6 +1369,17 @@ export function getReferencedColumnIds(layer: IndexPatternLayer, columnId: strin return referencedIds; } +export function hasTermsWithManyBuckets(layer: IndexPatternLayer): boolean { + return layer.columnOrder.some((columnId) => { + const column = layer.columns[columnId]; + if (column) { + return ( + column.isBucketed && column.params && 'size' in column.params && column.params.size > 5 + ); + } + }); +} + export function isOperationAllowedAsReference({ operationType, validation,