mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Aggregate anomalies table data using configured Kibana timezone (#26192)
* [ML] Aggregate anomalies table data using configured Kibana timezone * [ML] Move dataFormatTz prop out of controller scope
This commit is contained in:
parent
51a418ea43
commit
d97609eaeb
6 changed files with 30 additions and 8 deletions
|
@ -15,7 +15,7 @@
|
|||
import _ from 'lodash';
|
||||
import $ from 'jquery';
|
||||
import DragSelect from 'dragselect';
|
||||
import moment from 'moment';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import 'plugins/ml/components/anomalies_table';
|
||||
import 'plugins/ml/components/controls';
|
||||
|
@ -76,6 +76,7 @@ module.controller('MlExplorerController', function (
|
|||
$timeout,
|
||||
AppState,
|
||||
Private,
|
||||
config,
|
||||
mlCheckboxShowChartsService,
|
||||
mlSelectLimitService,
|
||||
mlSelectIntervalService,
|
||||
|
@ -87,6 +88,10 @@ module.controller('MlExplorerController', function (
|
|||
timefilter.enableTimeRangeSelector();
|
||||
timefilter.enableAutoRefreshSelector();
|
||||
|
||||
// Pass the timezone to the server for use when aggregating anomalies (by day / hour) for the table.
|
||||
const tzConfig = config.get('dateFormat:tz');
|
||||
const dateFormatTz = (tzConfig !== 'Browser') ? tzConfig : moment.tz.guess();
|
||||
|
||||
const TimeBuckets = Private(IntervalHelperProvider);
|
||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||
const mlJobSelectService = Private(JobSelectServiceProvider);
|
||||
|
@ -946,6 +951,7 @@ module.controller('MlExplorerController', function (
|
|||
mlSelectSeverityService.state.get('threshold').val,
|
||||
timeRange.earliestMs,
|
||||
timeRange.latestMs,
|
||||
dateFormatTz,
|
||||
500,
|
||||
MAX_CATEGORY_EXAMPLES
|
||||
).then((resp) => {
|
||||
|
|
|
@ -21,6 +21,7 @@ export const results = {
|
|||
threshold,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
maxRecords,
|
||||
maxExamples) {
|
||||
|
||||
|
@ -35,6 +36,7 @@ export const results = {
|
|||
threshold,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
maxRecords,
|
||||
maxExamples
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import 'plugins/ml/components/anomalies_table';
|
||||
import 'plugins/ml/components/controls';
|
||||
|
@ -71,6 +71,7 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
$timeout,
|
||||
Private,
|
||||
AppState,
|
||||
config,
|
||||
mlSelectIntervalService,
|
||||
mlSelectSeverityService) {
|
||||
|
||||
|
@ -98,6 +99,10 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
$scope.showForecast = true; // Toggles display of forecast data in the focus chart
|
||||
$scope.showForecastCheckbox = false;
|
||||
|
||||
// Pass the timezone to the server for use when aggregating anomalies (by day / hour) for the table.
|
||||
const tzConfig = config.get('dateFormat:tz');
|
||||
const dateFormatTz = (tzConfig !== 'Browser') ? tzConfig : moment.tz.guess();
|
||||
|
||||
$scope.permissions = {
|
||||
canForecastJob: checkPermission('canForecastJob')
|
||||
};
|
||||
|
@ -682,6 +687,7 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
}
|
||||
|
||||
function loadAnomaliesTableData(earliestMs, latestMs) {
|
||||
|
||||
ml.results.getAnomaliesTableData(
|
||||
[$scope.selectedJob.job_id],
|
||||
$scope.criteriaFields,
|
||||
|
@ -690,6 +696,7 @@ module.controller('MlTimeSeriesExplorerController', function (
|
|||
mlSelectSeverityService.state.get('threshold').val,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
ANOMALIES_MAX_RESULTS
|
||||
).then((resp) => {
|
||||
const anomalies = resp.anomalies;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import {
|
||||
getEntityFieldName,
|
||||
|
@ -17,13 +17,15 @@ import {
|
|||
|
||||
|
||||
// Builds the items for display in the anomalies table from the supplied list of anomaly records.
|
||||
export function buildAnomalyTableItems(anomalyRecords, aggregationInterval) {
|
||||
// Provide the timezone to use for aggregating anomalies (by day or hour) as set in the
|
||||
// Kibana dateFormat:tz setting.
|
||||
export function buildAnomalyTableItems(anomalyRecords, aggregationInterval, dateFormatTz) {
|
||||
|
||||
// Aggregate the anomaly records if necessary, and create skeleton display records with
|
||||
// time, detector (description) and source record properties set.
|
||||
let displayRecords = [];
|
||||
if (aggregationInterval !== 'second') {
|
||||
displayRecords = aggregateAnomalies(anomalyRecords, aggregationInterval);
|
||||
displayRecords = aggregateAnomalies(anomalyRecords, aggregationInterval, dateFormatTz);
|
||||
} else {
|
||||
// Show all anomaly records.
|
||||
displayRecords = anomalyRecords.map((record) => {
|
||||
|
@ -115,7 +117,7 @@ export function buildAnomalyTableItems(anomalyRecords, aggregationInterval) {
|
|||
});
|
||||
}
|
||||
|
||||
function aggregateAnomalies(anomalyRecords, interval) {
|
||||
function aggregateAnomalies(anomalyRecords, interval, dateFormatTz) {
|
||||
// Aggregate the anomaly records by time, jobId, detectorIndex, and entity (by/over/partition).
|
||||
// anomalyRecords assumed to be supplied in ascending time order.
|
||||
if (anomalyRecords.length === 0) {
|
||||
|
@ -125,7 +127,9 @@ function aggregateAnomalies(anomalyRecords, interval) {
|
|||
const aggregatedData = {};
|
||||
anomalyRecords.forEach((record) => {
|
||||
// Use moment.js to get start of interval.
|
||||
const roundedTime = moment(record.timestamp).startOf(interval).valueOf();
|
||||
const roundedTime = (dateFormatTz !== undefined) ?
|
||||
moment(record.timestamp).tz(dateFormatTz).startOf(interval).valueOf() :
|
||||
moment(record.timestamp).startOf(interval).valueOf();
|
||||
if (aggregatedData[roundedTime] === undefined) {
|
||||
aggregatedData[roundedTime] = {};
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export function resultsServiceProvider(callWithRequest) {
|
|||
threshold,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
maxRecords = DEFAULT_QUERY_SIZE,
|
||||
maxExamples = DEFAULT_MAX_EXAMPLES) {
|
||||
|
||||
|
@ -163,7 +164,7 @@ export function resultsServiceProvider(callWithRequest) {
|
|||
tableData.interval = (daysDiff < 2 ? 'hour' : 'day');
|
||||
}
|
||||
|
||||
tableData.anomalies = buildAnomalyTableItems(records, tableData.interval);
|
||||
tableData.anomalies = buildAnomalyTableItems(records, tableData.interval, dateFormatTz);
|
||||
|
||||
// Load examples for any categorization anomalies.
|
||||
const categoryAnomalies = tableData.anomalies.filter(item => item.entityName === 'mlcategory');
|
||||
|
|
|
@ -21,6 +21,7 @@ function getAnomaliesTableData(callWithRequest, payload) {
|
|||
threshold,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
maxRecords,
|
||||
maxExamples } = payload;
|
||||
return rs.getAnomaliesTableData(
|
||||
|
@ -31,6 +32,7 @@ function getAnomaliesTableData(callWithRequest, payload) {
|
|||
threshold,
|
||||
earliestMs,
|
||||
latestMs,
|
||||
dateFormatTz,
|
||||
maxRecords,
|
||||
maxExamples);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue