Skip to content

Commit

Permalink
[8.8] [Security solution] Grouping count bug (#156206) (#156310)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to `8.8`:
- [[Security solution] Grouping count bug
(#156206)](#156206)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Steph
Milovic","email":"[email protected]"},"sourceCommit":{"committedDate":"2023-05-01T21:58:23Z","message":"[Security
solution] Grouping count bug
(#156206)","sha":"7a0620f132fe34c03c5ffe70cc118d52b5ec4dbd","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Threat
Hunting","Team: SecuritySolution","Team:Threat
Hunting:Explore","v8.8.0","Feature:Alerts
Grouping","v8.9.0"],"number":156206,"url":"https://github.com/elastic/kibana/pull/156206","mergeCommit":{"message":"[Security
solution] Grouping count bug
(#156206)","sha":"7a0620f132fe34c03c5ffe70cc118d52b5ec4dbd"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"8.8","label":"v8.8.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/156206","number":156206,"mergeCommit":{"message":"[Security
solution] Grouping count bug
(#156206)","sha":"7a0620f132fe34c03c5ffe70cc118d52b5ec4dbd"}}]}]
BACKPORT-->

Co-authored-by: Steph Milovic <[email protected]>
  • Loading branch information
kibanamachine and stephmilovic authored May 1, 2023
1 parent 775569b commit 064a141
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const mockGroupingProps = {
activePage: 0,
data: {
groupsCount: {
value: 2,
value: 3,
},
groupByFields: {
doc_count_error_upper_bound: 0,
Expand Down Expand Up @@ -121,7 +121,10 @@ export const mockGroupingProps = {
],
},
unitsCount: {
value: 3,
value: 14,
},
unitsCountWithoutNull: {
value: 14,
},
},
groupingId: 'test-grouping-id',
Expand Down
14 changes: 2 additions & 12 deletions packages/kbn-securitysolution-grouping/src/components/grouping.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,13 @@ const GroupingComponent = <T,>({
const [trigger, setTrigger] = useState<Record<string, { state: 'open' | 'closed' | undefined }>>(
{}
);
const [nullCount, setNullCount] = useState({ unit: 0, group: 0 });

const unitCount = useMemo(
() => (data?.unitsCount?.value ?? 0) + nullCount.unit,
[data?.unitsCount?.value, nullCount.unit]
);
const unitCount = useMemo(() => data?.unitsCount?.value ?? 0, [data?.unitsCount?.value]);
const unitCountText = useMemo(() => {
return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`;
}, [unitCount, unit]);

const groupCount = useMemo(
() => (data?.groupsCount?.value ?? 0) + nullCount.group,
[data?.groupsCount?.value, nullCount.group]
);
const groupCount = useMemo(() => data?.groupsCount?.value ?? 0, [data?.groupsCount?.value]);
const groupCountText = useMemo(
() => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`,
[groupCount]
Expand All @@ -106,9 +99,6 @@ const GroupingComponent = <T,>({
const nullGroupMessage = isNullGroup
? NULL_GROUP(selectedGroup, unit(groupBucket.doc_count))
: undefined;
if (isNullGroup) {
setNullCount({ unit: groupBucket.doc_count, group: 1 });
}

return (
<span key={groupKey}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export interface RootAggregation<T> {
unitsCount?: {
value?: number | null;
};
unitsCountWithoutNull?: {
value?: number | null;
};
}

export type GroupingFieldTotalAggregation<T> = Record<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { GroupingQueryArgs } from './types';
import { getGroupingQuery, parseGroupingQuery } from '.';
import { getEmptyValue } from './helpers';
import { GroupingAggregation } from '../../..';

const testProps: GroupingQueryArgs = {
additionalFilters: [],
Expand Down Expand Up @@ -154,43 +155,76 @@ describe('group selector', () => {
});
});
});

const groupingAggs = {
groupByFields: {
buckets: [
{
key: ['20.80.64.28', '20.80.64.28'],
key_as_string: '20.80.64.28|20.80.64.28',
doc_count: 75,
},
{
key: ['0.0.0.0', '0.0.0.0'],
key_as_string: '0.0.0.0|0.0.0.0',
doc_count: 75,
},
{
key: ['0.0.0.0', '::'],
key_as_string: '0.0.0.0|::',
doc_count: 75,
},
],
},
unitsCount: {
value: 100,
},
unitsCountWithoutNull: {
value: 100,
},
groupsCount: {
value: 20,
},
};
it('parseGroupingQuery finds and flags the null group', () => {
const data = [
{
key: ['20.80.64.28', '20.80.64.28'],
key_as_string: '20.80.64.28|20.80.64.28',
doc_count: 75,
},
{
key: ['0.0.0.0', '0.0.0.0'],
key_as_string: '0.0.0.0|0.0.0.0',
doc_count: 75,
},
{
key: ['0.0.0.0', '::'],
key_as_string: '0.0.0.0|::',
doc_count: 75,
const result = parseGroupingQuery(groupingAggs);
expect(result).toEqual({
groupByFields: {
buckets: [
{
key: ['20.80.64.28'],
key_as_string: '20.80.64.28',
doc_count: 75,
},
{
key: ['0.0.0.0'],
key_as_string: '0.0.0.0',
doc_count: 75,
},
{
key: [getEmptyValue()],
key_as_string: getEmptyValue(),
isNullGroup: true,
doc_count: 75,
},
],
},
];
const result = parseGroupingQuery(data);
expect(result).toEqual([
{
key: ['20.80.64.28'],
key_as_string: '20.80.64.28',
doc_count: 75,
unitsCount: {
value: 100,
},
{
key: ['0.0.0.0'],
key_as_string: '0.0.0.0',
doc_count: 75,
unitsCountWithoutNull: {
value: 100,
},
{
key: [getEmptyValue()],
key_as_string: getEmptyValue(),
isNullGroup: true,
doc_count: 75,
groupsCount: {
value: 20,
},
]);
});
});
it('parseGroupingQuery adjust group count when null field group is present', () => {
const result: GroupingAggregation<{}> = parseGroupingQuery({
...groupingAggs,
unitsCountWithoutNull: { value: 99 },
});

expect(result.groupsCount?.value).toEqual(21);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
*/

import { getEmptyValue, getFieldTypeMissingValues } from './helpers';
import { GroupingBucket } from '../..';
import { RawBucket } from '../../..';
import { GroupingAggregation } from '../..';
import type { GroupingQueryArgs, GroupingQuery } from './types';
/** The maximum number of groups to render */
export const DEFAULT_GROUP_BY_FIELD_SIZE = 10;
Expand Down Expand Up @@ -84,6 +83,16 @@ export const getGroupingQuery = ({
: {}),
},
},

unitsCountWithoutNull: { value_count: { field: groupByField } },
unitsCount: {
value_count: {
field: groupByField,
missing: getFieldTypeMissingValues(selectedGroupEsTypes)[0],
},
},
groupsCount: { cardinality: { field: groupByField } },

...(rootAggregations
? rootAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {})
: {}),
Expand Down Expand Up @@ -113,9 +122,12 @@ export const getGroupingQuery = ({
* @param buckets buckets returned from the grouping query
*/
export const parseGroupingQuery = <T>(
buckets: Array<RawBucket<T>>
): Array<RawBucket<T> & GroupingBucket> =>
buckets.map((group) => {
aggs?: GroupingAggregation<T>
): GroupingAggregation<T> | {} => {
if (!aggs) {
return {};
}
const groupByFields = aggs?.groupByFields?.buckets?.map((group) => {
if (!Array.isArray(group.key)) {
return group;
}
Expand All @@ -135,3 +147,15 @@ export const parseGroupingQuery = <T>(
isNullGroup: true,
};
});

return {
...aggs,
groupByFields: { buckets: groupByFields },
groupsCount: {
value:
(aggs.unitsCount?.value !== aggs.unitsCountWithoutNull?.value
? (aggs.groupsCount?.value ?? 0) + 1
: aggs.groupsCount?.value) ?? 0,
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,17 @@ describe('GroupedAlertsTable', () => {
},
},
groupsCount: { cardinality: { field: 'kibana.alert.rule.name' } },
unitsCount: { value_count: { field: 'kibana.alert.rule.name' } },
unitsCount: {
value_count: {
field: 'kibana.alert.rule.name',
missing: '-',
},
},
unitsCountWithoutNull: {
value_count: {
field: 'kibana.alert.rule.name',
},
},
},
query: {
bool: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ export const GroupedSubLevelComponent: React.FC<AlertsTableComponentProps> = ({
skip: isNoneGroup([selectedGroup]),
});

const buckets = useMemo(
() => parseGroupingQuery(alertsGroupsData?.aggregations?.groupByFields?.buckets ?? []),
const aggs = useMemo(
() => parseGroupingQuery(alertsGroupsData?.aggregations),
[alertsGroupsData]
);

Expand Down Expand Up @@ -248,10 +248,7 @@ export const GroupedSubLevelComponent: React.FC<AlertsTableComponentProps> = ({
activePage: pageIndex,
data: {
...alertsGroupsData?.aggregations,
groupByFields: {
...alertsGroupsData?.aggregations?.groupByFields,
buckets,
},
...aggs,
},
groupingLevel,
inspectButton: inspect,
Expand All @@ -268,7 +265,7 @@ export const GroupedSubLevelComponent: React.FC<AlertsTableComponentProps> = ({
getGrouping,
pageIndex,
alertsGroupsData,
buckets,
aggs,
groupingLevel,
inspect,
loading,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,7 @@ RuleNameGroupContent.displayName = 'RuleNameGroup';

const HostNameGroupContent = React.memo<{ hostName: string | string[]; nullGroupMessage?: string }>(
({ hostName, nullGroupMessage }) => (
<EuiFlexGroup
data-test-subj="host-name-group-renderer"
gutterSize="s"
alignItems="center"
justifyContent="center"
>
<EuiFlexGroup data-test-subj="host-name-group-renderer" gutterSize="s" alignItems="center">
<EuiFlexItem
grow={false}
style={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ describe('getAlertsGroupingQuery', () => {
_source: false,
aggs: {
unitsCount: {
value_count: {
field: 'kibana.alert.rule.name',
missing: '-',
},
},
unitsCountWithoutNull: {
value_count: {
field: 'kibana.alert.rule.name',
},
Expand Down Expand Up @@ -197,6 +203,12 @@ describe('getAlertsGroupingQuery', () => {
_source: false,
aggs: {
unitsCount: {
value_count: {
field: 'process.name',
missing: '-',
},
},
unitsCountWithoutNull: {
value_count: {
field: 'process.name',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,6 @@ export const getAlertsGroupingQuery = ({
? getAggregationsByGroupField(selectedGroup)
: [],
pageNumber: pageIndex * pageSize,
rootAggregations: [
{
unitsCount: { value_count: { field: selectedGroup } },
},
...(!isNoneGroup([selectedGroup])
? [{ groupsCount: { cardinality: { field: selectedGroup } } }]
: []),
],
runtimeMappings,
selectedGroupEsTypes,
size: pageSize,
Expand Down

0 comments on commit 064a141

Please sign in to comment.