From b97ae8596bc8f28f1d5709693cd84aab86c49dea Mon Sep 17 00:00:00 2001 From: Julia Rechkunova Date: Tue, 9 Jul 2024 12:32:48 +0200 Subject: [PATCH] [ES|QL] Support counter fields (#186292) - Closes https://github.com/elastic/kibana/issues/186160 ## Summary This PR adds a new util to help with converting ES|QL column into data view field representation https://github.com/elastic/kibana/blob/9d63332c74523b00f2b9056352a5b3a86eaf2b75/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field.ts#L13 This allows to handle counter fields in a more predicable way despite of the different format of ES|QL column data. https://github.com/elastic/kibana/pull/186154#issuecomment-2164973060 Screenshot 2024-07-03 at 13 48 20 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine Co-authored-by: Stratoula Kalafateli Co-authored-by: Matthias Wilhelm (cherry picked from commit 692b656f9c247b438951a946f3f1123d36ac00a6) --- packages/kbn-data-view-utils/index.ts | 2 +- .../convert_to_data_view_field_spec.test.ts | 60 +++++++++++++++++++ .../utils/convert_to_data_view_field_spec.ts | 37 ++++++++++++ packages/kbn-data-view-utils/tsconfig.json | 4 ++ .../kbn-field-types/src/kbn_field_types.ts | 4 ++ .../utils/get_text_based_column_icon_type.ts | 9 ++- packages/kbn-field-utils/tsconfig.json | 1 + .../components/sidebar/lib/get_field_list.ts | 11 +--- 8 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts create mode 100644 packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts diff --git a/packages/kbn-data-view-utils/index.ts b/packages/kbn-data-view-utils/index.ts index 1c881dbdacf79..ad783bc163c59 100644 --- a/packages/kbn-data-view-utils/index.ts +++ b/packages/kbn-data-view-utils/index.ts @@ -7,6 +7,6 @@ */ export * from './src/constants'; - +export { convertDatatableColumnToDataViewFieldSpec } from './src/utils/convert_to_data_view_field_spec'; export { createRegExpPatternFrom } from './src/utils/create_regexp_pattern_from'; export { testPatternAgainstAllowedList } from './src/utils/test_pattern_against_allowed_list'; diff --git a/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts new file mode 100644 index 0000000000000..94dce4a6a60da --- /dev/null +++ b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.test.ts @@ -0,0 +1,60 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { DatatableColumnType } from '@kbn/expressions-plugin/common'; +import { convertDatatableColumnToDataViewFieldSpec } from './convert_to_data_view_field_spec'; + +describe('convertDatatableColumnToDataViewFieldSpec', () => { + it('should return a DataViewField object for a counter column', () => { + const column = { + id: 'bytes_counter', + name: 'bytes_counter', + meta: { + esType: 'counter_long', + type: 'number' as DatatableColumnType, + }, + isNull: false, + }; + const result = convertDatatableColumnToDataViewFieldSpec(column); + expect(result).toEqual( + expect.objectContaining({ + name: 'bytes_counter', + type: 'number', + esTypes: ['long'], + searchable: true, + aggregatable: false, + isNull: false, + timeSeriesMetric: 'counter', + }) + ); + }); + + it('should return a DataViewField object with timeSeriesMetric undefined if esType does not start with counter_', () => { + const column = { + id: 'test', + name: 'test', + meta: { + esType: 'keyword', + type: 'string' as DatatableColumnType, + }, + isNull: false, + }; + const result = convertDatatableColumnToDataViewFieldSpec(column); + expect(result.timeSeriesMetric).toBeUndefined(); + expect(result).toEqual( + expect.objectContaining({ + name: 'test', + type: 'string', + esTypes: ['keyword'], + searchable: true, + aggregatable: false, + isNull: false, + }) + ); + }); +}); diff --git a/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts new file mode 100644 index 0000000000000..7cc408a414ade --- /dev/null +++ b/packages/kbn-data-view-utils/src/utils/convert_to_data_view_field_spec.ts @@ -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 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 or the Server + * Side Public License, v 1. + */ + +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; +import type { MappingTimeSeriesMetricType } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { FieldSpec } from '@kbn/data-views-plugin/common'; + +/** + * Convert a datatable column to a DataViewFieldSpec + */ +export function convertDatatableColumnToDataViewFieldSpec(column: DatatableColumn): FieldSpec { + let esType = column.meta?.esType; + let timeSeriesMetric: MappingTimeSeriesMetricType | undefined; + + // 'counter_integer', 'counter_long', 'counter_double'... + if (esType?.startsWith('counter_')) { + esType = esType?.replace('counter_', ''); + timeSeriesMetric = 'counter'; + } + + // `DataViewField` class is defined in "data-views" plugin, so we can't create an instance of it from a package. + // We will return a data view field spec here instead then. + return { + name: column.name, + type: column.meta?.type ?? 'unknown', + esTypes: esType ? [esType] : undefined, + searchable: true, + aggregatable: false, + isNull: Boolean(column?.isNull), + ...(timeSeriesMetric ? { timeSeriesMetric } : {}), + }; +} diff --git a/packages/kbn-data-view-utils/tsconfig.json b/packages/kbn-data-view-utils/tsconfig.json index a41af0b7c5017..05400030e1001 100644 --- a/packages/kbn-data-view-utils/tsconfig.json +++ b/packages/kbn-data-view-utils/tsconfig.json @@ -14,5 +14,9 @@ ], "exclude": [ "target/**/*" + ], + "kbn_references": [ + "@kbn/data-views-plugin", + "@kbn/expressions-plugin", ] } diff --git a/packages/kbn-field-types/src/kbn_field_types.ts b/packages/kbn-field-types/src/kbn_field_types.ts index 7ec22de078230..5059d37da1d0e 100644 --- a/packages/kbn-field-types/src/kbn_field_types.ts +++ b/packages/kbn-field-types/src/kbn_field_types.ts @@ -50,6 +50,10 @@ export const getFilterableKbnTypeNames = (): string[] => registeredKbnTypes.filter((type) => type.filterable).map((type) => type.name); export function esFieldTypeToKibanaFieldType(type: string) { + // 'counter_integer', 'counter_long', 'counter_double'... + if (type.startsWith('counter_')) { + return KBN_FIELD_TYPES.NUMBER; + } switch (type) { case ES_FIELD_TYPES._INDEX: return KBN_FIELD_TYPES.STRING; diff --git a/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts b/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts index 0ccad94674208..3c37b9b01c3fb 100644 --- a/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts +++ b/packages/kbn-field-utils/src/utils/get_text_based_column_icon_type.ts @@ -7,6 +7,7 @@ */ import type { DatatableColumnMeta } from '@kbn/expressions-plugin/common'; +import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import { getFieldIconType } from './get_field_icon_type'; export function getTextBasedColumnIconType( @@ -19,10 +20,8 @@ export function getTextBasedColumnIconType( | null ): string | null { return columnMeta && columnMeta.type - ? getFieldIconType({ - name: '', - type: columnMeta.type, - esTypes: columnMeta.esType ? [columnMeta.esType] : [], - }) + ? getFieldIconType( + convertDatatableColumnToDataViewFieldSpec({ id: '', name: '', meta: columnMeta }) + ) : null; } diff --git a/packages/kbn-field-utils/tsconfig.json b/packages/kbn-field-utils/tsconfig.json index 4b75159b5f7fe..9ac5ba7e942bc 100644 --- a/packages/kbn-field-utils/tsconfig.json +++ b/packages/kbn-field-utils/tsconfig.json @@ -10,6 +10,7 @@ "@kbn/react-field", "@kbn/field-types", "@kbn/expressions-plugin", + "@kbn/data-view-utils", ], "exclude": ["target/**/*"] } diff --git a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts index 99d911dd14f61..36a07faaef80d 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/lib/get_field_list.ts @@ -8,6 +8,7 @@ import { difference } from 'lodash'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { fieldWildcardFilter } from '@kbn/kibana-utils-plugin/public'; import { isNestedFieldParent } from '@kbn/discover-utils'; @@ -66,14 +67,6 @@ export function getEsqlQueryFieldList(esqlQueryColumns?: DatatableColumn[]): Dat return []; } return esqlQueryColumns.map( - (column) => - new DataViewField({ - name: column.name, - type: column.meta?.type ?? 'unknown', - esTypes: column.meta?.esType ? [column.meta?.esType] : undefined, - searchable: true, - aggregatable: false, - isNull: Boolean(column?.isNull), - }) + (column) => new DataViewField(convertDatatableColumnToDataViewFieldSpec(column)) ); }