mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Explain Log Rate Spikes: Fix client side code to transform groups into table rows. (#147592)
Fixes client side code to transform groups into table rows. Because the transformation used a dictionary like structure with field names as keys, we missed if there were multiple values for a field. This changes the structure to an array of field/value pairs so we can support multiple values per field.
This commit is contained in:
parent
141078e8f5
commit
92ffe2764f
5 changed files with 65 additions and 67 deletions
|
@ -27,16 +27,16 @@ const selectedGroupMock: GroupTableItem = {
|
|||
id: '21289599',
|
||||
docCount: 20468,
|
||||
pValue: 2.2250738585072626e-308,
|
||||
group: {
|
||||
'error.message': 'rate limit exceeded',
|
||||
message: 'too many requests',
|
||||
'user_agent.original.keyword': 'Mozilla/5.0',
|
||||
},
|
||||
repeatedValues: {
|
||||
'beat.hostname.keyword': 'ip-192-168-1-1',
|
||||
'beat.name.keyword': 'i-1234',
|
||||
'docker.container.id.keyword': 'asdf',
|
||||
},
|
||||
group: [
|
||||
{ fieldName: 'error.message', fieldValue: 'rate limit exceeded' },
|
||||
{ fieldName: 'message', fieldValue: 'too many requests' },
|
||||
{ fieldName: 'user_agent.original.keyword', fieldValue: 'Mozilla/5.0' },
|
||||
],
|
||||
repeatedValues: [
|
||||
{ fieldName: 'beat.hostname.keyword', fieldValue: 'ip-192-168-1-1' },
|
||||
{ fieldName: 'beat.name.keyword', fieldValue: 'i-1234' },
|
||||
{ fieldName: 'docker.container.id.keyword', fieldValue: 'asdf' },
|
||||
],
|
||||
histogram: [],
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
|||
|
||||
import { Query } from '@kbn/es-query';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import type { ChangePoint } from '@kbn/ml-agg-utils';
|
||||
import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils';
|
||||
import type { GroupTableItem } from '../../components/spike_analysis_table/spike_analysis_table_groups';
|
||||
|
||||
/*
|
||||
|
@ -52,11 +52,10 @@ export function buildBaseFilterCriteria(
|
|||
|
||||
const groupFilter = [];
|
||||
if (selectedGroup) {
|
||||
const allItems = { ...selectedGroup.group, ...selectedGroup.repeatedValues };
|
||||
for (const fieldName in allItems) {
|
||||
if (allItems.hasOwnProperty(fieldName)) {
|
||||
groupFilter.push({ term: { [fieldName]: allItems[fieldName] } });
|
||||
}
|
||||
const allItems: FieldValuePair[] = [...selectedGroup.group, ...selectedGroup.repeatedValues];
|
||||
for (const item of allItems) {
|
||||
const { fieldName, fieldValue } = item;
|
||||
groupFilter.push({ term: { [fieldName]: fieldValue } });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import type { WindowParameters } from '@kbn/aiops-utils';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { Query } from '@kbn/es-query';
|
||||
import type { FieldValuePair } from '@kbn/ml-agg-utils';
|
||||
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { initialState, streamReducer } from '../../../common/api/stream_reducer';
|
||||
|
@ -163,15 +164,15 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
|
|||
const sortedGroup = group.sort((a, b) =>
|
||||
a.fieldName > b.fieldName ? 1 : b.fieldName > a.fieldName ? -1 : 0
|
||||
);
|
||||
const dedupedGroup: Record<string, any> = {};
|
||||
const repeatedValues: Record<string, any> = {};
|
||||
const dedupedGroup: FieldValuePair[] = [];
|
||||
const repeatedValues: FieldValuePair[] = [];
|
||||
|
||||
sortedGroup.forEach((pair) => {
|
||||
const { fieldName, fieldValue } = pair;
|
||||
if (pair.duplicate === false) {
|
||||
dedupedGroup[fieldName] = fieldValue;
|
||||
dedupedGroup.push({ fieldName, fieldValue });
|
||||
} else {
|
||||
repeatedValues[fieldName] = fieldValue;
|
||||
repeatedValues.push({ fieldName, fieldValue });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -197,7 +198,7 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
|
|||
|
||||
const showSpikeAnalysisTable = data?.changePoints.length > 0;
|
||||
const groupItemCount = groupTableItems.reduce((p, c) => {
|
||||
return p + Object.keys(c.group).length;
|
||||
return p + c.group.length;
|
||||
}, 0);
|
||||
const foundGroups = groupTableItems.length > 0 && groupItemCount > 0;
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { escapeKuery } from '@kbn/es-query';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { ChangePoint } from '@kbn/ml-agg-utils';
|
||||
import type { ChangePoint, FieldValuePair } from '@kbn/ml-agg-utils';
|
||||
|
||||
import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
|
@ -58,8 +58,8 @@ export interface GroupTableItem {
|
|||
id: string;
|
||||
docCount: number;
|
||||
pValue: number | null;
|
||||
group: Record<string, string | number>;
|
||||
repeatedValues: Record<string, string | number>;
|
||||
group: FieldValuePair[];
|
||||
repeatedValues: FieldValuePair[];
|
||||
histogram: ChangePoint['histogram'];
|
||||
}
|
||||
|
||||
|
@ -99,23 +99,22 @@ export const SpikeAnalysisGroupsTable: FC<SpikeAnalysisTableProps> = ({
|
|||
const { group, repeatedValues } = item;
|
||||
|
||||
const expandedTableItems = [];
|
||||
const fullGroup = { ...group, ...repeatedValues };
|
||||
const fullGroup: FieldValuePair[] = [...group, ...repeatedValues];
|
||||
|
||||
for (const fieldName in fullGroup) {
|
||||
if (fullGroup.hasOwnProperty(fieldName)) {
|
||||
const fieldValue = fullGroup[fieldName];
|
||||
expandedTableItems.push({
|
||||
fieldName: `${fieldName}`,
|
||||
fieldValue: `${fullGroup[fieldName]}`,
|
||||
...(changePoints.find(
|
||||
(changePoint) =>
|
||||
(changePoint.fieldName === fieldName ||
|
||||
changePoint.fieldName === `${fieldName}.keyword`) &&
|
||||
(changePoint.fieldValue === fieldValue ||
|
||||
changePoint.fieldValue === `${fieldValue}.keyword`)
|
||||
) ?? {}),
|
||||
});
|
||||
}
|
||||
for (const fullGroupItem of fullGroup) {
|
||||
const { fieldName, fieldValue } = fullGroupItem;
|
||||
|
||||
expandedTableItems.push({
|
||||
...(changePoints.find(
|
||||
(changePoint) =>
|
||||
(changePoint.fieldName === fieldName ||
|
||||
changePoint.fieldName === `${fieldName}.keyword`) &&
|
||||
(changePoint.fieldValue === fieldValue ||
|
||||
changePoint.fieldValue === `${fieldValue}.keyword`)
|
||||
) ?? {}),
|
||||
fieldName: `${fieldName}`,
|
||||
fieldValue: `${fieldValue}`,
|
||||
});
|
||||
}
|
||||
|
||||
itemIdToExpandedRowMapValues[item.id] = (
|
||||
|
@ -178,12 +177,12 @@ export const SpikeAnalysisGroupsTable: FC<SpikeAnalysisTableProps> = ({
|
|||
query: {
|
||||
language: SEARCH_QUERY_LANGUAGE.KUERY,
|
||||
query: [
|
||||
...Object.entries(groupTableItem.group).map(
|
||||
([fieldName, fieldValue]) =>
|
||||
...groupTableItem.group.map(
|
||||
({ fieldName, fieldValue }) =>
|
||||
`${escapeKuery(fieldName)}:${escapeKuery(String(fieldValue))}`
|
||||
),
|
||||
...Object.entries(groupTableItem.repeatedValues).map(
|
||||
([fieldName, fieldValue]) =>
|
||||
...groupTableItem.repeatedValues.map(
|
||||
({ fieldName, fieldValue }) =>
|
||||
`${escapeKuery(fieldName)}:${escapeKuery(String(fieldValue))}`
|
||||
),
|
||||
].join(' AND '),
|
||||
|
@ -253,27 +252,26 @@ export const SpikeAnalysisGroupsTable: FC<SpikeAnalysisTableProps> = ({
|
|||
),
|
||||
render: (_, { group, repeatedValues }) => {
|
||||
const valuesBadges = [];
|
||||
const hasExtraBadges = Object.keys(group).length > MAX_GROUP_BADGES;
|
||||
const hasExtraBadges = group.length > MAX_GROUP_BADGES;
|
||||
|
||||
for (const fieldName in group) {
|
||||
if (group.hasOwnProperty(fieldName)) {
|
||||
if (valuesBadges.length === MAX_GROUP_BADGES) break;
|
||||
valuesBadges.push(
|
||||
<>
|
||||
<EuiBadge
|
||||
key={`${fieldName}-id`}
|
||||
data-test-subj="aiopsSpikeAnalysisTableColumnGroupBadge"
|
||||
color="hollow"
|
||||
>
|
||||
<span>{`${fieldName}: `}</span>
|
||||
<span style={{ color: visColors[2] }}>{`${group[fieldName]}`}</span>
|
||||
</EuiBadge>
|
||||
<EuiSpacer size="xs" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
for (const groupItem of group) {
|
||||
const { fieldName, fieldValue } = groupItem;
|
||||
if (valuesBadges.length === MAX_GROUP_BADGES) break;
|
||||
valuesBadges.push(
|
||||
<>
|
||||
<EuiBadge
|
||||
key={`${fieldName}-id`}
|
||||
data-test-subj="aiopsSpikeAnalysisTableColumnGroupBadge"
|
||||
color="hollow"
|
||||
>
|
||||
<span>{`${fieldName}: `}</span>
|
||||
<span style={{ color: visColors[2] }}>{`${fieldValue}`}</span>
|
||||
</EuiBadge>
|
||||
<EuiSpacer size="xs" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (Object.keys(repeatedValues).length > 0 || hasExtraBadges) {
|
||||
if (repeatedValues.length > 0 || hasExtraBadges) {
|
||||
valuesBadges.push(
|
||||
<>
|
||||
<EuiBadge
|
||||
|
@ -286,16 +284,16 @@ export const SpikeAnalysisGroupsTable: FC<SpikeAnalysisTableProps> = ({
|
|||
<FormattedMessage
|
||||
id="xpack.aiops.explainLogRateSpikes.spikeAnalysisTableGroups.moreLabel"
|
||||
defaultMessage="+{count, plural, one {# more field/value pair} other {# more field/value pairs}}"
|
||||
values={{ count: Object.keys(group).length - MAX_GROUP_BADGES }}
|
||||
values={{ count: group.length - MAX_GROUP_BADGES }}
|
||||
/>
|
||||
<br />
|
||||
</>
|
||||
) : null}
|
||||
{Object.keys(repeatedValues).length > 0 ? (
|
||||
{repeatedValues.length > 0 ? (
|
||||
<FormattedMessage
|
||||
id="xpack.aiops.explainLogRateSpikes.spikeAnalysisTableGroups.moreRepeatedLabel"
|
||||
defaultMessage="+{count, plural, one {# more field/value pair} other {# more field/value pairs}} also appearing in other groups"
|
||||
values={{ count: Object.keys(repeatedValues).length }}
|
||||
values={{ count: repeatedValues.length }}
|
||||
/>
|
||||
) : null}
|
||||
</EuiBadge>
|
||||
|
|
|
@ -55,7 +55,7 @@ export const artificialLogDataViewTestData: TestData = {
|
|||
totalDocCountFormatted: '8,400',
|
||||
analysisGroupsTable: [
|
||||
{ group: 'user: Peter', docCount: '1981' },
|
||||
{ group: 'response_code: 500url: login.php', docCount: '792' },
|
||||
{ group: 'response_code: 500url: home.phpurl: login.php', docCount: '792' },
|
||||
],
|
||||
analysisTable: [
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue