Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Response Ops][Alerting] Adding group by options to ES query rule type #144689

Merged
merged 55 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
fc99107
Renaming folder
ymao1 Nov 2, 2022
57f2b79
Renaming alertType to ruleType. Adding parameters to support group by
ymao1 Nov 2, 2022
0611e2b
Renaming
ymao1 Nov 4, 2022
a024df4
Renaming
ymao1 Nov 4, 2022
c6f04fa
Merge branch 'rename-stack-alerts' into alerting/es-query-grouping
ymao1 Nov 4, 2022
db1f204
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 7, 2022
8f1830b
wip
ymao1 Nov 7, 2022
2614d77
Passing in fields from KQL search. Resetting selections when index se…
ymao1 Nov 7, 2022
682f1af
Moving aggregation builder into separate function for reuse
ymao1 Nov 8, 2022
6fb8b2f
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 8, 2022
c397547
wip
ymao1 Nov 9, 2022
98796e1
merge
ymao1 Nov 9, 2022
d8b203d
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 9, 2022
a4be551
front end dsl query and test working
ymao1 Nov 9, 2022
810f547
search source test query working
ymao1 Nov 9, 2022
cbc920a
Merging in main
ymao1 Nov 9, 2022
91a9cdd
Fixing CI checks
ymao1 Nov 9, 2022
12c222f
Updating server params
ymao1 Nov 14, 2022
5dd0f57
wip
ymao1 Nov 14, 2022
fb37c95
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 14, 2022
1077aee
Disabling deduplicate checkbox when group by is selected. Fixing bug …
ymao1 Nov 14, 2022
ce94cab
Updating rule executor
ymao1 Nov 14, 2022
89f7931
Fixing stuff
ymao1 Nov 14, 2022
6a35ffa
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 21, 2022
d498d34
Fixing some tests
ymao1 Nov 21, 2022
0de6582
Unit testing server code
ymao1 Nov 21, 2022
de2d9a5
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 21, 2022
97ce602
Fixing checks
ymao1 Nov 21, 2022
7b2be56
Adding functional tests
ymao1 Nov 21, 2022
6312d69
Adding functional tests
ymao1 Nov 21, 2022
c25e9a6
Respecing alert limit
ymao1 Nov 21, 2022
872d5da
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 21, 2022
194cf29
Adding migration
ymao1 Nov 21, 2022
1ad9861
Fixing checks
ymao1 Nov 21, 2022
db5b286
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 22, 2022
d93614e
Fixing edit rule
ymao1 Nov 22, 2022
7d343ee
Updating docs
ymao1 Nov 22, 2022
9d3ced8
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Nov 28, 2022
8b527d8
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Dec 1, 2022
ebfe25f
PR feedback
ymao1 Dec 1, 2022
546dd7e
Fixing async bundle size
ymao1 Dec 1, 2022
1ea9b0b
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Dec 5, 2022
6b9bf85
Merge branch 'main' into alerting/es-query-grouping
kibanamachine Dec 6, 2022
e2a54c9
Merge branch 'main' into alerting/es-query-grouping
kibanamachine Dec 8, 2022
c4f453d
Merge branch 'main' into alerting/es-query-grouping
kibanamachine Dec 12, 2022
f8aadf1
Fixing font size
ymao1 Dec 12, 2022
c2f7e3c
Fixing merge conflict
ymao1 Dec 12, 2022
ee9f5d2
Resetting optional fields when index changes
ymao1 Dec 12, 2022
4f10144
Merge branch 'main' into alerting/es-query-grouping
kibanamachine Dec 14, 2022
429d36c
Merging in main
ymao1 Dec 14, 2022
a340635
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Dec 14, 2022
a6e35fe
Merge branch 'main' of https://github.com/elastic/kibana into alertin…
ymao1 Dec 15, 2022
6b816b1
Apply suggestions from code review
ymao1 Dec 15, 2022
ab851c8
Merge branch 'alerting/es-query-grouping' of https://github.com/ymao1…
ymao1 Dec 15, 2022
6893e1f
PR feedback
ymao1 Dec 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 18 additions & 10 deletions docs/user/alerting/rule-types/es-query.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,41 @@ threshold condition is met.
Fill in the <<defining-rules-general-details, rule details>>, then select
*{es} query*.

[float]
=== Select the query type

{es} query rule can be defined using KQL/Lucene or Query DSL.
ymao1 marked this conversation as resolved.
Show resolved Hide resolved

[role="screenshot"]
image::user/alerting/images/rule-types-es-query-query-type.png[Select KQL or DSL query type]


[float]
=== Define the conditions

Define properties to detect the condition.

[role="screenshot"]
image::user/alerting/images/rule-types-es-query-conditions.png[Six clauses define the condition to detect]
image::user/alerting/images/rule-types-es-query-conditions.png[Eight clauses define the condition to detect]

Index:: Specifies an *index or data view* and a *time field* that is used for
the *time window*.
Size:: Specifies the number of documents to pass to the configured actions when
the threshold condition is met.
{es} query:: Specifies the ES DSL query. The number of documents that
match this query is evaluated against the threshold condition. Only the `query`, `fields`, `_source` and `runtime_mappings`
fields are used, other DSL fields are not considered.
{es} query:: Specifies the ES DSL query. Only the `query`, `fields`, `_source` and `runtime_mappings` fields are used, other DSL fields are not considered.
When:: Specifies how the value to be compared to the threshold is calculated. The value is calculated by aggregating a numeric field a the *time window*. The aggregation options are: `count`, `average`, `sum`, `min`, and `max`. When using `count` the document count is used, and an aggregation field is not necessary.
ymao1 marked this conversation as resolved.
Show resolved Hide resolved
Over/Grouped Over:: Specifies whether the aggregation is applied over all documents, or should be split into groups using a grouping field. If grouping is used, an <<alerting-concepts-alerts, alert>> will be created for each group when it meets the condition. To limit the number of alerts on high cardinality fields, you must specify the number of groups to check against the threshold. Only the *top* groups are checked.
ymao1 marked this conversation as resolved.
Show resolved Hide resolved
Threshold:: Defines a threshold value and a comparison operator (`is above`,
`is above or equals`, `is below`, `is below or equals`, or `is between`). The
number of documents that match the specified query is compared to this
threshold.
`is above or equals`, `is below`, `is below or equals`, or `is between`). The value
calculated by the aggregation is compared to this threshold.
Time window:: Defines how far back to search for documents, using the
*time field* set in the *index* clause. Generally this value should be set to a
value higher than the *check every* value in the
<<defining-rules-general-details, general rule details>>, to avoid gaps in
detection.
Size:: Specifies the number of documents to pass to the configured actions when
the threshold condition is met.
Exclude matches from previous run:: Turn on to avoid alert duplication by
excluding documents that have already been detected by the previous rule run.
excluding documents that have already been detected by the previous rule run. This
option is not available when a grouping field is specified.

[float]
=== Add action variables
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
Object {
"action": "7858e6d5a9f231bf23f6f2e57328eb0095b26735",
"action_task_params": "bbd38cbfd74bf6713586fe078e3fa92db2234299",
"alert": "eefada4a02ce05962387c0679d7b292771a931c4",
"alert": "90ae0b15c8666e4266a5340f9bc0d017fa86dee7",
"api_key_pending_invalidation": "9b4bc1235337da9a87ef05a1d1f4858b2a3b77c6",
"apm-indices": "ceb0870f3a74e2ffc3a1cd3a3c73af76baca0999",
"apm-server-schema": "2bfd2998d3873872e1366458ce553def85418f91",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server';
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
import { createEsoMigration, isEsQueryRuleType, pipeMigrations } from '../utils';
import { RawRule } from '../../../types';

function addGroupByToEsQueryRule(
doc: SavedObjectUnsanitizedDoc<RawRule>
): SavedObjectUnsanitizedDoc<RawRule> {
// Adding another check in for isEsQueryRuleType in case we add more migrations
if (isEsQueryRuleType(doc)) {
return {
...doc,
attributes: {
...doc.attributes,
params: {
...doc.attributes.params,
aggType: 'count',
Copy link
Member

Choose a reason for hiding this comment

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

Did we consider defaulting these fields, if not set in the params, to the values being set here? I can see in various places in the code where we are apply defaults for these, at least in the UX. I guess when referenced by the executorIt avoids migration, but ... kinda messy/sloppy?

It's certainly nice to have it very explicit, and don't have an issue with this migration - but am kinda wondering now with the "migration changes for rolling upgrades", if this pushes us in one direction or the other ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did consider it but in the past we've always taken the approach of "explicit is better" so I went for the migration instead. Definitely something we'll have to consider harder with rolling upgrades though.

groupBy: 'all',
},
},
};
}

return doc;
}

export const getMigrations870 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) =>
createEsoMigration(
encryptedSavedObjects,
(doc): doc is SavedObjectUnsanitizedDoc<RawRule> => isEsQueryRuleType(doc),
pipeMigrations(addGroupByToEsQueryRule)
);
Original file line number Diff line number Diff line change
Expand Up @@ -2508,6 +2508,41 @@ describe('successful migrations', () => {
});
});

describe('8.7.0', () => {
test('migrates es_query rule params and adds group by fields', () => {
const migration870 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.7.0'];
const rule = getMockData(
{
params: { esQuery: '{ "query": "test-query" }', searchType: 'esQuery' },
alertTypeId: '.es-query',
},
true
);
const migratedAlert870 = migration870(rule, migrationContext);

expect(migratedAlert870.attributes.params).toEqual({
esQuery: '{ "query": "test-query" }',
searchType: 'esQuery',
aggType: 'count',
groupBy: 'all',
});
});

test('does not migrate rule params if rule is not es query', () => {
const migration870 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.7.0'];
const rule = getMockData(
{
params: { foo: true },
alertTypeId: '.not-es-query',
},
true
);
const migratedAlert870 = migration870(rule, migrationContext);

expect(migratedAlert870.attributes.params).toEqual({ foo: true });
});
});

describe('Metrics Inventory Threshold rule', () => {
test('Migrates incorrect action group spelling', () => {
const migration800 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.0.0'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { getMigrations830 } from './8.3';
import { getMigrations841 } from './8.4';
import { getMigrations850 } from './8.5';
import { getMigrations860 } from './8.6';
import { getMigrations870 } from './8.7';
import { AlertLogMeta, AlertMigration } from './types';
import { MINIMUM_SS_MIGRATION_VERSION } from './constants';
import { createEsoMigration, isEsQueryRuleType, pipeMigrations } from './utils';
Expand Down Expand Up @@ -77,6 +78,7 @@ export function getMigrations(
'8.4.1': executeMigrationWithErrorHandling(getMigrations841(encryptedSavedObjects), '8.4.1'),
'8.5.0': executeMigrationWithErrorHandling(getMigrations850(encryptedSavedObjects), '8.5.0'),
'8.6.0': executeMigrationWithErrorHandling(getMigrations860(encryptedSavedObjects), '8.6.0'),
'8.7.0': executeMigrationWithErrorHandling(getMigrations870(encryptedSavedObjects), '8.7.0'),
},
getSearchSourceMigrations(encryptedSavedObjects, searchSourceMigrations)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -108,9 +105,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -160,9 +154,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -213,9 +204,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -271,9 +259,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -329,9 +314,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down Expand Up @@ -380,9 +362,6 @@ describe('buildSortedEventsQuery', () => {
],
},
},
{
match_all: {},
},
],
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,7 @@ export const buildSortedEventsQuery = ({
docvalue_fields: docFields,
query: {
bool: {
filter: [
...filterWithTime,
{
match_all: {},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure why this match_all was in here, so removed.

},
],
filter: [...filterWithTime],
},
},
...(aggs ? { aggs } : {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { getComparatorScript } from './comparator';
import { Comparator } from '../../../common/comparator_types';
import { Comparator } from './comparator_types';

describe('getComparatorScript', () => {
it('correctly returns script when comparator is LT', () => {
Expand Down
81 changes: 81 additions & 0 deletions x-pack/plugins/stack_alerts/common/comparator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { Comparator } from './comparator_types';

export type ComparatorFn = (value: number, threshold: number[]) => boolean;

const humanReadableComparators = new Map<Comparator, string>([
[Comparator.LT, 'less than'],
[Comparator.LT_OR_EQ, 'less than or equal to'],
[Comparator.GT_OR_EQ, 'greater than or equal to'],
[Comparator.GT, 'greater than'],
[Comparator.BETWEEN, 'between'],
[Comparator.NOT_BETWEEN, 'not between'],
]);

export const ComparatorFns = new Map<Comparator, ComparatorFn>([
[Comparator.LT, (value: number, threshold: number[]) => value < threshold[0]],
[Comparator.LT_OR_EQ, (value: number, threshold: number[]) => value <= threshold[0]],
[Comparator.GT_OR_EQ, (value: number, threshold: number[]) => value >= threshold[0]],
[Comparator.GT, (value: number, threshold: number[]) => value > threshold[0]],
[
Comparator.BETWEEN,
(value: number, threshold: number[]) => value >= threshold[0] && value <= threshold[1],
],
[
Comparator.NOT_BETWEEN,
(value: number, threshold: number[]) => value < threshold[0] || value > threshold[1],
],
]);

export const getComparatorScript = (
comparator: Comparator,
threshold: number[],
fieldName: string
) => {
if (threshold.length === 0) {
throw new Error('Threshold value required');
}

function getThresholdString(thresh: number) {
return Number.isInteger(thresh) ? `${thresh}L` : `${thresh}`;
}

switch (comparator) {
case Comparator.LT:
return `${fieldName} < ${getThresholdString(threshold[0])}`;
case Comparator.LT_OR_EQ:
return `${fieldName} <= ${getThresholdString(threshold[0])}`;
case Comparator.GT:
return `${fieldName} > ${getThresholdString(threshold[0])}`;
case Comparator.GT_OR_EQ:
return `${fieldName} >= ${getThresholdString(threshold[0])}`;
case Comparator.BETWEEN:
if (threshold.length < 2) {
throw new Error('Threshold values required');
}
return `${fieldName} >= ${getThresholdString(
threshold[0]
)} && ${fieldName} <= ${getThresholdString(threshold[1])}`;
case Comparator.NOT_BETWEEN:
if (threshold.length < 2) {
throw new Error('Threshold values required');
}
return `${fieldName} < ${getThresholdString(
threshold[0]
)} || ${fieldName} > ${getThresholdString(threshold[1])}`;
}
};

export const ComparatorFnNames = new Set(ComparatorFns.keys());

export function getHumanReadableComparator(comparator: Comparator) {
return humanReadableComparators.has(comparator)
? humanReadableComparators.get(comparator)
: comparator;
}
6 changes: 6 additions & 0 deletions x-pack/plugins/stack_alerts/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@
* 2.0.
*/

export {
ComparatorFns,
getComparatorScript,
ComparatorFnNames,
getHumanReadableComparator,
} from './comparator';
export { STACK_ALERTS_FEATURE_ID } from './constants';
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const DEFAULT_VALUES = {
TIME_WINDOW_SIZE: 5,
TIME_WINDOW_UNIT: 'm',
THRESHOLD: [1000],
AGGREGATION_TYPE: 'count',
TERM_SIZE: 5,
GROUP_BY: 'all',
EXCLUDE_PREVIOUS_HITS: true,
};

Expand All @@ -33,6 +36,11 @@ export const EXPRESSION_ERRORS = {
timeWindowSize: new Array<string>(),
searchConfiguration: new Array<string>(),
searchType: new Array<string>(),
aggField: new Array<string>(),
aggType: new Array<string>(),
groupBy: new Array<string>(),
termSize: new Array<string>(),
termField: new Array<string>(),
};

export const EXPRESSION_ERROR_KEYS = Object.keys(EXPRESSION_ERRORS) as ErrorKey[];
Loading