Removing unnecessary hit count check from es query alert (#97735)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e01d5fd255
commit
c27245b201
|
@ -216,46 +216,47 @@ export function getAlertType(
|
|||
|
||||
const { body: searchResult } = await esClient.search(query);
|
||||
|
||||
if (searchResult.hits.hits.length > 0) {
|
||||
const numMatches = (searchResult.hits.total as estypes.TotalHits).value;
|
||||
logger.debug(`alert ${ES_QUERY_ID}:${alertId} "${name}" query has ${numMatches} matches`);
|
||||
logger.debug(
|
||||
`alert ${ES_QUERY_ID}:${alertId} "${name}" result - ${JSON.stringify(searchResult)}`
|
||||
);
|
||||
|
||||
// apply the alert condition
|
||||
const conditionMet = compareFn(numMatches, params.threshold);
|
||||
const numMatches = (searchResult.hits.total as estypes.TotalHits).value;
|
||||
|
||||
if (conditionMet) {
|
||||
const humanFn = i18n.translate(
|
||||
'xpack.stackAlerts.esQuery.alertTypeContextConditionsDescription',
|
||||
{
|
||||
defaultMessage: `Number of matching documents is {thresholdComparator} {threshold}`,
|
||||
values: {
|
||||
thresholdComparator: getHumanReadableComparator(params.thresholdComparator),
|
||||
threshold: params.threshold.join(' and '),
|
||||
},
|
||||
}
|
||||
);
|
||||
// apply the alert condition
|
||||
const conditionMet = compareFn(numMatches, params.threshold);
|
||||
|
||||
const baseContext: EsQueryAlertActionContext = {
|
||||
date: new Date().toISOString(),
|
||||
value: numMatches,
|
||||
conditions: humanFn,
|
||||
hits: searchResult.hits.hits,
|
||||
};
|
||||
|
||||
const actionContext = addMessages(options, baseContext, params);
|
||||
const alertInstance = options.services.alertInstanceFactory(ConditionMetAlertInstanceId);
|
||||
alertInstance
|
||||
// store the params we would need to recreate the query that led to this alert instance
|
||||
.replaceState({ latestTimestamp: timestamp, dateStart, dateEnd })
|
||||
.scheduleActions(ActionGroupId, actionContext);
|
||||
|
||||
// update the timestamp based on the current search results
|
||||
const firstValidTimefieldSort = getValidTimefieldSort(
|
||||
searchResult.hits.hits.find((hit) => getValidTimefieldSort(hit.sort))?.sort
|
||||
);
|
||||
if (firstValidTimefieldSort) {
|
||||
timestamp = firstValidTimefieldSort;
|
||||
if (conditionMet) {
|
||||
const humanFn = i18n.translate(
|
||||
'xpack.stackAlerts.esQuery.alertTypeContextConditionsDescription',
|
||||
{
|
||||
defaultMessage: `Number of matching documents is {thresholdComparator} {threshold}`,
|
||||
values: {
|
||||
thresholdComparator: getHumanReadableComparator(params.thresholdComparator),
|
||||
threshold: params.threshold.join(' and '),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const baseContext: EsQueryAlertActionContext = {
|
||||
date: new Date().toISOString(),
|
||||
value: numMatches,
|
||||
conditions: humanFn,
|
||||
hits: searchResult.hits.hits,
|
||||
};
|
||||
|
||||
const actionContext = addMessages(options, baseContext, params);
|
||||
const alertInstance = options.services.alertInstanceFactory(ConditionMetAlertInstanceId);
|
||||
alertInstance
|
||||
// store the params we would need to recreate the query that led to this alert instance
|
||||
.replaceState({ latestTimestamp: timestamp, dateStart, dateEnd })
|
||||
.scheduleActions(ActionGroupId, actionContext);
|
||||
|
||||
// update the timestamp based on the current search results
|
||||
const firstValidTimefieldSort = getValidTimefieldSort(
|
||||
searchResult.hits.hits.find((hit) => getValidTimefieldSort(hit.sort))?.sort
|
||||
);
|
||||
if (firstValidTimefieldSort) {
|
||||
timestamp = firstValidTimefieldSort;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,6 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
// write documents in the future, figure out the end date
|
||||
const endDateMillis = Date.now() + (ALERT_INTERVALS_TO_WRITE - 1) * ALERT_INTERVAL_MILLIS;
|
||||
endDate = new Date(endDateMillis).toISOString();
|
||||
|
||||
// write documents from now to the future end date in groups
|
||||
createEsDocumentsInGroups(ES_GROUPS_TO_WRITE);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -65,6 +62,9 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('runs correctly: threshold on hit count < >', async () => {
|
||||
// write documents from now to the future end date in groups
|
||||
createEsDocumentsInGroups(ES_GROUPS_TO_WRITE);
|
||||
|
||||
await createAlert({
|
||||
name: 'never fire',
|
||||
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
|
||||
|
@ -104,6 +104,9 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('runs correctly with query: threshold on hit count < >', async () => {
|
||||
// write documents from now to the future end date in groups
|
||||
createEsDocumentsInGroups(ES_GROUPS_TO_WRITE);
|
||||
|
||||
const rangeQuery = (rangeThreshold: number) => {
|
||||
return {
|
||||
query: {
|
||||
|
@ -126,8 +129,8 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
name: 'never fire',
|
||||
esQuery: JSON.stringify(rangeQuery(ES_GROUPS_TO_WRITE * ALERT_INTERVALS_TO_WRITE + 1)),
|
||||
size: 100,
|
||||
thresholdComparator: '>=',
|
||||
threshold: [0],
|
||||
thresholdComparator: '<',
|
||||
threshold: [-1],
|
||||
});
|
||||
|
||||
await createAlert({
|
||||
|
@ -154,6 +157,37 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
}
|
||||
});
|
||||
|
||||
it('runs correctly: no matches', async () => {
|
||||
await createAlert({
|
||||
name: 'always fire',
|
||||
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
|
||||
size: 100,
|
||||
thresholdComparator: '<',
|
||||
threshold: [1],
|
||||
});
|
||||
|
||||
const docs = await waitForDocs(1);
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
const doc = docs[i];
|
||||
const { previousTimestamp, hits } = doc._source;
|
||||
const { name, title, message } = doc._source.params;
|
||||
|
||||
expect(name).to.be('always fire');
|
||||
expect(title).to.be(`alert 'always fire' matched query`);
|
||||
const messagePattern = /alert 'always fire' is active:\n\n- Value: 0+\n- Conditions Met: Number of matching documents is less than 1 over 15s\n- Timestamp: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;
|
||||
expect(message).to.match(messagePattern);
|
||||
expect(hits).to.be.empty();
|
||||
|
||||
// during the first execution, the latestTimestamp value should be empty
|
||||
// since this alert always fires, the latestTimestamp value should be updated each execution
|
||||
if (!i) {
|
||||
expect(previousTimestamp).to.be.empty();
|
||||
} else {
|
||||
expect(previousTimestamp).not.to.be.empty();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function createEsDocumentsInGroups(groups: number) {
|
||||
await createEsDocuments(
|
||||
es,
|
||||
|
|
Loading…
Reference in a new issue