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

[ES|QL] [Discover] Keeps the preferred chart configuration when possible #197453

Merged
merged 50 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
d29f5ed
[ES|QL] Keep the preferred chart type
stratoula Oct 9, 2024
8ee27c8
Merge branch 'main' into esql-keep-chart-type-discover
stratoula Oct 17, 2024
56fb6e0
Move chart type enum to visualization utils
stratoula Oct 18, 2024
a391020
Keep preferred type in transformational commands too
stratoula Oct 18, 2024
6ba9ae5
Fix ChartType imports
stratoula Oct 18, 2024
039471f
Merge branch 'main' into esql-keep-chart-type-discover
stratoula Oct 21, 2024
cb084c9
Merge branch 'main' into esql-keep-chart-type-discover
stratoula Oct 22, 2024
a6e712e
Adds a unit test
stratoula Oct 22, 2024
dcba4b3
Add a function description
stratoula Oct 22, 2024
24a4867
Merge branch 'main' into esql-keep-chart-type-discover
stratoula Oct 23, 2024
8bc6389
Adds a FT
stratoula Oct 23, 2024
484beb7
Fixes lens suggestion
stratoula Oct 23, 2024
a15c583
Cleanup
stratoula Oct 23, 2024
6f9e0fd
[ES|QL] Keep vis config
stratoula Oct 23, 2024
810bc19
Improves unit test
stratoula Oct 24, 2024
2dfd973
Merge with main chart
stratoula Oct 24, 2024
baa6658
Fix bugs
stratoula Oct 24, 2024
ae88fa7
Cleanup
stratoula Oct 24, 2024
342f7e4
Merge branch 'main' into keep-vis-config-esql
stratoula Oct 25, 2024
d77b9da
Simplification
stratoula Oct 25, 2024
712e789
Apply the change only if the vis state is different
stratoula Oct 25, 2024
312164f
Fixes
stratoula Oct 25, 2024
6793d30
fixes
stratoula Oct 25, 2024
424ba41
Fix
stratoula Oct 25, 2024
c9069b0
Merge branch 'main' into keep-vis-config-esql
stratoula Oct 28, 2024
9782de5
Adds unit tests
stratoula Oct 28, 2024
aa7698e
Adds another FT
stratoula Oct 28, 2024
8d14f47
Change
stratoula Oct 28, 2024
7e52f0c
Change file name
stratoula Oct 28, 2024
6fc4b8f
Merge branch 'main' into keep-vis-config-esql
stratoula Oct 28, 2024
35d7b41
Delete file
stratoula Oct 28, 2024
6378488
Add this again
stratoula Oct 28, 2024
33a082f
Update only when necessary
stratoula Oct 28, 2024
0397b5f
Fixes FT
stratoula Oct 29, 2024
c1dbf42
Fix FT
stratoula Oct 29, 2024
cf05cdd
Merge branch 'main' into keep-vis-config-esql
stratoula Oct 30, 2024
5e4d303
Merge branch 'main' into keep-vis-config-esql
stratoula Oct 31, 2024
9dd1b0d
Test the transition to histogram
stratoula Oct 31, 2024
087ebfa
Merge branch 'main' into keep-vis-config-esql
stratoula Nov 4, 2024
e572ef5
Change to fit the charttyp
stratoula Nov 4, 2024
de63f5c
Fixes bug that also exists in main
stratoula Nov 4, 2024
1e92f8d
Apply PR comments
stratoula Nov 4, 2024
6f07afc
Merge branch 'main' into keep-vis-config-esql
stratoula Nov 4, 2024
ea3e443
Revert the cleanup
stratoula Nov 4, 2024
abbe859
Suggestion
stratoula Nov 5, 2024
7ee61ab
Update packages/kbn-visualization-utils/src/map_vis_to_chart_type.ts
stratoula Nov 5, 2024
6daa2c8
Apply PR comment
stratoula Nov 5, 2024
cf707f8
Fix wrong suggestion
stratoula Nov 5, 2024
16063d1
Small change
stratoula Nov 5, 2024
4fad39b
Update snapshots
stratoula Nov 5, 2024
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
3 changes: 3 additions & 0 deletions packages/kbn-visualization-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ export { getTimeZone } from './src/get_timezone';
export { getLensAttributesFromSuggestion } from './src/get_lens_attributes';
markov00 marked this conversation as resolved.
Show resolved Hide resolved
export { TooltipWrapper } from './src/tooltip_wrapper';
export { useDebouncedValue } from './src/debounced_value';
export { ChartType } from './src/types';
export { getDatasourceId } from './src/get_datasource_id';
export { mapVisToChartType } from './src/map_vis_to_chart_type';
17 changes: 17 additions & 0 deletions packages/kbn-visualization-utils/src/get_datasource_id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export const getDatasourceId = (datasourceStates: Record<string, unknown>) => {
const datasourceId: 'formBased' | 'textBased' | undefined = [
'formBased' as const,
'textBased' as const,
].find((key) => Boolean(datasourceStates[key]));

return datasourceId;
};
37 changes: 37 additions & 0 deletions packages/kbn-visualization-utils/src/map_vis_to_chart_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { ChartType, LensVisualizationType } from './types';

export const mapVisToChartType = (visualizationType: string) => {
let chartType: ChartType | undefined;

switch (visualizationType) {
case LensVisualizationType.XY:
chartType = ChartType.XY;
break;
case LensVisualizationType.Metric:
chartType = ChartType.Metric;
break;
case LensVisualizationType.Pie:
chartType = ChartType.Pie;
break;
case LensVisualizationType.Heatmap:
chartType = ChartType.Heatmap;
break;
case LensVisualizationType.Datatable:
chartType = ChartType.Table;
break;
case LensVisualizationType.LegacyMetric:
chartType = ChartType.Metric;
break;
}

return chartType;
stratoula marked this conversation as resolved.
Show resolved Hide resolved
};
27 changes: 27 additions & 0 deletions packages/kbn-visualization-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,30 @@ export interface Suggestion<T = unknown, V = unknown> {
changeType: TableChangeType;
keptLayerIds: string[];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this here as it is used in many places already

export enum ChartType {
XY = 'XY',
Gauge = 'Gauge',
Bar = 'Bar',
Line = 'Line',
Area = 'Area',
Donut = 'Donut',
Heatmap = 'Heatmap',
stratoula marked this conversation as resolved.
Show resolved Hide resolved
Metric = 'Metric',
Treemap = 'Treemap',
Tagcloud = 'Tagcloud',
Waffle = 'Waffle',
Pie = 'Pie',
Mosaic = 'Mosaic',
Table = 'Table',
}

export enum LensVisualizationType {
XY = 'lnsXY',
Metric = 'lnsMetric',
Pie = 'lnsPie',
Heatmap = 'lnsHeatmap',
Gauge = 'lnsGauge',
Datatable = 'lnsDatatable',
LegacyMetric = 'lnsLegacyMetric',
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import type {
import type { AggregateQuery, TimeRange } from '@kbn/es-query';
import { getAggregateQueryMode, isOfAggregateQueryType } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { getLensAttributesFromSuggestion } from '@kbn/visualization-utils';
import {
getLensAttributesFromSuggestion,
ChartType,
mapVisToChartType,
} from '@kbn/visualization-utils';
import { LegendSize } from '@kbn/visualizations-plugin/public';
import { XYConfiguration } from '@kbn/visualizations-plugin/common';
import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common';
Expand All @@ -42,6 +46,7 @@ import {
isSuggestionShapeAndVisContextCompatible,
deriveLensSuggestionFromLensAttributes,
type QueryParams,
injectESQLQueryIntoLensLayers,
} from '../utils/external_vis_context';
import { computeInterval } from '../utils/compute_interval';
import { fieldSupportsBreakdown } from '../utils/field_supports_breakdown';
Expand Down Expand Up @@ -147,7 +152,10 @@ export class LensVisService {
externalVisContextStatus: UnifiedHistogramExternalVisContextStatus
) => void;
}) => {
const allSuggestions = this.getAllSuggestions({ queryParams });
const allSuggestions = this.getAllSuggestions({
queryParams,
preferredVisAttributes: externalVisContext?.attributes,
});

const suggestionState = this.getCurrentSuggestionState({
externalVisContext,
Expand Down Expand Up @@ -252,6 +260,7 @@ export class LensVisService {
const histogramSuggestionForESQL = this.getHistogramSuggestionForESQL({
queryParams,
breakdownField,
preferredVisAttributes: externalVisContext?.attributes,
});
if (histogramSuggestionForESQL) {
// In case if histogram suggestion, we want to empty the array and push the new suggestion
Expand Down Expand Up @@ -463,9 +472,11 @@ export class LensVisService {
private getHistogramSuggestionForESQL = ({
queryParams,
breakdownField,
preferredVisAttributes,
}: {
queryParams: QueryParams;
breakdownField?: DataViewField;
preferredVisAttributes?: UnifiedHistogramVisContext['attributes'];
}): Suggestion | undefined => {
const { dataView, query, timeRange, columns } = queryParams;
const breakdownColumn = breakdownField?.name
Expand Down Expand Up @@ -510,7 +521,22 @@ export class LensVisService {
if (breakdownColumn) {
context.textBasedColumns.push(breakdownColumn);
}
const suggestions = this.lensSuggestionsApi(context, dataView, ['lnsDatatable']) ?? [];

// here the attributes contain the main query and not the histogram one
const updatedAttributesWithQuery = preferredVisAttributes
? injectESQLQueryIntoLensLayers(preferredVisAttributes, {
esql: esqlQuery,
})
: undefined;

const suggestions =
this.lensSuggestionsApi(
context,
dataView,
['lnsDatatable'],
ChartType.XY,
updatedAttributesWithQuery
) ?? [];
if (suggestions.length) {
const suggestion = suggestions[0];
const suggestionVisualizationState = Object.assign({}, suggestion?.visualizationState);
Expand Down Expand Up @@ -574,17 +600,39 @@ export class LensVisService {
);
};

private getAllSuggestions = ({ queryParams }: { queryParams: QueryParams }): Suggestion[] => {
private getAllSuggestions = ({
queryParams,
preferredVisAttributes,
}: {
queryParams: QueryParams;
preferredVisAttributes?: UnifiedHistogramVisContext['attributes'];
}): Suggestion[] => {
const { dataView, columns, query, isPlainRecord } = queryParams;

const preferredChartType = preferredVisAttributes
? mapVisToChartType(preferredVisAttributes.visualizationType)
: undefined;

let visAttributes = preferredVisAttributes;

if (query && isOfAggregateQueryType(query) && preferredVisAttributes) {
visAttributes = injectESQLQueryIntoLensLayers(preferredVisAttributes, query);
}

const context = {
dataViewSpec: dataView?.toSpec(),
fieldName: '',
textBasedColumns: columns,
query: query && isOfAggregateQueryType(query) ? query : undefined,
};
const allSuggestions = isPlainRecord
? this.lensSuggestionsApi(context, dataView, ['lnsDatatable']) ?? []
? this.lensSuggestionsApi(
context,
dataView,
['lnsDatatable'],
preferredChartType,
visAttributes
) ?? []
: [];

return allSuggestions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
canImportVisContext,
exportVisContext,
isSuggestionShapeAndVisContextCompatible,
injectESQLQueryIntoLensLayers,
} from './external_vis_context';
import { getLensVisMock } from '../__mocks__/lens_vis';
import { dataViewWithTimefieldMock } from '../__mocks__/data_view_with_timefield';
Expand Down Expand Up @@ -162,4 +163,63 @@ describe('external_vis_context', () => {
).toBe(true);
});
});

describe('injectESQLQueryIntoLensLayers', () => {
it('should return the Lens attributes as they are for unknown datasourceId', async () => {
const attributes = {
visualizationType: 'lnsXY',
state: {
visualization: { preferredSeriesType: 'line' },
datasourceStates: { unknownId: { layers: {} } },
},
} as unknown as UnifiedHistogramVisContext['attributes'];
expect(injectESQLQueryIntoLensLayers(attributes, { esql: 'from foo' })).toStrictEqual(
attributes
);
});

it('should return the Lens attributes as they are for DSL config (formbased)', async () => {
const attributes = {
visualizationType: 'lnsXY',
state: {
visualization: { preferredSeriesType: 'line' },
datasourceStates: { formBased: { layers: {} } },
},
} as UnifiedHistogramVisContext['attributes'];
expect(injectESQLQueryIntoLensLayers(attributes, { esql: 'from foo' })).toStrictEqual(
attributes
);
});

it('should inject the query to the Lens attributes for ES|QL config (textbased)', async () => {
const attributes = {
visualizationType: 'lnsXY',
state: {
visualization: { preferredSeriesType: 'line' },
datasourceStates: { textBased: { layers: { layer1: { query: { esql: 'from foo' } } } } },
},
} as unknown as UnifiedHistogramVisContext['attributes'];

const expectedAttributes = {
...attributes,
state: {
...attributes.state,
datasourceStates: {
...attributes.state.datasourceStates,
textBased: {
...attributes.state.datasourceStates.textBased,
layers: {
layer1: {
query: { esql: 'from foo | stats count(*)' },
},
},
},
},
},
} as unknown as UnifiedHistogramVisContext['attributes'];
expect(
injectESQLQueryIntoLensLayers(attributes, { esql: 'from foo | stats count(*)' })
).toStrictEqual(expectedAttributes);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { isEqual } from 'lodash';
import { isEqual, cloneDeep } from 'lodash';
import type { DataView } from '@kbn/data-views-plugin/common';
import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query';
import { getDatasourceId } from '@kbn/visualization-utils';
import type { DatatableColumn } from '@kbn/expressions-plugin/common';
import type { PieVisualizationState, Suggestion, XYState } from '@kbn/lens-plugin/public';
import { UnifiedHistogramSuggestionType, UnifiedHistogramVisContext } from '../types';
Expand Down Expand Up @@ -103,6 +104,42 @@ export const isSuggestionShapeAndVisContextCompatible = (
);
};

export const injectESQLQueryIntoLensLayers = (
stratoula marked this conversation as resolved.
Show resolved Hide resolved
visAttributes: UnifiedHistogramVisContext['attributes'],
query: AggregateQuery
) => {
const datasourceId = getDatasourceId(visAttributes.state.datasourceStates);

// if the datasource is formBased, we should not fix the query
if (!datasourceId || datasourceId === 'formBased') {
return visAttributes;
}

if (!visAttributes.state.datasourceStates[datasourceId]) {
return visAttributes;
}

const datasourceState = cloneDeep(visAttributes.state.datasourceStates[datasourceId]);

if (datasourceState && datasourceState.layers) {
Object.values(datasourceState.layers).forEach((layer) => {
if (!isEqual(layer.query, query)) {
layer.query = query;
}
});
}
return {
...visAttributes,
state: {
...visAttributes.state,
datasourceStates: {
...visAttributes.state.datasourceStates,
[datasourceId]: datasourceState,
},
},
};
};

export function deriveLensSuggestionFromLensAttributes({
externalVisContext,
queryParams,
Expand All @@ -122,10 +159,7 @@ export function deriveLensSuggestionFromLensAttributes({
}

// it should be one of 'formBased'/'textBased' and have value
const datasourceId: 'formBased' | 'textBased' | undefined = [
'formBased' as const,
'textBased' as const,
].find((key) => Boolean(externalVisContext.attributes.state.datasourceStates[key]));
const datasourceId = getDatasourceId(externalVisContext.attributes.state.datasourceStates);

if (!datasourceId) {
return undefined;
Expand Down
Loading