From ed3b6102fce55ea0b6ad5080748e98ce2a91329a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:52:16 +1000 Subject: [PATCH] [8.x] [data views] Use map object for field attributes (#193760) (#194252) # Backport This will backport the following commits from `main` to `8.x`: - [[data views] Use map object for field attributes (#193760)](https://github.com/elastic/kibana/pull/193760) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Matthew Kime --- .../common/data_views/abstract_data_views.ts | 21 +++++++------- .../common/data_views/data_view.test.ts | 4 +-- .../common/data_views/data_view_lazy.ts | 28 +++++++++++-------- .../common/data_views/data_views.ts | 26 ++++++++++------- src/plugins/data_views/common/types.ts | 9 +++--- .../log_views/log_views_client.test.ts | 2 +- 6 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/plugins/data_views/common/data_views/abstract_data_views.ts b/src/plugins/data_views/common/data_views/abstract_data_views.ts index 51bd618f8bca0..2ad3f04fffc3e 100644 --- a/src/plugins/data_views/common/data_views/abstract_data_views.ts +++ b/src/plugins/data_views/common/data_views/abstract_data_views.ts @@ -13,7 +13,7 @@ import type { SerializedFieldFormat, } from '@kbn/field-formats-plugin/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; -import { cloneDeep, merge } from 'lodash'; +import { cloneDeep } from 'lodash'; import type { DataViewFieldBase } from '@kbn/es-query'; import type { DataViewSpec, @@ -161,7 +161,7 @@ export abstract class AbstractDataView { } return acc; }, {} as Record) - : []; + : {}; this.allowNoIndex = spec?.allowNoIndex || false; @@ -191,7 +191,7 @@ export abstract class AbstractDataView { this.sourceFilters = [...(spec.sourceFilters || [])]; this.type = spec.type; this.typeMeta = spec.typeMeta; - this.fieldAttrs = cloneDeep(merge({}, extractedFieldAttrs, spec.fieldAttrs)) || {}; + this.fieldAttrs = new Map(Object.entries({ ...extractedFieldAttrs, ...spec.fieldAttrs })); this.runtimeFieldMap = cloneDeep(spec.runtimeFieldMap) || {}; this.namespaces = spec.namespaces || []; this.name = spec.name || ''; @@ -300,10 +300,8 @@ export abstract class AbstractDataView { attrName: K, value: FieldAttrSet[K] ) { - if (!this.fieldAttrs[fieldName]) { - this.fieldAttrs[fieldName] = {} as FieldAttrSet; - } - this.fieldAttrs[fieldName][attrName] = value; + const fieldAttrs = this.fieldAttrs.get(fieldName) || {}; + this.fieldAttrs.set(fieldName, { ...fieldAttrs, [attrName]: value }); } /** @@ -368,7 +366,7 @@ export abstract class AbstractDataView { const stringifyOrUndefined = (obj: any) => (obj ? JSON.stringify(obj) : undefined); return { - fieldAttrs: stringifyOrUndefined(this.fieldAttrs), + fieldAttrs: stringifyOrUndefined(Object.fromEntries(this.fieldAttrs.entries())), title: this.getIndexPattern(), timeFieldName: this.timeFieldName, sourceFilters: stringifyOrUndefined(this.sourceFilters), @@ -385,7 +383,7 @@ export abstract class AbstractDataView { protected toSpecShared(includeFields = true): DataViewSpec { // if fields aren't included, don't include count - const fieldAttrs = cloneDeep(this.fieldAttrs); + const fieldAttrs = Object.fromEntries(this.fieldAttrs.entries()); if (!includeFields) { Object.keys(fieldAttrs).forEach((key) => { delete fieldAttrs[key].count; @@ -546,5 +544,8 @@ export abstract class AbstractDataView { this.runtimeFieldMap[name] = removeFieldAttrs(runtimeField); } - getFieldAttrs = () => cloneDeep(this.fieldAttrs); + getFieldAttrs = () => { + const clonedFieldAttrs = cloneDeep(Object.fromEntries(this.fieldAttrs.entries())); + return new Map(Object.entries(clonedFieldAttrs)); + }; } diff --git a/src/plugins/data_views/common/data_views/data_view.test.ts b/src/plugins/data_views/common/data_views/data_view.test.ts index 854340099f143..ebb148bdb2740 100644 --- a/src/plugins/data_views/common/data_views/data_view.test.ts +++ b/src/plugins/data_views/common/data_views/data_view.test.ts @@ -523,7 +523,7 @@ describe('IndexPattern', () => { }, }, }); - expect(dataView.getFieldAttrs()).toMatchInlineSnapshot(` + expect(Object.fromEntries(dataView.getFieldAttrs().entries())).toMatchInlineSnapshot(` Object { "test1": Object { "count": 5, @@ -555,7 +555,7 @@ describe('IndexPattern', () => { }, }, }); - expect(dataView.getFieldAttrs()).toMatchInlineSnapshot(` + expect(Object.fromEntries(dataView.getFieldAttrs().entries())).toMatchInlineSnapshot(` Object { "test1": Object { "count": 2, diff --git a/src/plugins/data_views/common/data_views/data_view_lazy.ts b/src/plugins/data_views/common/data_views/data_view_lazy.ts index 2d456fa1773a6..7b16af56c0601 100644 --- a/src/plugins/data_views/common/data_views/data_view_lazy.ts +++ b/src/plugins/data_views/common/data_views/data_view_lazy.ts @@ -132,11 +132,12 @@ export class DataViewLazy extends AbstractDataView { return col; } if (!cachedField) { + const fldAttrs = this.fieldAttrs.get(field.name) || {}; cachedField = new DataViewField({ ...field, - count: this.fieldAttrs?.[field.name]?.count, - customLabel: this.fieldAttrs?.[field.name]?.customLabel, - customDescription: this.fieldAttrs?.[field.name]?.customDescription, + count: fldAttrs.count, + customLabel: fldAttrs.customLabel, + customDescription: fldAttrs.customDescription, shortDotsEnable: this.shortDotsEnable, }); this.fieldCache.set(field.name, cachedField); @@ -338,6 +339,7 @@ export class DataViewLazy extends AbstractDataView { runtimeField: RuntimeFieldSpec, parentName?: string ) => { + const fldAttrs = this.fieldAttrs.get(name) || {}; spec[name] = { name, type: castEsToKbnFieldTypeName(fieldType), @@ -346,9 +348,9 @@ export class DataViewLazy extends AbstractDataView { aggregatable: true, searchable: true, readFromDocValues: false, - customLabel: this.fieldAttrs?.[name]?.customLabel, - customDescription: this.fieldAttrs?.[name]?.customDescription, - count: this.fieldAttrs?.[name]?.count, + customLabel: fldAttrs.customLabel, + customDescription: fldAttrs.customDescription, + count: fldAttrs.count, }; if (parentName) { @@ -391,14 +393,15 @@ export class DataViewLazy extends AbstractDataView { if (fld && !fld.scripted && fld.isMapped) { this.fieldCache.delete(field.name); } + const fldAttrs = this.fieldAttrs.get(field.name) || {}; fld = new DataViewField({ ...field, scripted: true, searchable: true, aggregatable: true, - count: this.fieldAttrs?.[field.name]?.count, - customLabel: this.fieldAttrs?.[field.name]?.customLabel, - customDescription: this.fieldAttrs?.[field.name]?.customDescription, + count: fldAttrs.count, + customLabel: fldAttrs.customLabel, + customDescription: fldAttrs.customDescription, }); this.fieldCache.set(field.name, fld); dataViewFields[field.name] = fld; @@ -437,11 +440,12 @@ export class DataViewLazy extends AbstractDataView { fld.spec.runtimeField = undefined; // unset if it was a runtime field but now mapped fld.spec.isMapped = true; } else { + const fldAttrs = this.fieldAttrs.get(field.name) || {}; fld = new DataViewField({ ...field, - count: this.fieldAttrs?.[field.name]?.count, - customLabel: this.fieldAttrs?.[field.name]?.customLabel, - customDescription: this.fieldAttrs?.[field.name]?.customDescription, + count: fldAttrs.count, + customLabel: fldAttrs.customLabel, + customDescription: fldAttrs.customDescription, shortDotsEnable: this.shortDotsEnable, }); this.fieldCache.set(field.name, fld); diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index e159aa55cf771..5fe44fa230a15 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -28,6 +28,7 @@ import { DataViewSpec, DataViewAttributes, FieldAttrs, + FieldAttrsAsObject, FieldSpec, DataViewFieldMap, TypeMeta, @@ -187,7 +188,7 @@ export interface DataViewsServicePublicMethods { * @params fieldAttrs - Field attributes, map by name * @returns Field map by name */ - fieldArrayToMap: (fields: FieldSpec[], fieldAttrs?: FieldAttrs | undefined) => DataViewFieldMap; + fieldArrayToMap: (fields: FieldSpec[], fieldAttrs?: FieldAttrsAsObject) => DataViewFieldMap; /** * Search for data views based on title * @param search - Search string @@ -657,7 +658,8 @@ export class DataViewsService { const scripted = this.scriptedFieldsEnabled ? indexPattern.getScriptedFields().map((field) => field.spec) : []; - const fieldAttrs = indexPattern.getFieldAttrs(); + + const fieldAttrs = Object.fromEntries(indexPattern.getFieldAttrs().entries()); const fieldsWithSavedAttrs = Object.values( this.fieldArrayToMap([...fields, ...scripted], fieldAttrs) ); @@ -720,7 +722,7 @@ export class DataViewsService { id: string, title: string, options: GetFieldsOptions, - fieldAttrs: FieldAttrs = {}, + fieldAttrs: FieldAttrsAsObject = {}, displayErrors: boolean = true ) => { const fieldsAsArr = Object.values(fields); @@ -771,7 +773,7 @@ export class DataViewsService { * @param fieldAttrs: FieldAttrs * @returns Record */ - fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrs) => + fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrsAsObject) => fields.reduce((collector, field) => { collector[field.name] = { ...field, @@ -813,7 +815,7 @@ export class DataViewsService { const parsedTypeMeta = typeMeta ? JSON.parse(typeMeta) : undefined; const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; - const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {}; + const parsedFieldAttrs: FieldAttrsAsObject = fieldAttrs ? JSON.parse(fieldAttrs) : {}; const parsedRuntimeFieldMap: Record = runtimeFieldMap ? JSON.parse(runtimeFieldMap) : {}; @@ -872,7 +874,10 @@ export class DataViewsService { displayErrors ); - const runtimeFieldSpecs = this.getRuntimeFields(runtimeFieldMap, spec.fieldAttrs); + const runtimeFieldSpecs = this.getRuntimeFields( + runtimeFieldMap, + new Map(Object.entries(spec.fieldAttrs || {})) + ); // mapped fields overwrite runtime fields return { fields: { ...runtimeFieldSpecs, ...fields }, indices: indices || [], etag }; }; @@ -938,7 +943,7 @@ export class DataViewsService { private getRuntimeFields = ( runtimeFieldMap: Record | undefined = {}, - fieldAttrs: FieldAttrs | undefined = {} + fieldAttrs: FieldAttrs | undefined = new Map() ) => { const spec: DataViewFieldMap = {}; @@ -948,6 +953,7 @@ export class DataViewsService { runtimeField: RuntimeFieldSpec, parentName?: string ) => { + const fldAttrs = fieldAttrs.get(name) || {}; spec[name] = { name, type: castEsToKbnFieldTypeName(fieldType), @@ -956,9 +962,9 @@ export class DataViewsService { aggregatable: true, searchable: true, readFromDocValues: false, - customLabel: fieldAttrs?.[name]?.customLabel, - customDescription: fieldAttrs?.[name]?.customDescription, - count: fieldAttrs?.[name]?.count, + customLabel: fldAttrs.customLabel, + customDescription: fldAttrs.customDescription, + count: fldAttrs.count, }; if (parentName) { diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 068c3a74f6867..45f747691fc4b 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -173,10 +173,7 @@ export interface DataViewAttributes { * @public * Storage of field attributes. Necessary since the field list isn't saved. */ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type FieldAttrs = { - [key: string]: FieldAttrSet; -}; +export type FieldAttrs = Map; /** * Field attributes that are stored on the data view @@ -198,6 +195,8 @@ export type FieldAttrSet = { count?: number; }; +export type FieldAttrsAsObject = Record; + /** * Handler for data view notifications * @public @@ -537,7 +536,7 @@ export type DataViewSpec = { /** * Map of field attributes by field name, currently customName and count */ - fieldAttrs?: FieldAttrs; + fieldAttrs?: FieldAttrsAsObject; /** * Determines whether failure to load field list should be reported as error */ diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts index f6df48b22ba7e..004b3e90d4b59 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts @@ -255,7 +255,7 @@ describe('LogViewsClient class', () => { "deleteFieldFormat": [Function], "deleteScriptedFieldInternal": [Function], "etag": undefined, - "fieldAttrs": Object {}, + "fieldAttrs": Map {}, "fieldFormatMap": Object {}, "fieldFormats": Object { "deserialize": [MockFunction],