[data views] Use map object for field attributes (#193760)

## Summary

Use map object for field attributes

---------

Co-authored-by: Davis McPhee <davismcphee@hotmail.com>
This commit is contained in:
Matthew Kime 2024-09-27 00:17:44 -05:00 committed by GitHub
parent 9357d44372
commit 760455a0ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 40 deletions

View file

@ -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<string, FieldAttrSet>)
: [];
: {};
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));
};
}

View file

@ -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,

View file

@ -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);

View file

@ -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
@ -658,7 +659,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)
);
@ -721,7 +723,7 @@ export class DataViewsService {
id: string,
title: string,
options: GetFieldsOptions,
fieldAttrs: FieldAttrs = {},
fieldAttrs: FieldAttrsAsObject = {},
displayErrors: boolean = true
) => {
const fieldsAsArr = Object.values(fields);
@ -772,7 +774,7 @@ export class DataViewsService {
* @param fieldAttrs: FieldAttrs
* @returns Record<string, FieldSpec>
*/
fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrs) =>
fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrsAsObject) =>
fields.reduce<DataViewFieldMap>((collector, field) => {
collector[field.name] = {
...field,
@ -814,7 +816,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<string, RuntimeField> = runtimeFieldMap
? JSON.parse(runtimeFieldMap)
: {};
@ -877,7 +879,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 };
};
@ -946,7 +951,7 @@ export class DataViewsService {
private getRuntimeFields = (
runtimeFieldMap: Record<string, RuntimeFieldSpec> | undefined = {},
fieldAttrs: FieldAttrs | undefined = {}
fieldAttrs: FieldAttrs | undefined = new Map()
) => {
const spec: DataViewFieldMap = {};
@ -956,6 +961,7 @@ export class DataViewsService {
runtimeField: RuntimeFieldSpec,
parentName?: string
) => {
const fldAttrs = fieldAttrs.get(name) || {};
spec[name] = {
name,
type: castEsToKbnFieldTypeName(fieldType),
@ -964,9 +970,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) {

View file

@ -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<string, FieldAttrSet>;
/**
* Field attributes that are stored on the data view
@ -198,6 +195,8 @@ export type FieldAttrSet = {
count?: number;
};
export type FieldAttrsAsObject = Record<string, FieldAttrSet>;
/**
* 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
*/

View file

@ -255,7 +255,7 @@ describe('LogViewsClient class', () => {
"deleteFieldFormat": [Function],
"deleteScriptedFieldInternal": [Function],
"etag": undefined,
"fieldAttrs": Object {},
"fieldAttrs": Map {},
"fieldFormatMap": Object {},
"fieldFormats": Object {
"deserialize": [MockFunction],