Skip to content

Commit

Permalink
[ML] Explain Log Rate Spikes: Fix mini histograms for groups with mul…
Browse files Browse the repository at this point in the history
…tiples values per field. (#147597)

For groups that have multiple values for the same field, the group
histogram query wasn't able to fetch data because it filters with a bool
`must` and individual `term` aggregations. This PR fixes it by using a
`terms` aggregation if there are multiple values for a field so just
these get treated as `OR`.
  • Loading branch information
walterra committed Dec 20, 2022
1 parent 60867aa commit 3a48b7a
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 6 deletions.
8 changes: 2 additions & 6 deletions x-pack/plugins/aiops/server/routes/explain_log_rate_spikes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import {
getSimpleHierarchicalTreeLeaves,
markDuplicates,
} from './queries/get_simple_hierarchical_tree';
import { getGroupFilter } from './queries/get_group_filter';

// 10s ping frequency to keep the stream alive.
const PING_FREQUENCY = 10000;
Expand Down Expand Up @@ -639,12 +640,7 @@ export const defineExplainLogRateSpikesRoute = (
}

if (overallTimeSeries !== undefined) {
const histogramQuery = getHistogramQuery(
request.body,
cpg.group.map((d) => ({
term: { [d.fieldName]: d.fieldValue },
}))
);
const histogramQuery = getHistogramQuery(request.body, getGroupFilter(cpg));

let cpgTimeSeries: NumericChartData;
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 { getGroupFilter } from './get_group_filter';

const changePointGroups = [
{
id: '2038579476',
group: [
{ fieldName: 'response_code', fieldValue: '500', duplicate: false },
{ fieldName: 'url', fieldValue: 'home.php', duplicate: false },
{ fieldName: 'url', fieldValue: 'login.php', duplicate: false },
],
docCount: 792,
pValue: 0.010770456205312423,
},
{
id: '817080373',
group: [{ fieldName: 'user', fieldValue: 'Peter', duplicate: false }],
docCount: 1981,
pValue: 2.7454255728359757e-21,
},
];

describe('getGroupFilter', () => {
it('gets a query filter for the change points of a group with multiple values per field', () => {
expect(getGroupFilter(changePointGroups[0])).toStrictEqual([
{
term: {
response_code: '500',
},
},
{
terms: {
url: ['home.php', 'login.php'],
},
},
]);
});

it('gets a query filter for the change points of a group with just a single field/value', () => {
expect(getGroupFilter(changePointGroups[1])).toStrictEqual([
{
term: {
user: 'Peter',
},
},
]);
});
});
33 changes: 33 additions & 0 deletions x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

import type { ChangePointGroup } from '@kbn/ml-agg-utils';

// Transforms a list of change point items from a group in a query filter.
// Uses a `term` filter for single field value combinations.
// For fields with multiple values it creates a single `terms` filter that includes
// all values. This avoids queries not returning any results otherwise because
// separate `term` filter for multiple values for the same field would rule each other out.
export function getGroupFilter(
changePointGroup: ChangePointGroup
): estypes.QueryDslQueryContainer[] {
return Object.entries(
changePointGroup.group.reduce<Record<string, Array<string | number>>>((p, c) => {
if (p[c.fieldName]) {
p[c.fieldName].push(c.fieldValue);
} else {
p[c.fieldName] = [c.fieldValue];
}
return p;
}, {})
).reduce<estypes.QueryDslQueryContainer[]>((p, [key, values]) => {
p.push(values.length > 1 ? { terms: { [key]: values } } : { term: { [key]: values[0] } });
return p;
}, []);
}

0 comments on commit 3a48b7a

Please sign in to comment.