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

[ML][AIOps] Log rate analysis: ensure ability to sort on Log rate change #193501

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export function getLogRateChange(
values: { deviationBucketRate },
}
),
factor: deviationBucketRate,
};
}
} else {
Expand Down Expand Up @@ -76,6 +77,7 @@ export function getLogRateChange(
values: { baselineBucketRate },
}
),
factor: baselineBucketRate,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
*/

import type { FC } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { orderBy, isEqual } from 'lodash';
import React, { useEffect, useMemo } from 'react';
import { isEqual } from 'lodash';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

import type { EuiTableSortingType } from '@elastic/eui';
import { useEuiBackgroundColor, EuiBasicTable } from '@elastic/eui';
import { useEuiBackgroundColor, EuiInMemoryTable } from '@elastic/eui';

import type { SignificantItem } from '@kbn/ml-agg-utils';
import { useTableState } from '@kbn/ml-in-memory-table';
import {
setPinnedSignificantItem,
setSelectedSignificantItem,
Expand Down Expand Up @@ -86,13 +86,15 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =

const dispatch = useAppDispatch();

const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<keyof SignificantItem>(
zeroDocsFallback ? DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK : DEFAULT_SORT_FIELD
);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(
zeroDocsFallback ? DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK : DEFAULT_SORT_DIRECTION
const { onTableChange, pagination, sorting } = useTableState<SignificantItem>(
significantItems ?? [],
zeroDocsFallback ? DEFAULT_SORT_FIELD_ZERO_DOCS_FALLBACK : DEFAULT_SORT_FIELD,
zeroDocsFallback ? DEFAULT_SORT_DIRECTION_ZERO_DOCS_FALLBACK : DEFAULT_SORT_DIRECTION,
{
pageIndex: 0,
pageSize: 10,
pageSizeOptions: PAGINATION_SIZE_OPTIONS,
}
);

const columns = useColumns(
Expand All @@ -104,82 +106,26 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
groupFilter !== undefined
);

const onChange = useCallback((tableSettings: any) => {
if (tableSettings.page) {
const { index, size } = tableSettings.page;
setPageIndex(index);
setPageSize(size);
}

if (tableSettings.sort) {
const { field, direction } = tableSettings.sort;
setSortField(field);
setSortDirection(direction);
}
}, []);

const { pagination, pageOfItems, sorting } = useMemo(() => {
const pageStart = pageIndex * pageSize;
const itemCount = significantItems?.length ?? 0;

let items: SignificantItem[] = significantItems ?? [];

const sortIteratees = [
(item: SignificantItem) => {
if (item && typeof item[sortField] === 'string') {
// @ts-ignore Object is possibly null or undefined
return item[sortField].toLowerCase();
}
return item[sortField];
},
];
const sortDirections = [sortDirection];

// Only if the table is sorted by p-value, add a secondary sort by doc count.
if (sortField === 'pValue') {
sortIteratees.push((item: SignificantItem) => item.doc_count);
sortDirections.push(sortDirection);
}

items = orderBy(significantItems, sortIteratees, sortDirections);

return {
pageOfItems: items.slice(pageStart, pageStart + pageSize),
pagination: {
pageIndex,
pageSize,
totalItemCount: itemCount,
pageSizeOptions: PAGINATION_SIZE_OPTIONS,
},
sorting: {
sort: {
field: sortField,
direction: sortDirection,
},
},
};
}, [pageIndex, pageSize, sortField, sortDirection, significantItems]);

useEffect(() => {
// If no row is hovered or pinned or the user switched to a new page,
// fall back to set the first row into a hovered state to make the
// main document count chart show a comparison view by default.
if (
(selectedSignificantItem === null ||
!pageOfItems.some((item) => isEqual(item, selectedSignificantItem))) &&
!significantItems.some((item) => isEqual(item, selectedSignificantItem))) &&
pinnedSignificantItem === null &&
pageOfItems.length > 0 &&
significantItems.length > 0 &&
selectedGroup === null &&
pinnedGroup === null
) {
dispatch(setSelectedSignificantItem(pageOfItems[0]));
dispatch(setSelectedSignificantItem(significantItems[0]));
}

// If a user switched pages and a pinned row is no longer visible
// on the current page, set the status of pinned rows back to `null`.
if (
pinnedSignificantItem !== null &&
!pageOfItems.some((item) => isEqual(item, pinnedSignificantItem)) &&
!significantItems.some((item) => isEqual(item, pinnedSignificantItem)) &&
selectedGroup === null &&
pinnedGroup === null
) {
Expand All @@ -189,7 +135,7 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
dispatch,
selectedGroup,
selectedSignificantItem,
pageOfItems,
significantItems,
pinnedGroup,
pinnedSignificantItem,
]);
Expand Down Expand Up @@ -239,15 +185,15 @@ export const LogRateAnalysisResultsTable: FC<LogRateAnalysisResultsTableProps> =
// running the analysis and will hide this table.

return (
<EuiBasicTable
<EuiInMemoryTable<SignificantItem>
data-test-subj="aiopsLogRateAnalysisResultsTable"
compressed
items={significantItems}
columns={columns}
items={pageOfItems}
onChange={onChange}
pagination={pagination.totalItemCount > pagination.pageSize ? pagination : undefined}
pagination={pagination}
sorting={sorting}
loading={false}
sorting={sorting as EuiTableSortingType<SignificantItem>}
onTableChange={onTableChange}
rowProps={(significantItem) => {
return {
'data-test-subj': `aiopsLogRateAnalysisResultsTableRow row-${significantItem.fieldName}-${significantItem.fieldValue}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,32 @@ export const useColumns = (
/>
</>
),
sortable: ({ doc_count: docCount, bg_count: bgCount }: SignificantItem) => {
// TODO: Move this duplicated calculation into a separate function
if (
interval === 0 ||
currentAnalysisType === undefined ||
currentAnalysisWindowParameters === undefined ||
buckets === undefined ||
isGroupsTable
)
return NOT_AVAILABLE;

const { baselineBucketRate, deviationBucketRate } = getBaselineAndDeviationRates(
currentAnalysisType,
buckets.baselineBuckets,
buckets.deviationBuckets,
docCount,
bgCount
);

const logRateChange = getLogRateChange(
currentAnalysisType,
baselineBucketRate,
deviationBucketRate
);
return logRateChange.factor;
},
render: ({ doc_count: docCount, bg_count: bgCount }: SignificantItem) => {
if (
interval === 0 ||
Expand Down