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

[Lens] Time shift metrics #98781

Merged
merged 53 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
db10c1b
make basic time offset work
flash1293 Apr 28, 2021
e79d607
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 Apr 29, 2021
4c4ad72
stabilize response merging and start building UI
flash1293 Apr 29, 2021
4d8c9b8
continue building time shift
flash1293 Apr 30, 2021
fa12d2e
add better error handling
flash1293 Apr 30, 2021
64e208a
add better error handling
flash1293 May 3, 2021
5b389ce
fix broken metric time shift
flash1293 May 3, 2021
20940a7
experimental fix action
flash1293 May 5, 2021
f0c65ec
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 5, 2021
5603bb9
cleanup
flash1293 May 5, 2021
8f6b95c
remove current runtime error thingy
flash1293 May 5, 2021
0d2621e
remove leftover runtime error stuff
flash1293 May 5, 2021
b318efd
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 6, 2021
3439d35
fix some casest push
flash1293 May 6, 2021
557d6d5
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 11, 2021
abeed2c
add tests and fix some bugs
flash1293 May 11, 2021
89dd546
add back session snapshots
flash1293 May 11, 2021
bcc0cd1
fix some bugs
flash1293 May 12, 2021
e6f112d
fix terms sorting
flash1293 May 12, 2021
e866d6d
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 17, 2021
34a2208
fix bug and add tests
flash1293 May 17, 2021
f91d72f
address comments
flash1293 May 17, 2021
50844cd
fix test
flash1293 May 17, 2021
fc5f171
fix types and docs
flash1293 May 17, 2021
2bc3872
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 18, 2021
7c4d2f2
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 18, 2021
16b5ee2
increase accepted span for percentiles
flash1293 May 18, 2021
1593bad
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 19, 2021
ef024fe
add warnings
flash1293 May 19, 2021
dc686f0
Merge branch 'master' into lens/splitted-time-offset
kibanamachine May 19, 2021
e2134e2
Merge branch 'master' into lens/splitted-time-offset
kibanamachine May 19, 2021
047d99f
improve warning
flash1293 May 20, 2021
d2ebab5
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 20, 2021
f0d3b44
Merge branch 'lens/splitted-time-offset' of github.com:flash1293/kiba…
flash1293 May 20, 2021
b4a988d
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 25, 2021
f6620ff
review comments
flash1293 May 25, 2021
eaa0819
adjust wording
flash1293 May 25, 2021
b066335
fix test
flash1293 May 25, 2021
e5287a3
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 25, 2021
81c1676
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 26, 2021
9c045a7
fix previous time shift error checks
flash1293 May 26, 2021
64fbd5a
Merge branch 'master' into lens/splitted-time-offset
kibanamachine May 27, 2021
47d0bae
Merge branch 'master' into lens/splitted-time-offset
kibanamachine May 27, 2021
026371f
add label suffix for cumulative sum
flash1293 May 27, 2021
6891e8b
review comments
flash1293 May 27, 2021
f4db9ec
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 27, 2021
8e9f724
fix functional test
flash1293 May 28, 2021
ab828a3
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 28, 2021
bc1633b
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 28, 2021
f0b137d
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 May 31, 2021
3849b04
review comments
flash1293 May 31, 2021
77139a1
Merge remote-tracking branch 'upstream/master' into lens/splitted-tim…
flash1293 Jun 2, 2021
e9b556f
fix tests
flash1293 Jun 2, 2021
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) &gt; [forceNow](./kibana-plugin-plugins-data-public.aggconfigs.forcenow.md)

## AggConfigs.forceNow property

<b>Signature:</b>

```typescript
forceNow?: Date;
```
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export declare class AggConfigs
| --- | --- | --- | --- |
| [aggs](./kibana-plugin-plugins-data-public.aggconfigs.aggs.md) | | <code>IAggConfig[]</code> | |
| [createAggConfig](./kibana-plugin-plugins-data-public.aggconfigs.createaggconfig.md) | | <code>&lt;T extends AggConfig = AggConfig&gt;(params: CreateAggConfigParams, { addToAggConfigs }?: {</code><br/><code> addToAggConfigs?: boolean &#124; undefined;</code><br/><code> }) =&gt; T</code> | |
| [forceNow](./kibana-plugin-plugins-data-public.aggconfigs.forcenow.md) | | <code>Date</code> | |
| [hierarchical](./kibana-plugin-plugins-data-public.aggconfigs.hierarchical.md) | | <code>boolean</code> | |
| [indexPattern](./kibana-plugin-plugins-data-public.aggconfigs.indexpattern.md) | | <code>IndexPattern</code> | |
| [timeFields](./kibana-plugin-plugins-data-public.aggconfigs.timefields.md) | | <code>string[]</code> | |
Expand Down Expand Up @@ -50,6 +51,7 @@ export declare class AggConfigs
| [jsonDataEquals(aggConfigs)](./kibana-plugin-plugins-data-public.aggconfigs.jsondataequals.md) | | Data-by-data comparison of this Aggregation Ignores the non-array indexes |
| [onSearchRequestStart(searchSource, options)](./kibana-plugin-plugins-data-public.aggconfigs.onsearchrequeststart.md) | | |
| [postFlightTransform(response)](./kibana-plugin-plugins-data-public.aggconfigs.postflighttransform.md) | | |
| [setForceNow(now)](./kibana-plugin-plugins-data-public.aggconfigs.setforcenow.md) | | |
| [setTimeFields(timeFields)](./kibana-plugin-plugins-data-public.aggconfigs.settimefields.md) | | |
| [setTimeRange(timeRange)](./kibana-plugin-plugins-data-public.aggconfigs.settimerange.md) | | |
| [toDsl()](./kibana-plugin-plugins-data-public.aggconfigs.todsl.md) | | |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) &gt; [setForceNow](./kibana-plugin-plugins-data-public.aggconfigs.setforcenow.md)

## AggConfigs.setForceNow() method

<b>Signature:</b>

```typescript
setForceNow(now: Date | undefined): void;
```

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| now | <code>Date &#124; undefined</code> | |

<b>Returns:</b>

`void`

8 changes: 8 additions & 0 deletions src/plugins/data/common/search/aggs/agg_configs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,14 @@ describe('AggConfigs', () => {
params: { field: 'bytes', timeShift: '1d' },
},
];
indexPattern.fields.push({
name: 'timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
filterable: true,
searchable: true,
} as IndexPatternField);

const ac = new AggConfigs(indexPattern, configStates, { typesRegistry });
ac.timeFields = ['timestamp'];
Expand Down
191 changes: 10 additions & 181 deletions src/plugins/data/common/search/aggs/agg_configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,10 @@
*/

import moment from 'moment';
import _, { cloneDeep, isArray } from 'lodash';
import _, { cloneDeep } from 'lodash';
import { i18n } from '@kbn/i18n';
import { Assign } from '@kbn/utility-types';
import {
Aggregate,
Bucket,
FiltersAggregate,
FiltersBucketItem,
MultiBucketAggregate,
} from '@elastic/elasticsearch/api/types';
import { Aggregate, Bucket } from '@elastic/elasticsearch/api/types';

import {
IEsSearchResponse,
Expand All @@ -31,6 +25,7 @@ import { AggGroupNames } from './agg_groups';
import { IndexPattern } from '../../index_patterns/index_patterns/index_pattern';
import { TimeRange, getTime, isRangeFilter } from '../../../common';
import { IBucketAggConfig } from './buckets';
import { insertTimeShiftSplit, mergeTimeShifts } from './utils/time_splits';

function removeParentAggs(obj: any) {
for (const prop in obj) {
Expand Down Expand Up @@ -82,6 +77,7 @@ export class AggConfigs {
public indexPattern: IndexPattern;
public timeRange?: TimeRange;
public timeFields?: string[];
public forceNow?: Date;
public hierarchical?: boolean = false;

private readonly typesRegistry: AggTypesRegistryStart;
Expand All @@ -108,6 +104,10 @@ export class AggConfigs {
this.timeFields = timeFields;
}

setForceNow(now: Date | undefined) {
this.forceNow = now;
}

setTimeRange(timeRange: TimeRange) {
this.timeRange = timeRange;

Expand Down Expand Up @@ -239,7 +239,7 @@ export class AggConfigs {
}

if (hasMultipleTimeShifts) {
dslLvlCursor = this.insertTimeShiftSplit(config, timeShifts, dslLvlCursor);
dslLvlCursor = insertTimeShiftSplit(this, config, timeShifts, dslLvlCursor);
}

if (config.type.hasNoDsl) {
Expand Down Expand Up @@ -284,44 +284,6 @@ export class AggConfigs {
return dslTopLvl;
}

private insertTimeShiftSplit(
config: AggConfig,
timeShifts: Record<string, moment.Duration>,
dslLvlCursor: Record<string, any>
) {
if ('splitForTimeShift' in config.type && !config.type.splitForTimeShift(config, this)) {
return dslLvlCursor;
}
if (!this.timeFields || this.timeFields.length < 1) {
throw new Error('Time shift can only be used with configured time field');
}
if (!this.timeRange) {
throw new Error('Time shift can only be used with configured time range');
}
const timeRange = this.timeRange;
const filters: Record<string, unknown> = {};
const timeField = this.timeFields[0];
Object.entries(timeShifts).forEach(([key, shift]) => {
filters[key] = {
range: {
[timeField]: {
// only works if there is a time range
gte: moment(timeRange.from).subtract(shift).toISOString(),
lte: moment(timeRange.to).subtract(shift).toISOString(),
},
},
};
});
dslLvlCursor.time_offset_split = {
filters: {
filters,
},
aggs: {},
};

return dslLvlCursor.time_offset_split.aggs;
}

getAll() {
return [...this.aggs];
}
Expand Down Expand Up @@ -447,8 +409,6 @@ export class AggConfigs {
if (!this.hasTimeShifts()) {
return response;
}
const timeShifts = this.getTimeShifts();
const hasMultipleTimeShifts = Object.keys(timeShifts).length > 1;
const transformedRawResponse = cloneDeep(response.rawResponse);
if (!transformedRawResponse.aggregations) {
transformedRawResponse.aggregations = {
Expand All @@ -457,138 +417,7 @@ export class AggConfigs {
}
const aggCursor = transformedRawResponse.aggregations!;

const requestAggs = this.getRequestAggs();
const bucketAggs = this.aggs.filter(
(agg) => agg.type.type === AggGroupNames.Buckets
) as IBucketAggConfig[];

const mergeAggLevel = (
target: GenericBucket,
source: GenericBucket,
shift: moment.Duration,
aggIndex: number
) => {
Object.entries(source).forEach(([key, val]) => {
// copy over doc count into special key
if (typeof val === 'number' && key === 'doc_count') {
if (shift.asMilliseconds() === 0) {
target.doc_count = val;
} else {
target[`doc_count_${shift.asMilliseconds()}`] = val;
}
} else if (typeof val !== 'object') {
// other meta keys not of interest
return;
} else {
// a sub-agg
const agg = requestAggs.find(
(requestAgg) => key.substr(0, String(requestAgg.id).length) === requestAgg.id
);
if (agg && agg.type.type === AggGroupNames.Metrics) {
const timeShift = agg.getTimeShift();
if (
(timeShift && timeShift.asMilliseconds() === shift.asMilliseconds()) ||
(shift.asMilliseconds() === 0 && !timeShift)
) {
// this is a metric from the current time shift, copy it over
target[key] = source[key];
}
} else if (agg && agg === bucketAggs[aggIndex]) {
const bucketAgg = agg as IBucketAggConfig;
// expected next bucket sub agg
const subAggregate = val as Aggregate;
const buckets = ('buckets' in subAggregate ? subAggregate.buckets : undefined) as
| GenericBucket[]
| Record<string, GenericBucket>
| undefined;
if (!target[key]) {
// sub aggregate only exists in shifted branch, not in base branch - create dummy aggregate
// which will be filled with shifted data
target[key] = {
buckets: isArray(buckets) ? [] : {},
};
}
const baseSubAggregate = target[key] as Aggregate;
// only supported bucket formats in agg configs are array of buckets and record of buckets for filters
const baseBuckets = ('buckets' in baseSubAggregate
? baseSubAggregate.buckets
: undefined) as GenericBucket[] | Record<string, GenericBucket> | undefined;
// merge
if (isArray(buckets) && isArray(baseBuckets)) {
const baseBucketMap: Record<string, GenericBucket> = {};
baseBuckets.forEach((bucket) => {
baseBucketMap[String(bucket.key)] = bucket;
});
buckets.forEach((bucket) => {
const bucketKey = bucketAgg.type.getShiftedKey(bucketAgg, bucket.key, shift);
// if a bucket is missing in the map, create an empty one
if (!baseBucketMap[bucketKey]) {
baseBucketMap[String(bucketKey)] = {
key: bucketKey,
} as GenericBucket;
}
mergeAggLevel(baseBucketMap[bucketKey], bucket, shift, aggIndex + 1);
});
(baseSubAggregate as MultiBucketAggregate).buckets = Object.values(
baseBucketMap
).sort((a, b) => bucketAgg.type.orderBuckets(bucketAgg, a, b));
} else if (baseBuckets && buckets && !isArray(baseBuckets)) {
Object.entries(buckets).forEach(([bucketKey, bucket]) => {
// if a bucket is missing in the base response, create an empty one
if (!baseBuckets[bucketKey]) {
baseBuckets[bucketKey] = {} as GenericBucket;
}
mergeAggLevel(baseBuckets[bucketKey], bucket, shift, aggIndex + 1);
});
}
}
}
});
};
const transformTimeShift = (cursor: Record<string, Aggregate>, aggIndex: number): undefined => {
const shouldSplit = this.aggs[aggIndex].type.splitForTimeShift(this.aggs[aggIndex], this);
if (shouldSplit) {
// multiple time shifts caused a filters agg in the tree we have to merge
if (hasMultipleTimeShifts && cursor.time_offset_split) {
const timeShiftedBuckets = (cursor.time_offset_split as FiltersAggregate)
.buckets as Record<string, FiltersBucketItem>;
const subTree = {};
Object.entries(timeShifts).forEach(([key, shift]) => {
mergeAggLevel(
subTree as GenericBucket,
timeShiftedBuckets[key] as GenericBucket,
shift,
aggIndex
);
});

delete cursor.time_offset_split;
Object.assign(cursor, subTree);
} else {
// otherwise we have to "merge" a single level to shift all keys
const [[, shift]] = Object.entries(timeShifts);
const subTree = {};
mergeAggLevel(subTree, cursor, shift, aggIndex);
Object.assign(cursor, subTree);
}
return;
}
// recurse deeper into the response object
Object.keys(cursor).forEach((subAggId) => {
const subAgg = cursor[subAggId];
if (typeof subAgg !== 'object' || !('buckets' in subAgg)) {
return;
}
if (isArray(subAgg.buckets)) {
subAgg.buckets.forEach((bucket) => transformTimeShift(bucket, aggIndex + 1));
} else {
Object.values(subAgg.buckets).forEach((bucket) =>
transformTimeShift(bucket, aggIndex + 1)
);
}
});
};
transformTimeShift(aggCursor, 0);
mergeTimeShifts(this, aggCursor);
return {
...response,
rawResponse: transformedRawResponse,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/common/search/aggs/metrics/avg_fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const aggAvg = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const aggBucketAvg = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const aggBucketMax = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const aggBucketMin = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const aggBucketSum = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const aggCardinality = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/common/search/aggs/metrics/count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const getCountMetricAgg = () =>
if (!timeShift) {
return bucket.doc_count;
} else {
return bucket[`doc_count_${timeShift.asMilliseconds()}`] ?? bucket.doc_count;
return bucket[`doc_count_${timeShift.asMilliseconds()}`];
}
},
isScalable() {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/common/search/aggs/metrics/count_fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const aggCount = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const aggCumulativeSum = (): FunctionDefinition => ({
types: ['string'],
help: i18n.translate('data.search.aggs.metrics.timeShift.help', {
defaultMessage:
'Specifies whether the time range of documents used for the metric should be shifted by the specified amount',
'Shift the time range for the metric by a set time, for example 1h or 7d. "previous" will use the closest time range from the date histogram or time range filter.',
}),
},
},
Expand Down
Loading