diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts index 5529d5bf521c0..14d7c44daf1f1 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts @@ -30,3 +30,22 @@ export const createGroupFilter = (selectedGroup: string, query?: string) => }, ] : []; + +export const getNullGroupFilter = (selectedGroup: string) => [ + { + meta: { + disabled: false, + negate: true, + alias: null, + key: selectedGroup, + field: selectedGroup, + value: 'exists', + type: 'exists', + }, + query: { + exists: { + field: selectedGroup, + }, + }, + }, +]; diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx index c1d55495cf785..f40fc79eb8c80 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiIconTip } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { firstNonNullValue } from '../../helpers'; import type { RawBucket } from '../types'; -import { createGroupFilter } from './helpers'; +import { createGroupFilter, getNullGroupFilter } from './helpers'; interface GroupPanelProps { customAccordionButtonClassName?: string; @@ -22,20 +22,35 @@ interface GroupPanelProps { groupPanelRenderer?: JSX.Element; groupingLevel?: number; isLoading: boolean; + isNullGroup?: boolean; + nullGroupMessage?: string; onGroupClose: () => void; onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void; renderChildComponent: (groupFilter: Filter[]) => React.ReactElement; selectedGroup: string; } -const DefaultGroupPanelRenderer = ({ title }: { title: string }) => ( +const DefaultGroupPanelRenderer = ({ + isNullGroup, + title, + nullGroupMessage, +}: { + isNullGroup: boolean; + title: string; + nullGroupMessage?: string; +}) => (
- +

{title}

+ {isNullGroup && nullGroupMessage && ( + + + + )}
); @@ -49,10 +64,12 @@ const GroupPanelComponent = ({ groupPanelRenderer, groupingLevel = 0, isLoading, + isNullGroup = false, onGroupClose, onToggleGroup, renderChildComponent, selectedGroup, + nullGroupMessage, }: GroupPanelProps) => { const lastForceState = useRef(forceState); useEffect(() => { @@ -68,8 +85,11 @@ const GroupPanelComponent = ({ const groupFieldValue = useMemo(() => firstNonNullValue(groupBucket.key), [groupBucket.key]); const groupFilters = useMemo( - () => createGroupFilter(selectedGroup, groupFieldValue), - [groupFieldValue, selectedGroup] + () => + isNullGroup + ? getNullGroupFilter(selectedGroup) + : createGroupFilter(selectedGroup, groupFieldValue), + [groupFieldValue, isNullGroup, selectedGroup] ); const onToggle = useCallback( @@ -86,7 +106,13 @@ const GroupPanelComponent = ({ buttonClassName={customAccordionButtonClassName} buttonContent={
- {groupPanelRenderer ?? } + {groupPanelRenderer ?? ( + + )}
} buttonElement="div" diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx index 0647c14485fad..412644910db99 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx @@ -8,10 +8,8 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -export const rule1Name = 'Rule 1 name'; -const rule1Desc = 'Rule 1 description'; -export const rule2Name = 'Rule 2 name'; -const rule2Desc = 'Rule 2 description'; +export const host1Name = 'nice-host'; +export const host2Name = 'cool-host'; export const mockGroupingProps = { activePage: 0, @@ -24,13 +22,13 @@ export const mockGroupingProps = { sum_other_doc_count: 0, buckets: [ { - key: [rule1Name, rule1Desc], - key_as_string: `${rule1Name}|${rule1Desc}`, + key: [host1Name], + key_as_string: `${host1Name}`, doc_count: 1, hostsCountAggregation: { value: 1, }, - ruleTags: { + hostTags: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [], @@ -56,13 +54,13 @@ export const mockGroupingProps = { }, }, { - key: [rule2Name, rule2Desc], - key_as_string: `${rule2Name}|${rule2Desc}`, + key: [host2Name], + key_as_string: `${host2Name}`, doc_count: 1, hostsCountAggregation: { value: 1, }, - ruleTags: { + hostTags: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [], @@ -87,10 +85,43 @@ export const mockGroupingProps = { value: 1, }, }, + { + key: ['-'], + key_as_string: `-`, + isNullGroup: true, + doc_count: 11, + hostsCountAggregation: { + value: 11, + }, + hostTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + alertsCount: { + value: 11, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 11, + }, + ], + }, + countSeveritySubAggregation: { + value: 11, + }, + usersCountAggregation: { + value: 11, + }, + }, ], }, unitsCount: { - value: 2, + value: 3, }, }, groupingId: 'test-grouping-id', @@ -98,7 +129,7 @@ export const mockGroupingProps = { itemsPerPage: 25, renderChildComponent: () =>

{'child component'}

, onGroupClose: () => {}, - selectedGroup: 'kibana.alert.rule.name', + selectedGroup: 'host.name', takeActionItems: () => [ {}}> {'Mark as acknowledged'} diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx index b961402ee3a7c..ed08d8541d0e8 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx @@ -23,6 +23,6 @@ export default { }, }; -export const Emtpy: Story = () => { +export const Empty: Story = () => { return ; }; diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx index 2376614ab444c..151b83e45cfd5 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx @@ -10,11 +10,11 @@ import { fireEvent, render, within } from '@testing-library/react'; import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { Grouping } from './grouping'; -import { createGroupFilter } from './accordion_panel/helpers'; +import { createGroupFilter, getNullGroupFilter } from './accordion_panel/helpers'; import { METRIC_TYPE } from '@kbn/analytics'; import { getTelemetryEvent } from '../telemetry/const'; -import { mockGroupingProps, rule1Name, rule2Name } from './grouping.mock'; +import { mockGroupingProps, host1Name, host2Name } from './grouping.mock'; const renderChildComponent = jest.fn(); const takeActionItems = jest.fn(); @@ -37,9 +37,9 @@ describe('grouping container', () => { ); - expect(getByTestId('unit-count').textContent).toBe('2 events'); - expect(getByTestId('group-count').textContent).toBe('2 groups'); - expect(getAllByTestId('grouping-accordion').length).toBe(2); + expect(getByTestId('unit-count').textContent).toBe('14 events'); + expect(getByTestId('group-count').textContent).toBe('3 groups'); + expect(getAllByTestId('grouping-accordion').length).toBe(3); expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument(); }); @@ -79,12 +79,12 @@ describe('grouping container', () => { fireEvent.click(group1); expect(renderChildComponent).toHaveBeenNthCalledWith( 1, - createGroupFilter(testProps.selectedGroup, rule1Name) + createGroupFilter(testProps.selectedGroup, host1Name) ); fireEvent.click(group2); expect(renderChildComponent).toHaveBeenNthCalledWith( 2, - createGroupFilter(testProps.selectedGroup, rule2Name) + createGroupFilter(testProps.selectedGroup, host2Name) ); }); @@ -116,4 +116,24 @@ describe('grouping container', () => { }) ); }); + + it('Renders a null group and passes the correct filter to take actions and child component', () => { + takeActionItems.mockReturnValue([]); + const { getAllByTestId, getByTestId } = render( + + + + ); + expect(getByTestId('null-group-icon')).toBeInTheDocument(); + + let lastGroup = getAllByTestId('grouping-accordion').at(-1); + fireEvent.click(within(lastGroup!).getByTestId('take-action-button')); + + expect(takeActionItems).toHaveBeenCalledWith(getNullGroupFilter('host.name'), 2); + + lastGroup = getAllByTestId('grouping-accordion').at(-1); + fireEvent.click(within(lastGroup!).getByTestId('group-panel-toggle')); + + expect(renderChildComponent).toHaveBeenCalledWith(getNullGroupFilter('host.name')); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 625beda320d04..8182d204f81dd 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -17,12 +17,12 @@ import type { Filter } from '@kbn/es-query'; import React, { useMemo, useState } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { defaultUnit, firstNonNullValue } from '../helpers'; -import { createGroupFilter } from './accordion_panel/helpers'; +import { createGroupFilter, getNullGroupFilter } from './accordion_panel/helpers'; import { GroupPanel } from './accordion_panel'; import { GroupStats } from './accordion_panel/group_stats'; import { EmptyGroupingComponent } from './empty_results_panel'; import { countCss, groupingContainerCss, groupingContainerCssLevel } from './styles'; -import { GROUPS_UNIT } from './translations'; +import { GROUPS_UNIT, NULL_GROUP } from './translations'; import type { GroupingAggregation, GroupPanelRenderer } from './types'; import { GroupStatsRenderer, OnGroupToggle } from './types'; import { getTelemetryEvent } from '../telemetry/const'; @@ -78,13 +78,20 @@ const GroupingComponent = ({ const [trigger, setTrigger] = useState>( {} ); + const [nullCount, setNullCount] = useState({ unit: 0, group: 0 }); - const unitCount = data?.unitsCount?.value ?? 0; + const unitCount = useMemo( + () => (data?.unitsCount?.value ?? 0) + nullCount.unit, + [data?.unitsCount?.value, nullCount.unit] + ); const unitCountText = useMemo(() => { return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`; }, [unitCount, unit]); - const groupCount = data?.groupsCount?.value ?? 0; + const groupCount = useMemo( + () => (data?.groupsCount?.value ?? 0) + nullCount.group, + [data?.groupsCount?.value, nullCount.group] + ); const groupCountText = useMemo( () => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`, [groupCount] @@ -95,15 +102,28 @@ const GroupingComponent = ({ data?.groupByFields?.buckets?.map((groupBucket, groupNumber) => { const group = firstNonNullValue(groupBucket.key); const groupKey = `group-${groupNumber}-${group}`; + const isNullGroup = groupBucket.isNullGroup ?? false; + const nullGroupMessage = isNullGroup + ? NULL_GROUP(selectedGroup, unit(groupBucket.doc_count)) + : undefined; + if (isNullGroup) { + setNullCount({ unit: groupBucket.doc_count, group: 1 }); + } return ( ({ forceState={(trigger[groupKey] && trigger[groupKey].state) ?? 'closed'} groupBucket={groupBucket} groupPanelRenderer={ - groupPanelRenderer && groupPanelRenderer(selectedGroup, groupBucket) + groupPanelRenderer && + groupPanelRenderer(selectedGroup, groupBucket, nullGroupMessage) } isLoading={isLoading} onToggleGroup={(isOpen) => { @@ -157,8 +178,10 @@ const GroupingComponent = ({ takeActionItems, tracker, trigger, + unit, ] ); + const pageCount = useMemo( () => (groupCount ? Math.ceil(groupCount / itemsPerPage) : 1), [groupCount, itemsPerPage] diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-securitysolution-grouping/src/components/translations.ts index f2cb8a172dbdc..2209d61d1b34f 100644 --- a/packages/kbn-securitysolution-grouping/src/components/translations.ts +++ b/packages/kbn-securitysolution-grouping/src/components/translations.ts @@ -54,3 +54,10 @@ export const DEFAULT_UNIT = (totalCount: number) => values: { totalCount }, defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, }); + +export const NULL_GROUP = (selectedGroup: string, unit: string) => + i18n.translate('grouping.nullGroup.title', { + values: { selectedGroup, unit }, + defaultMessage: + 'The selected group by field, {selectedGroup}, is missing a value for this group of {unit}.', + }); diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index cf5f55f5c27f3..3ed5cc43ff877 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -12,16 +12,19 @@ export interface GenericBuckets { key_as_string?: string; // contains, for example, formatted dates doc_count: number; } - export const NONE_GROUP_KEY = 'none'; export type RawBucket = GenericBuckets & T; +export interface GroupingBucket { + isNullGroup?: boolean; +} + /** Defines the shape of the aggregation returned by Elasticsearch */ // TODO: write developer docs for these fields export interface RootAggregation { groupByFields?: { - buckets?: Array>; + buckets?: Array & GroupingBucket>; }; groupsCount?: { value?: number | null; @@ -60,7 +63,8 @@ export type GroupStatsRenderer = ( export type GroupPanelRenderer = ( selectedGroup: string, - fieldBucket: RawBucket + fieldBucket: RawBucket, + nullGroupMessage?: string ) => JSX.Element | undefined; export type OnGroupToggle = (params: { diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts new file mode 100644 index 0000000000000..0de0e66fe3df0 --- /dev/null +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ES_FIELD_TYPES } from '@kbn/field-types'; +/** + * Returns a tuple of values according to the `esType` param, these values are meant to be applied in the _missing_ + * property of the query aggregation of the grouping, to look up for missing values in the response buckets. + * These values do not need to be anything in particular, the only requirement is they have to be 2 different values that validate against the field type. + */ +export function getFieldTypeMissingValues(esType: string[]): [number, number] | [string, string] { + const knownType: ES_FIELD_TYPES = esType[0] as ES_FIELD_TYPES; + switch (knownType) { + case ES_FIELD_TYPES.BYTE: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.SHORT: + case ES_FIELD_TYPES.UNSIGNED_LONG: + case ES_FIELD_TYPES.DATE: + case ES_FIELD_TYPES.DATE_NANOS: + return [0, 1]; + case ES_FIELD_TYPES.IP: + return ['0.0.0.0', '::']; + default: + return ['-', '--']; + } +} + +export const getEmptyValue = () => '—'; diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts index c3f81c4071c58..c3ab014119db7 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts @@ -7,12 +7,13 @@ */ import type { GroupingQueryArgs } from './types'; -import { getGroupingQuery, MAX_QUERY_SIZE } from '.'; +import { getGroupingQuery, parseGroupingQuery } from '.'; +import { getEmptyValue } from './helpers'; const testProps: GroupingQueryArgs = { additionalFilters: [], from: '2022-12-28T15:35:32.871Z', - groupByFields: ['host.name'], + groupByField: 'host.name', statsAggregations: [ { alertsCount: { @@ -53,6 +54,7 @@ const testProps: GroupingQueryArgs = { pageNumber: 0, rootAggregations: [], runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], size: 25, to: '2023-02-23T06:59:59.999Z', }; @@ -60,12 +62,13 @@ describe('group selector', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('Sets terms query when single stackBy field requested', () => { + it('Sets multi terms query with missing argument for 2 default values', () => { const result = getGroupingQuery(testProps); - expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); - expect(result.aggs.groupByFields.terms).toEqual({ - field: 'host.name', - size: MAX_QUERY_SIZE, + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? '-' : '--', + }); }); expect(result.aggs.groupByFields.aggs).toEqual({ bucket_truncate: { bucket_sort: { from: 0, size: 25 } }, @@ -79,7 +82,7 @@ describe('group selector', () => { expect(result.aggs.groupsNumber).toBeUndefined(); expect(result.query.bool.filter.length).toEqual(1); }); - it('Sets terms query when single stackBy field requested additionalAggregationsRoot', () => { + it('Sets additional rootAggregations', () => { const result = getGroupingQuery({ ...testProps, rootAggregations: [ @@ -105,17 +108,6 @@ describe('group selector', () => { }); expect(result.aggs.groupsNumber).toEqual({ cardinality: { field: 'host.name' } }); }); - it('Sets terms query when multiple stackBy fields requested', () => { - const result = getGroupingQuery({ - ...testProps, - groupByFields: ['kibana.alert.rule.name', 'kibana.alert.rule.description'], - }); - expect(result.aggs.groupByFields.terms).toBeUndefined(); - expect(result.aggs.groupByFields.multi_terms).toEqual({ - terms: [{ field: 'kibana.alert.rule.name' }, { field: 'kibana.alert.rule.description' }], - size: MAX_QUERY_SIZE, - }); - }); it('Additional filters get added to the query', () => { const result = getGroupingQuery({ ...testProps, @@ -144,4 +136,61 @@ describe('group selector', () => { }); expect(result.query.bool.filter.length).toEqual(2); }); + it('Uses 0/1 for number fields', () => { + const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['long'] }); + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? 0 : 1, + }); + }); + }); + it('Uses 0.0.0.0/:: for ip fields', () => { + const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['ip'] }); + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? '0.0.0.0' : '::', + }); + }); + }); + + 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(data); + expect(result).toEqual([ + { + 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, + }, + ]); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 23699c1ccf94a..e4440bc50e249 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -6,6 +6,9 @@ * Side Public License, v 1. */ +import { getEmptyValue, getFieldTypeMissingValues } from './helpers'; +import { GroupingBucket } from '../..'; +import { RawBucket } from '../../..'; import type { GroupingQueryArgs, GroupingQuery } from './types'; /** The maximum number of groups to render */ export const DEFAULT_GROUP_BY_FIELD_SIZE = 10; @@ -24,6 +27,7 @@ export const MAX_QUERY_SIZE = 10000; * @param rootAggregations Top level aggregations to get the groups number or overall groups metrics. * Array of {@link NamedAggregation} * @param runtimeMappings mappings of runtime fields [see runtimeMappings]{@link GroupingQueryArgs.runtimeMappings} + * @param selectedGroupEsTypes array of selected group types * @param size number of grouping results per page * @param sort add one or more sorts on specific fields * @param statsAggregations group level aggregations which correspond to {@link GroupStatsRenderer} configuration @@ -31,14 +35,16 @@ export const MAX_QUERY_SIZE = 10000; * * @returns query dsl {@link GroupingQuery} */ + export const getGroupingQuery = ({ additionalFilters = [], from, - groupByFields, + groupByField, + pageNumber, rootAggregations, runtimeMappings, + selectedGroupEsTypes, size = DEFAULT_GROUP_BY_FIELD_SIZE, - pageNumber, sort, statsAggregations, to, @@ -46,21 +52,25 @@ export const getGroupingQuery = ({ size: 0, aggs: { groupByFields: { - ...(groupByFields.length > 1 - ? { - multi_terms: { - terms: groupByFields.map((groupByField) => ({ - field: groupByField, - })), - size: MAX_QUERY_SIZE, - }, - } - : { - terms: { - field: groupByFields[0], - size: MAX_QUERY_SIZE, - }, - }), + multi_terms: { + terms: [ + // by looking up multiple missing values, we can ensure we're not overwriting an existing group with the default value + { + field: groupByField, + // the AggregationsMultiTermLookup type is wrong in the elasticsearch node package + // when this issues is resolved, we can remove these ts expect errors + // https://github.com/elastic/elasticsearch/issues/95628 + // @ts-expect-error + missing: getFieldTypeMissingValues(selectedGroupEsTypes)[0], + }, + { + field: groupByField, + // @ts-expect-error + missing: getFieldTypeMissingValues(selectedGroupEsTypes)[1], + }, + ], + size: MAX_QUERY_SIZE, + }, aggs: { bucket_truncate: { bucket_sort: { @@ -96,3 +106,32 @@ export const getGroupingQuery = ({ runtime_mappings: runtimeMappings, _source: false, }); + +/** + * Parses the grouping query response to add the isNullGroup + * flag to the buckets and to format the bucket keys + * @param buckets buckets returned from the grouping query + */ +export const parseGroupingQuery = ( + buckets: Array> +): Array & GroupingBucket> => + buckets.map((group) => { + if (!Array.isArray(group.key)) { + return group; + } + const emptyValue = getEmptyValue(); + // If the keys are different means that the `missing` values of the multi_terms aggregation have been applied, we use the default empty string. + // If the keys are equal means the `missing` values have not been applied, they are stored values. + return group.key[0] === group.key[1] + ? { + ...group, + key: [group.key[0]], + key_as_string: group.key[0], + } + : { + ...group, + key: [emptyValue], + key_as_string: emptyValue, + isNullGroup: true, + }; + }); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts index 5a8b2f822fb5c..4a370094d0d5c 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts @@ -23,11 +23,12 @@ export type NamedAggregation = Record; statsAggregations?: NamedAggregation[]; @@ -37,8 +38,7 @@ export interface GroupingQueryArgs { export interface MainAggregation extends NamedAggregation { groupByFields: { aggs: NamedAggregation; - multi_terms?: estypes.AggregationsAggregationContainer['multi_terms']; - terms?: estypes.AggregationsAggregationContainer['terms']; + multi_terms: estypes.AggregationsAggregationContainer['multi_terms']; }; } diff --git a/packages/kbn-securitysolution-grouping/tsconfig.json b/packages/kbn-securitysolution-grouping/tsconfig.json index 621ba68957cf1..efecb457f96de 100644 --- a/packages/kbn-securitysolution-grouping/tsconfig.json +++ b/packages/kbn-securitysolution-grouping/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/kibana-react-plugin", "@kbn/shared-svg", "@kbn/ui-theme", - "@kbn/analytics" + "@kbn/analytics", + "@kbn/field-types" ] } diff --git a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts index e21a5a519fc51..e6d5d67daf2bf 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts @@ -97,6 +97,15 @@ export const getBrowserFieldPath = (field: string, browserFields: BrowserFields) return [splitFields[0], 'fields', field]; }; +export const getFieldEsTypes = (field: string, browserFields: BrowserFields): string[] => { + const pathBrowserField = getBrowserFieldPath(field, browserFields); + const browserField = get(pathBrowserField, browserFields); + if (browserField != null) { + return browserField.esTypes; + } + return []; +}; + export const checkIfFieldTypeIsDate = (field: string, browserFields: BrowserFields) => { const pathBrowserField = getBrowserFieldPath(field, browserFields); const browserField = get(pathBrowserField, browserFields); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index 4a57d7cac8e73..dae31cb56a79b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -155,15 +155,9 @@ describe('GroupedAlertsTable', () => { if (i.skip) { return mockQueryResponse; } - if (i.query.aggs.groupByFields.multi_terms != null) { - return { - ...mockQueryResponse, - data: groupingSearchResponse.ruleName, - }; - } return { ...mockQueryResponse, - data: i.query.aggs.groupByFields.terms.field != null ? groupingSearchResponse.hostName : {}, + data: groupingSearchResponse, }; }); }); @@ -214,6 +208,68 @@ describe('GroupedAlertsTable', () => { expect(within(level0).getByTestId('alerts-table')).toBeInTheDocument(); }); + it('Query gets passed correctly', () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name'])); + + render( + + + + ); + expect(mockUseQueryAlerts).toHaveBeenLastCalledWith({ + indexName: 'test', + query: { + _source: false, + aggs: { + groupByFields: { + aggs: { + bucket_truncate: { + bucket_sort: { from: 0, size: 25, sort: [{ unitsCount: { order: 'desc' } }] }, + }, + countSeveritySubAggregation: { cardinality: { field: 'kibana.alert.severity' } }, + hostsCountAggregation: { cardinality: { field: 'host.name' } }, + description: { terms: { field: 'kibana.alert.rule.description', size: 1 } }, + ruleTags: { terms: { field: 'kibana.alert.rule.tags' } }, + severitiesSubAggregation: { terms: { field: 'kibana.alert.severity' } }, + unitsCount: { cardinality: { field: 'kibana.alert.uuid' } }, + usersCountAggregation: { cardinality: { field: 'user.name' } }, + }, + multi_terms: { + size: 10000, + terms: [ + { field: 'kibana.alert.rule.name', missing: '-' }, + { field: 'kibana.alert.rule.name', missing: '--' }, + ], + }, + }, + groupsCount: { cardinality: { field: 'kibana.alert.rule.name' } }, + unitsCount: { value_count: { field: 'kibana.alert.rule.name' } }, + }, + query: { + bool: { + filter: [ + { bool: { filter: [], must: [], must_not: [], should: [] } }, + { + range: { + '@timestamp': { + gte: '2020-07-07T08:20:18.966Z', + lte: '2020-07-08T08:20:18.966Z', + }, + }, + }, + ], + }, + }, + runtime_mappings: {}, + size: 0, + }, + queryName: 'securitySolutionUI fetchAlerts grouping', + skip: false, + }); + }); + it('renders grouping table in second accordion level when 2 groups are selected', async () => { jest .spyOn(window.localStorage, 'getItem') diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index 99ae3f4ff3433..968e415f38214 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -132,10 +132,6 @@ const GroupedAlertsTableComponent: React.FC = (props) setPageIndex((curr) => curr.map(() => DEFAULT_PAGE_INDEX)); }, []); - useEffect(() => { - resetAllPagination(); - }, [resetAllPagination, selectedGroups]); - const setPageVar = useCallback( (newNumber: number, groupingLevel: number, pageType: 'index' | 'size') => { if (pageType === 'index') { @@ -158,23 +154,31 @@ const GroupedAlertsTableComponent: React.FC = (props) [setStoragePageSize] ); - const nonGroupingFilters = useRef({ + const paginationResetTriggers = useRef({ defaultFilters: props.defaultFilters, globalFilters: props.globalFilters, globalQuery: props.globalQuery, + selectedGroups, }); useEffect(() => { - const nonGrouping = { + const triggers = { defaultFilters: props.defaultFilters, globalFilters: props.globalFilters, globalQuery: props.globalQuery, + selectedGroups, }; - if (!isEqual(nonGroupingFilters.current, nonGrouping)) { + if (!isEqual(paginationResetTriggers.current, triggers)) { resetAllPagination(); - nonGroupingFilters.current = nonGrouping; + paginationResetTriggers.current = triggers; } - }, [props.defaultFilters, props.globalFilters, props.globalQuery, resetAllPagination]); + }, [ + props.defaultFilters, + props.globalFilters, + props.globalQuery, + resetAllPagination, + selectedGroups, + ]); const getLevel = useCallback( (level: number, selectedGroup: string, parentGroupingFilter?: string) => { @@ -184,6 +188,7 @@ const GroupedAlertsTableComponent: React.FC = (props) return getLevel( level + 1, selectedGroups[level + 1], + // stringify because if the filter is passed as an object, it will cause unnecessary re-rendering JSON.stringify([ ...groupingFilters, ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx index cf8a8128cb166..6fefc30e556cc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -15,7 +15,8 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common'; import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import { combineQueries } from '../../../common/lib/kuery'; +import { parseGroupingQuery } from '@kbn/securitysolution-grouping/src'; +import { combineQueries, getFieldEsTypes } from '../../../common/lib/kuery'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import type { AlertsGroupingAggregation } from './grouping_settings/types'; import type { Status } from '../../../../common/detection_engine/schemas/common'; @@ -140,17 +141,32 @@ export const GroupedSubLevelComponent: React.FC = ({ } }, [defaultFilters, globalFilters, globalQuery, parentGroupingFilter]); + const selectedGroupEsTypes = useMemo( + () => getFieldEsTypes(selectedGroup, browserFields), + [selectedGroup, browserFields] + ); + const queryGroups = useMemo(() => { return getAlertsGroupingQuery({ additionalFilters, selectedGroup, + selectedGroupEsTypes, from, runtimeMappings, to, pageSize, pageIndex, }); - }, [additionalFilters, from, pageIndex, pageSize, runtimeMappings, selectedGroup, to]); + }, [ + additionalFilters, + from, + pageIndex, + pageSize, + runtimeMappings, + selectedGroup, + selectedGroupEsTypes, + to, + ]); const emptyGlobalQuery = useMemo(() => getGlobalQuery([]), [getGlobalQuery]); @@ -177,6 +193,11 @@ export const GroupedSubLevelComponent: React.FC = ({ skip: isNoneGroup([selectedGroup]), }); + const buckets = useMemo( + () => parseGroupingQuery(alertsGroupsData?.aggregations?.groupByFields?.buckets ?? []), + [alertsGroupsData] + ); + useEffect(() => { if (!isNoneGroup([selectedGroup])) { setAlertsQuery(queryGroups); @@ -225,7 +246,13 @@ export const GroupedSubLevelComponent: React.FC = ({ () => getGrouping({ activePage: pageIndex, - data: alertsGroupsData?.aggregations, + data: { + ...alertsGroupsData?.aggregations, + groupByFields: { + ...alertsGroupsData?.aggregations?.groupByFields, + buckets, + }, + }, groupingLevel, inspectButton: inspect, isLoading: loading || isLoadingGroups, @@ -238,20 +265,21 @@ export const GroupedSubLevelComponent: React.FC = ({ takeActionItems: getTakeActionItems, }), [ - alertsGroupsData?.aggregations, getGrouping, - getTakeActionItems, + pageIndex, + alertsGroupsData, + buckets, groupingLevel, inspect, - isLoadingGroups, loading, - pageIndex, + isLoadingGroups, pageSize, renderChildComponent, onGroupClose, selectedGroup, - setPageIndex, + getTakeActionItems, setPageSize, + setPageIndex, ] ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx index ff70bfe8e47ce..2038bddea952e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx @@ -5,51 +5,74 @@ * 2.0. */ -import { shallow } from 'enzyme'; - import { renderGroupPanel } from '.'; +import { render } from '@testing-library/react'; describe('renderGroupPanel', () => { it('renders correctly when the field renderer exists', () => { - const wrapperRuleName = shallow( - renderGroupPanel('kibana.alert.rule.name', { - key: ['Rule name test', 'Some description'], - doc_count: 10, - })! + let { getByTestId } = render( + renderGroupPanel( + 'kibana.alert.rule.name', + { + key: ['Rule name test', 'Some description'], + doc_count: 10, + }, + 'This is a null group!' + )! ); - expect(wrapperRuleName.find('[data-test-subj="rule-name-group-renderer"]')).toBeTruthy(); - const wrapperHostName = shallow( - renderGroupPanel('host.name', { - key: 'Host', - doc_count: 2, - })! + expect(getByTestId('rule-name-group-renderer')).toBeInTheDocument(); + const result1 = render( + renderGroupPanel( + 'host.name', + { + key: 'Host', + doc_count: 2, + }, + 'This is a null group!' + )! ); + getByTestId = result1.getByTestId; + + expect(getByTestId('host-name-group-renderer')).toBeInTheDocument(); - expect(wrapperHostName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy(); - const wrapperUserName = shallow( - renderGroupPanel('user.name', { - key: 'User test', - doc_count: 1, - })! + const result2 = render( + renderGroupPanel( + 'user.name', + { + key: 'User test', + doc_count: 1, + }, + 'This is a null group!' + )! ); + getByTestId = result2.getByTestId; - expect(wrapperUserName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy(); - const wrapperSourceIp = shallow( - renderGroupPanel('source.ip', { - key: 'sourceIp', - doc_count: 23, - })! + expect(getByTestId('host-name-group-renderer')).toBeInTheDocument(); + const result3 = render( + renderGroupPanel( + 'source.ip', + { + key: 'sourceIp', + doc_count: 23, + }, + 'This is a null group!' + )! ); + getByTestId = result3.getByTestId; - expect(wrapperSourceIp.find('[data-test-subj="source-ip-group-renderer"]')).toBeTruthy(); + expect(getByTestId('source-ip-group-renderer')).toBeInTheDocument(); }); it('returns undefined when the renderer does not exist', () => { - const wrapper = renderGroupPanel('process.name', { - key: 'process', - doc_count: 10, - }); + const wrapper = renderGroupPanel( + 'process.name', + { + key: 'process', + doc_count: 10, + }, + 'This is a null group!' + ); expect(wrapper).toBeUndefined(); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index ffea45007fc6d..2781b78247cf6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiIconTip, EuiText, EuiTextColor, EuiTitle, @@ -18,32 +19,35 @@ import { import { euiThemeVars } from '@kbn/ui-theme'; import { isArray } from 'lodash/fp'; import React from 'react'; -import type { RawBucket } from '@kbn/securitysolution-grouping'; +import type { GroupPanelRenderer } from '@kbn/securitysolution-grouping/src'; import type { AlertsGroupingAggregation } from './types'; import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers'; import type { GenericBuckets } from '../../../../../common/search_strategy'; import { PopoverItems } from '../../../../common/components/popover_items'; import { COLUMN_TAGS } from '../../../pages/detection_engine/rules/translations'; -export const renderGroupPanel = ( - selectedGroup: string, - bucket: RawBucket +export const renderGroupPanel: GroupPanelRenderer = ( + selectedGroup, + bucket, + nullGroupMessage ) => { switch (selectedGroup) { case 'kibana.alert.rule.name': return isArray(bucket.key) ? ( ) : undefined; case 'host.name': - return ; + return ; case 'user.name': - return ; + return ; case 'source.ip': - return ; + return ; } }; @@ -89,66 +93,87 @@ const RuleNameGroupContent = React.memo<{ }); RuleNameGroupContent.displayName = 'RuleNameGroup'; -const HostNameGroupContent = React.memo<{ hostName: string | string[] }>(({ hostName }) => ( - - ( + ({ hostName, nullGroupMessage }) => ( + - - - - - -
{hostName}
-
-
-
-)); -HostNameGroupContent.displayName = 'HostNameGroupContent'; - -const UserNameGroupContent = React.memo<{ userName: string | string[] }>(({ userName }) => { - const userNameValue = firstNonNullValue(userName) ?? '-'; - return ( - - - + + - + -
{userName}
+
{hostName}
+ {nullGroupMessage && ( + + + + )}
- ); -}); + ) +); +HostNameGroupContent.displayName = 'HostNameGroupContent'; + +const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroupMessage?: string }>( + ({ userName, nullGroupMessage }) => { + const userNameValue = firstNonNullValue(userName) ?? '-'; + return ( + + + + + + + +
{userName}
+
+
+ {nullGroupMessage && ( + + + + )} +
+ ); + } +); UserNameGroupContent.displayName = 'UserNameGroupContent'; -const SourceIpGroupContent = React.memo<{ sourceIp: string | string[] }>(({ sourceIp }) => ( - - - - - - -
{sourceIp}
-
-
-
-)); +const SourceIpGroupContent = React.memo<{ sourceIp: string | string[]; nullGroupMessage?: string }>( + ({ sourceIp, nullGroupMessage }) => ( + + + + + + +
{sourceIp}
+
+
+ {nullGroupMessage && ( + + + + )} +
+ ) +); SourceIpGroupContent.displayName = 'SourceIpGroupContent'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts index 9e0b4e63715aa..c9717c2a1ad2b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts @@ -8,1729 +8,1339 @@ import { mockAlertSearchResponse } from '../../../../common/components/alerts_treemap/lib/mocks/mock_alert_search_response'; export const groupingSearchResponse = { - ruleName: { - ...mockAlertSearchResponse, - hits: { - total: { - value: 6048, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - groupsCount: { - value: 32, - }, - groupByFields: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: ['critical hosts [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical users [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['critical users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['high users [Duplicate]', 'f'], - key_as_string: 'high users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['high users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['low users [Duplicate]', 'f'], - key_as_string: 'low users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['low users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['medium users [Duplicate]', 'f'], - key_as_string: 'medium users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['medium users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['critical hosts', 'f'], - key_as_string: 'critical hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts', 'f'], - key_as_string: 'high hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts ', 'f'], - key_as_string: 'low hosts |f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts', 'f'], - key_as_string: 'medium hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical users [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 91, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 91, - }, - { - key: 'rule', - doc_count: 91, - }, - ], - }, - unitsCount: { - value: 91, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 91, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - ], - }, - unitsCount: { - value: 6048, - }, + ...mockAlertSearchResponse, + hits: { + total: { + value: 6048, + relation: 'eq', }, + max_score: null, + hits: [], }, - hostName: { - ...mockAlertSearchResponse, - hits: { - total: { - value: 900, - relation: 'eq', - }, - max_score: null, - hits: [], + aggregations: { + groupsCount: { + value: 32, + }, + groupByFields: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: ['critical hosts [Duplicate]', 'critical hosts [Duplicate]'], + key_as_string: 'critical hosts [Duplicate]|critical hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical hosts [Duplicate] [Duplicate]', 'critical hosts [Duplicate] [Duplicate]'], + key_as_string: + 'critical hosts [Duplicate] [Duplicate]|critical hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate]', 'high hosts [Duplicate]'], + key_as_string: 'high hosts [Duplicate]|high hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate] [Duplicate]', 'high hosts [Duplicate] [Duplicate]'], + key_as_string: 'high hosts [Duplicate] [Duplicate]|high hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate]', 'low hosts [Duplicate]'], + key_as_string: 'low hosts [Duplicate]|low hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate] [Duplicate]', 'low hosts [Duplicate] [Duplicate]'], + key_as_string: 'low hosts [Duplicate] [Duplicate]|low hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate]', 'medium hosts [Duplicate]'], + key_as_string: 'medium hosts [Duplicate]|medium hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate] [Duplicate]', 'medium hosts [Duplicate] [Duplicate]'], + key_as_string: + 'medium hosts [Duplicate] [Duplicate]|medium hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical users [Duplicate]', 'critical users [Duplicate]'], + key_as_string: 'critical users [Duplicate]|critical users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: [ + 'critical users [Duplicate] [Duplicate]', + 'critical users [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical users [Duplicate] [Duplicate]|critical users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate]', 'high users [Duplicate]'], + key_as_string: 'high users [Duplicate]|high users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate] [Duplicate]', 'high users [Duplicate] [Duplicate]'], + key_as_string: 'high users [Duplicate] [Duplicate]|high users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate]', 'low users [Duplicate]'], + key_as_string: 'low users [Duplicate]|low users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate] [Duplicate]', 'low users [Duplicate] [Duplicate]'], + key_as_string: 'low users [Duplicate] [Duplicate]|low users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate]', 'medium users [Duplicate]'], + key_as_string: 'medium users [Duplicate]|medium users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate] [Duplicate]', 'medium users [Duplicate] [Duplicate]'], + key_as_string: + 'medium users [Duplicate] [Duplicate]|medium users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['critical hosts', 'critical hosts'], + key_as_string: 'critical hosts|critical hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'critical hosts [Duplicate] [Duplicate] [Duplicate]', + 'critical hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical hosts [Duplicate] [Duplicate] [Duplicate]|critical hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts', 'high hosts'], + key_as_string: 'high hosts|high hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'high hosts [Duplicate] [Duplicate] [Duplicate]', + 'high hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'high hosts [Duplicate] [Duplicate] [Duplicate]|high hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts ', 'low hosts '], + key_as_string: 'low hosts |low hosts ', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'low hosts [Duplicate] [Duplicate] [Duplicate]', + 'low hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'low hosts [Duplicate] [Duplicate] [Duplicate]|low hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts', 'medium hosts'], + key_as_string: 'medium hosts|medium hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'medium hosts [Duplicate] [Duplicate] [Duplicate]', + 'medium hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'medium hosts [Duplicate] [Duplicate] [Duplicate]|medium hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'critical users [Duplicate] [Duplicate] [Duplicate]', + 'critical users [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical users [Duplicate] [Duplicate] [Duplicate]|critical users [Duplicate] [Duplicate] [Duplicate]', + doc_count: 91, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 91, + }, + { + key: 'rule', + doc_count: 91, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 91, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 91, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], }, - aggregations: { - groupsCount: { - value: 40, - }, - groupByFields: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-f0m6ngo8fo', - doc_count: 75, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 75, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 75, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 25, - }, - }, - { - key: 'Host-4aijlqggv8', - doc_count: 63, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 63, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 63, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 21, - }, - }, - { - key: 'Host-e50lhbdm91', - doc_count: 51, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 51, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 51, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 17, - }, - }, - { - key: 'sqp', - doc_count: 42, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 42, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 42, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'sUl', - doc_count: 33, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 33, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 33, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'vLJ', - doc_count: 30, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 30, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 30, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Host-n28uwmsqmd', - doc_count: 27, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 27, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 27, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 9, - }, - }, - { - key: 'JaE', - doc_count: 27, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 27, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 27, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'CUA', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'FWT', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'ZqT', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'mmn', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'xRS', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'HiC', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Host-d7zbfvl3zz', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 7, - }, - }, - { - key: 'Nnc', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'OqH', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Vaw', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'XPg', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'qBS', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'rwt', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'xVJ', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Bxg', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'efP', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'qcb', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - ], - }, - unitsCount: { - value: 900, - }, + unitsCount: { + value: 6048, }, }, }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts index b4f8568da2c60..0d92882d4c53a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts @@ -15,6 +15,7 @@ describe('getAlertsGroupingQuery', () => { pageIndex: 0, pageSize: 25, runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], selectedGroup: 'kibana.alert.rule.name', additionalFilters: [ { @@ -60,11 +61,23 @@ describe('getAlertsGroupingQuery', () => { field: 'kibana.alert.uuid', }, }, + description: { + terms: { + field: 'kibana.alert.rule.description', + size: 1, + }, + }, bucket_truncate: { bucket_sort: { from: 0, size: 25, - sort: undefined, + sort: [ + { + unitsCount: { + order: 'desc', + }, + }, + ], }, }, countSeveritySubAggregation: { @@ -98,9 +111,11 @@ describe('getAlertsGroupingQuery', () => { terms: [ { field: 'kibana.alert.rule.name', + missing: '-', }, { - field: 'kibana.alert.rule.description', + field: 'kibana.alert.rule.name', + missing: '--', }, ], }, @@ -152,6 +167,7 @@ describe('getAlertsGroupingQuery', () => { pageIndex: 0, pageSize: 25, runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], selectedGroup: 'process.name', additionalFilters: [ { @@ -201,7 +217,13 @@ describe('getAlertsGroupingQuery', () => { bucket_sort: { from: 0, size: 25, - sort: undefined, + sort: [ + { + unitsCount: { + order: 'desc', + }, + }, + ], }, }, rulesCountAggregation: { @@ -210,9 +232,18 @@ describe('getAlertsGroupingQuery', () => { }, }, }, - terms: { - field: 'process.name', + multi_terms: { size: 10000, + terms: [ + { + field: 'process.name', + missing: '-', + }, + { + field: 'process.name', + missing: '--', + }, + ], }, }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 921a8d3e3d43f..cde272af8d498 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -10,14 +10,6 @@ import type { BoolQuery } from '@kbn/es-query'; import type { NamedAggregation } from '@kbn/securitysolution-grouping'; import { isNoneGroup, getGroupingQuery } from '@kbn/securitysolution-grouping'; -const getGroupFields = (groupValue: string) => { - if (groupValue === 'kibana.alert.rule.name') { - return [groupValue, 'kibana.alert.rule.description']; - } else { - return [groupValue]; - } -}; - interface AlertsGroupingQueryParams { additionalFilters: Array<{ bool: BoolQuery; @@ -27,6 +19,7 @@ interface AlertsGroupingQueryParams { pageSize: number; runtimeMappings: MappingRuntimeFields; selectedGroup: string; + selectedGroupEsTypes: string[]; to: string; } @@ -37,12 +30,13 @@ export const getAlertsGroupingQuery = ({ pageSize, runtimeMappings, selectedGroup, + selectedGroupEsTypes, to, }: AlertsGroupingQueryParams) => getGroupingQuery({ additionalFilters, from, - groupByFields: !isNoneGroup([selectedGroup]) ? getGroupFields(selectedGroup) : [], + groupByField: selectedGroup, statsAggregations: !isNoneGroup([selectedGroup]) ? getAggregationsByGroupField(selectedGroup) : [], @@ -56,7 +50,9 @@ export const getAlertsGroupingQuery = ({ : []), ], runtimeMappings, + selectedGroupEsTypes, size: pageSize, + sort: [{ unitsCount: { order: 'desc' } }], to, }); @@ -74,6 +70,14 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { case 'kibana.alert.rule.name': aggMetrics.push( ...[ + { + description: { + terms: { + field: 'kibana.alert.rule.description', + size: 1, + }, + }, + }, { countSeveritySubAggregation: { cardinality: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts index 72d3d95b86789..d271551f83460 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts @@ -12,6 +12,9 @@ export interface AlertsGroupingAggregation { unitsCount?: { value?: NumberOrNull; }; + description?: { + buckets?: GenericBuckets[]; + }; severitiesSubAggregation?: { buckets?: GenericBuckets[]; };