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

141189 alerts table performance #141385

Merged
merged 11 commits into from
Sep 27, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,59 @@

import { EuiDataGridColumn } from '@elastic/eui';

const remove = ({ columnIds, index }: { columnIds: string[]; index: number }) => {
return [...columnIds.slice(0, index), ...columnIds.slice(index + 1)];
const remove = ({ columns, index }: { columns: EuiDataGridColumn[]; index: number }) => {
return [...columns.slice(0, index), ...columns.slice(index + 1)];
};

const insert = ({
columnId,
columnIds,
column,
columns,
defaultColumns,
}: {
columnId: string;
columnIds: string[];
column: EuiDataGridColumn;
columns: EuiDataGridColumn[];
defaultColumns: EuiDataGridColumn[];
}) => {
const defaultIndex = defaultColumns.findIndex(
(column: EuiDataGridColumn) => column.id === columnId
(defaultColumn: EuiDataGridColumn) => defaultColumn.id === column.id
);
const isInDefaultConfig = defaultIndex >= 0;

// if the column isn't shown but it's part of the default config
// insert into the same position as in the default config
if (isInDefaultConfig) {
return [...columnIds.slice(0, defaultIndex), columnId, ...columnIds.slice(defaultIndex)];
return [...columns.slice(0, defaultIndex), column, ...columns.slice(defaultIndex)];
}

// if the column isn't shown and it's not part of the default config
// push it into the second position. Behaviour copied by t_grid, security
// does this to insert right after the timestamp column
return [columnIds[0], columnId, ...columnIds.slice(1)];
return [columns[0], column, ...columns.slice(1)];
};

/**
* @param param.columnId id of the column to be removed/inserted
* @param param.columnIds Current array of columnIds in the grid
* @param param.defaultColumns Those initial columns set up in the configuration before being modified by the user
* @returns the new list of columns to be shown
* @param param.column column to be removed/inserted
* @param param.columns current array of columns in the grid
* @param param.defaultColumns Initial columns set up in the configuration before being modified by the user
* @returns the new list of columns
*/
export const toggleColumn = ({
columnId,
columnIds,
column,
columns,
defaultColumns,
}: {
columnId: string;
columnIds: string[];
column: EuiDataGridColumn;
columns: EuiDataGridColumn[];
defaultColumns: EuiDataGridColumn[];
}): string[] => {
const currentIndex = columnIds.indexOf(columnId);
}): EuiDataGridColumn[] => {
const currentIndex = columns.findIndex(
(currentColumn: EuiDataGridColumn) => currentColumn.id === column.id
);
const isVisible = currentIndex >= 0;

if (isVisible) {
return remove({ columnIds, index: currentIndex });
return remove({ columns, index: currentIndex });
}

return insert({ defaultColumns, columnId, columnIds });
return insert({ defaultColumns, column, columns });
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import { EuiDataGridColumn } from '@elastic/eui';
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { BrowserField, BrowserFields } from '@kbn/rule-registry-plugin/common';
import { useCallback, useEffect, useState } from 'react';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { AlertsTableStorage } from '../../alerts_table_state';
import { useFetchBrowserFieldCapabilities } from '../use_fetch_browser_fields_capabilities';
import { toggleColumn } from './toggle_column';
import { useFetchBrowserFieldCapabilities } from '../use_fetch_browser_fields_capabilities';

interface UseColumnsArgs {
featureIds: AlertConsumers[];
Expand All @@ -29,54 +29,76 @@ const fieldTypeToDataGridColumnTypeMapper = (fieldType: string | undefined) => {
return fieldType;
};

const getFieldCategoryFromColumnId = (columnId: string): string => {
const fieldName = columnId.split('.');

if (fieldName.length === 1) {
return 'base';
}

return fieldName[0];
};
Comment on lines +32 to +40
Copy link
Contributor

Choose a reason for hiding this comment

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

I love this one, it simplifies the search to constant time 💯
The only exception to that criteria are runtime fields, but I don't think you have to deal with them on your side yet.


/**
* EUI Data Grid expects the columns to have a property 'schema' defined for proper sorting
* this schema as its own types as can be check out in the docs so we add it here manually
* https://eui.elastic.co/#/tabular-content/data-grid-schema-columns
*/
const euiColumnFactory = (
column: EuiDataGridColumn,
browserFields: BrowserFields
columnId: string,
browserFields: BrowserFields,
defaultColumns: EuiDataGridColumn[]
): EuiDataGridColumn => {
const browserFieldsProps = getBrowserFieldProps(column.id, browserFields);
const defaultColumn = getColumnByColumnId(defaultColumns, columnId);
const column = defaultColumn ? defaultColumn : { id: columnId };

const browserFieldsProps = getBrowserFieldProps(columnId, browserFields);
return {
...column,
schema: fieldTypeToDataGridColumnTypeMapper(browserFieldsProps.type),
};
};

/**
* Searches in browser fields object for a specific field
*/
const getBrowserFieldProps = (
columnId: string,
browserFields: BrowserFields
): Partial<BrowserField> => {
for (const [, categoryDescriptor] of Object.entries(browserFields)) {
if (!categoryDescriptor.fields) {
continue;
}

for (const [fieldName, fieldDescriptor] of Object.entries(categoryDescriptor.fields)) {
if (fieldName === columnId) {
return fieldDescriptor;
}
}
const notFoundSpecs = { type: 'string' };

if (!browserFields || Object.keys(browserFields).length === 0) {
return notFoundSpecs;
}

const category = getFieldCategoryFromColumnId(columnId);
if (!browserFields[category]) {
return notFoundSpecs;
}

const categorySpecs = browserFields[category].fields;
if (!categorySpecs) {
return notFoundSpecs;
}
return { type: 'string' };

const fieldSpecs = categorySpecs[columnId];
return fieldSpecs ? fieldSpecs : notFoundSpecs;
};

const isPopulatedColumn = (column: EuiDataGridColumn) => Boolean(column.schema);

/**
* @param columns Columns to be considered in the alerts table
* @param browserFields constant object with all field capabilities
* @returns columns but with the info needed by the data grid to work as expected, e.g sorting
*/
const populateColumns = (
columns: EuiDataGridColumn[],
browserFields: BrowserFields
browserFields: BrowserFields,
defaultColumns: EuiDataGridColumn[]
): EuiDataGridColumn[] => {
return columns.map((column: EuiDataGridColumn) => {
return euiColumnFactory(column, browserFields);
return isPopulatedColumn(column)
? column
: euiColumnFactory(column.id, browserFields, defaultColumns);
});
};

Expand Down Expand Up @@ -128,10 +150,10 @@ export const useColumns = ({
useEffect(() => {
if (isBrowserFieldDataLoading !== false || isColumnsPopulated) return;

const populatedColumns = populateColumns(columns, browserFields);
const populatedColumns = populateColumns(columns, browserFields, defaultColumns);
setColumnsPopulated(true);
setColumns(populatedColumns);
}, [browserFields, columns, isBrowserFieldDataLoading, isColumnsPopulated]);
}, [browserFields, columns, defaultColumns, isBrowserFieldDataLoading, isColumnsPopulated]);

const setColumnsAndSave = useCallback(
(newColumns: EuiDataGridColumn[]) => {
Expand All @@ -156,15 +178,12 @@ export const useColumns = ({

const onToggleColumn = useCallback(
(columnId: string): void => {
const newColumnIds = toggleColumn({
columnId,
columnIds: getColumnIds(columns),
defaultColumns,
});
const column = euiColumnFactory(columnId, browserFields, defaultColumns);

const newColumns = newColumnIds.map((_columnId: string) => {
const column = getColumnByColumnId(defaultColumns, _columnId);
return euiColumnFactory(column ? column : { id: _columnId }, browserFields);
const newColumns = toggleColumn({
column,
columns,
defaultColumns,
});

setColumnsAndSave(newColumns);
Expand All @@ -173,7 +192,7 @@ export const useColumns = ({
);

const onResetColumns = useCallback(() => {
const populatedDefaultColumns = populateColumns(defaultColumns, browserFields);
const populatedDefaultColumns = populateColumns(defaultColumns, browserFields, defaultColumns);
setColumnsAndSave(populatedDefaultColumns);
}, [browserFields, defaultColumns, setColumnsAndSave]);

Expand Down