diff --git a/common/types/explorer.ts b/common/types/explorer.ts
index 6dbab6daeb..ceb95bb9e2 100644
--- a/common/types/explorer.ts
+++ b/common/types/explorer.ts
@@ -381,3 +381,23 @@ export interface VisSpecificMetaData {
x_coordinate: string;
y_coordinate: string;
}
+
+export type MOMENT_UNIT_OF_TIME =
+ | 'years'
+ | 'y'
+ | 'quarters'
+ | 'Q'
+ | 'months'
+ | 'M'
+ | 'weeks'
+ | 'w'
+ | 'days'
+ | 'd'
+ | 'hours'
+ | 'h'
+ | 'minutes'
+ | 'm'
+ | 'seconds'
+ | 's'
+ | 'milliseconds'
+ | 'ms';
diff --git a/public/components/event_analytics/explorer/explorer.tsx b/public/components/event_analytics/explorer/explorer.tsx
index f24508a6b8..52c9abaa9f 100644
--- a/public/components/event_analytics/explorer/explorer.tsx
+++ b/public/components/event_analytics/explorer/explorer.tsx
@@ -530,7 +530,12 @@ export const Explorer = ({
startTime={appLogEvents ? startTime : dateRange[0]}
endTime={appLogEvents ? endTime : dateRange[1]}
/>
-
+
-
-
-
-
-
-
+/>
`;
exports[`Count distribution component Renders empty count distribution component 1`] = ``;
diff --git a/public/components/event_analytics/explorer/visualizations/count_distribution/count_distribution.tsx b/public/components/event_analytics/explorer/visualizations/count_distribution/count_distribution.tsx
index e7ea547dfb..8e5f10f315 100644
--- a/public/components/event_analytics/explorer/visualizations/count_distribution/count_distribution.tsx
+++ b/public/components/event_analytics/explorer/visualizations/count_distribution/count_distribution.tsx
@@ -6,13 +6,20 @@
import React from 'react';
import { BarOrientation, LONG_CHART_COLOR } from '../../../../../../common/constants/shared';
import { Plt } from '../../../../visualizations/plotly/plot';
+import { fillTimeDataWithEmpty } from '../../../utils/utils';
-export const CountDistribution = ({ countDistribution }: any) => {
+export const CountDistribution = ({
+ countDistribution,
+ selectedInterval,
+ startTime,
+ endTime,
+}: any) => {
if (
!countDistribution ||
!countDistribution.data ||
!countDistribution.metadata ||
- !countDistribution.metadata.fields
+ !countDistribution.metadata.fields ||
+ !selectedInterval
)
return null;
@@ -31,9 +38,31 @@ export const CountDistribution = ({ countDistribution }: any) => {
},
];
+ // fill the final data with the exact right amount of empty buckets
+ function fillWithEmpty(processedData: any) {
+ // original x and y fields
+ const xVals = processedData[0].x;
+ const yVals = processedData[0].y;
+
+ const { buckets, values } = fillTimeDataWithEmpty(
+ xVals,
+ yVals,
+ selectedInterval.replace(/^auto_/, ''),
+ startTime,
+ endTime
+ );
+
+ // replace old x and y values with new
+ processedData[0].x = buckets;
+ processedData[0].y = values;
+
+ // // at the end, return the new object
+ return processedData;
+ }
+
return (
{
@@ -80,4 +81,69 @@ describe('Utils event analytics helper functions', () => {
).toBeTruthy();
expect(getHeaders([], ['', 'Time', '_source'], undefined)).toBeTruthy();
});
+
+ it('validates fillTimeDataWithEmpty function', () => {
+ expect(
+ fillTimeDataWithEmpty(
+ ['2023-07-01 00:00:00', '2023-08-01 00:00:00', '2023-09-01 00:00:00'],
+ [54, 802, 292],
+ 'M',
+ '2023-01-01T08:00:00.000Z',
+ '2023-09-12T21:36:31.389Z'
+ )
+ ).toEqual({
+ buckets: [
+ '2023-01-01 00:00:00',
+ '2023-02-01 00:00:00',
+ '2023-03-01 00:00:00',
+ '2023-04-01 00:00:00',
+ '2023-05-01 00:00:00',
+ '2023-06-01 00:00:00',
+ '2023-07-01 00:00:00',
+ '2023-08-01 00:00:00',
+ '2023-09-01 00:00:00',
+ ],
+ values: [0, 0, 0, 0, 0, 0, 54, 802, 292],
+ });
+ expect(
+ fillTimeDataWithEmpty(
+ [
+ '2023-09-11 07:00:00',
+ '2023-09-11 09:00:00',
+ '2023-09-11 10:00:00',
+ '2023-09-11 11:00:00',
+ '2023-09-11 12:00:00',
+ '2023-09-11 13:00:00',
+ '2023-09-11 14:00:00',
+ '2023-09-11 15:00:00',
+ ],
+ [1, 1, 5, 4, 2, 3, 3, 1],
+ 'h',
+ '2023-09-11T00:00:00.000',
+ '2023-09-11T17:00:00.000'
+ )
+ ).toEqual({
+ buckets: [
+ '2023-09-11 00:00:00',
+ '2023-09-11 01:00:00',
+ '2023-09-11 02:00:00',
+ '2023-09-11 03:00:00',
+ '2023-09-11 04:00:00',
+ '2023-09-11 05:00:00',
+ '2023-09-11 06:00:00',
+ '2023-09-11 07:00:00',
+ '2023-09-11 08:00:00',
+ '2023-09-11 09:00:00',
+ '2023-09-11 10:00:00',
+ '2023-09-11 11:00:00',
+ '2023-09-11 12:00:00',
+ '2023-09-11 13:00:00',
+ '2023-09-11 14:00:00',
+ '2023-09-11 15:00:00',
+ '2023-09-11 16:00:00',
+ '2023-09-11 17:00:00',
+ ],
+ values: [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 5, 4, 2, 3, 3, 1, 0, 0],
+ });
+ });
});
diff --git a/public/components/event_analytics/utils/utils.tsx b/public/components/event_analytics/utils/utils.tsx
index cc786f0d8d..fe7d47aa43 100644
--- a/public/components/event_analytics/utils/utils.tsx
+++ b/public/components/event_analytics/utils/utils.tsx
@@ -1,13 +1,15 @@
-/* eslint-disable no-bitwise */
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
+/* eslint-disable no-bitwise */
+
import { uniqueId, isEmpty } from 'lodash';
import moment from 'moment';
import React from 'react';
import { EuiText } from '@elastic/eui';
+import datemath from '@elastic/datemath';
import { HttpStart } from '../../../../../../src/core/public';
import {
CUSTOM_LABEL,
@@ -15,6 +17,7 @@ import {
GROUPBY,
AGGREGATIONS,
BREAKDOWNS,
+ DATE_PICKER_FORMAT,
} from '../../../../common/constants/explorer';
import { PPL_DATE_FORMAT, PPL_INDEX_REGEX } from '../../../../common/constants/shared';
import {
@@ -23,6 +26,7 @@ import {
IExplorerFields,
IField,
IQuery,
+ MOMENT_UNIT_OF_TIME,
} from '../../../../common/types/explorer';
import PPLService from '../../../services/requests/ppl';
import { DocViewRow, IDocType } from '../explorer/events_views';
@@ -459,3 +463,55 @@ export const getContentTabTitle = (tabID: string, tabTitle: string) => {
>
);
};
+
+/**
+ * Used to fill in missing empty data where x is an array of time values and there are only x
+ * values when y is non-zero.
+ * @param xVals all x values being used
+ * @param yVals all y values being used
+ * @param intervalPeriod Moment unitOfTime used to dictate how long each interval is
+ * @param startTime starting time of x values
+ * @param endTime ending time of x values
+ * @returns an object with buckets and values where the buckets are all of the new x values and
+ * values are the corresponding values which include y values that are 0 for empty data
+ */
+export const fillTimeDataWithEmpty = (
+ xVals: string[],
+ yVals: number[],
+ intervalPeriod: MOMENT_UNIT_OF_TIME,
+ startTime: string,
+ endTime: string
+): { buckets: string[]; values: number[] } => {
+ // parses out datetime for start and end, then reformats
+ const startDate = datemath
+ .parse(startTime)
+ ?.startOf(intervalPeriod === 'w' ? 'isoWeek' : intervalPeriod);
+ const endDate = datemath
+ .parse(endTime)
+ ?.startOf(intervalPeriod === 'w' ? 'isoWeek' : intervalPeriod);
+
+ // find the number of buckets
+ // below essentially does ((end - start) / interval_period) + 1
+ const numBuckets = endDate.diff(startDate, intervalPeriod) + 1;
+
+ // populate buckets as x values in the graph
+ const buckets = [startDate.format(DATE_PICKER_FORMAT)];
+ const currentDate = startDate;
+ for (let i = 1; i < numBuckets; i++) {
+ const nextBucket = currentDate.add(1, intervalPeriod);
+ buckets.push(nextBucket.format(DATE_PICKER_FORMAT));
+ }
+
+ // create y values, use old y values if they exist
+ const values: number[] = [];
+ buckets.forEach((bucket) => {
+ const bucketIndex = xVals.findIndex((x: string) => x === bucket);
+ if (bucketIndex !== -1) {
+ values.push(yVals[bucketIndex]);
+ } else {
+ values.push(0);
+ }
+ });
+
+ return { buckets, values };
+};