-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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] Bind all time fields to the time picker #63874
Changes from 9 commits
840bbb7
f04a572
c54fdeb
f4073fc
c78f3f1
fbde5fb
c8baa9a
76aaff7
2dcbb76
290611d
5b2f684
e69e101
872d76e
47234a3
1da467a
3158097
cb777ed
b6a9fc2
f4da7aa
d745736
20e69ad
e8dbdb1
b2407f9
c312ab0
6d6befd
8016d5a
cbf7ecc
93e6956
caad0b7
5517aef
5f7d3e5
7a3adef
4b08d1e
8562812
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,16 +32,26 @@ import { Adapters } from '../../../../../plugins/inspector/public'; | |
import { IAggConfigs } from '../aggs'; | ||
import { ISearchSource, SearchSource } from '../search_source'; | ||
import { tabifyAggResponse } from '../tabify'; | ||
import { Filter, Query, serializeFieldFormat, TimeRange } from '../../../common'; | ||
import { FilterManager, getTime } from '../../query'; | ||
import { | ||
Filter, | ||
Query, | ||
serializeFieldFormat, | ||
TimeRange, | ||
IIndexPattern, | ||
RangeFilter, | ||
} from '../../../common'; | ||
import { FilterManager } from '../../query'; | ||
import { getSearchService, getQueryService, getIndexPatterns } from '../../services'; | ||
import { buildTabularInspectorData } from './build_tabular_inspector_data'; | ||
import { getRequestInspectorStats, getResponseInspectorStats, serializeAggConfig } from './utils'; | ||
import { calculateBounds, getTimeForField } from '../../query/timefilter/get_time'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. //nit Should be exported from |
||
|
||
export interface RequestHandlerParams { | ||
searchSource: ISearchSource; | ||
aggs: IAggConfigs; | ||
timeRange?: TimeRange; | ||
timeFields?: string[]; | ||
indexPattern?: IIndexPattern; | ||
query?: Query; | ||
filters?: Filter[]; | ||
forceFetch: boolean; | ||
|
@@ -65,12 +75,15 @@ interface Arguments { | |
partialRows: boolean; | ||
includeFormatHints: boolean; | ||
aggConfigs: string; | ||
timeField?: string[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The parameter is not defined is required in the definition below, so it can be skipped and thus can be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so we have timeField and timeFields ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@timroes This actually highlights weird thing about the Expression function typings which I don't think we have been doing consistently: The types are what get enforced inside the function body. So while any arg with So which is right? Do we write the typings to be accurate for the users of the functions (which means lots of IMO your proposed approach is probably better (include There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ^ Thinking about this more, in At any rate; that's a topic for a different thread 🙂 |
||
} | ||
|
||
const handleCourierRequest = async ({ | ||
searchSource, | ||
aggs, | ||
timeRange, | ||
timeFields, | ||
indexPattern, | ||
query, | ||
filters, | ||
forceFetch, | ||
|
@@ -111,9 +124,22 @@ const handleCourierRequest = async ({ | |
return aggs.onSearchRequestStart(paramSearchSource, options); | ||
}); | ||
|
||
if (timeRange) { | ||
// If timeFields have been specified, use the specified ones, otherwise use primary time field of index | ||
// pattern if it's available. | ||
const allTimeFields = | ||
timeFields && timeFields.length > 0 | ||
? timeFields | ||
: [ | ||
indexPattern?.fields.find(field => field.name === indexPattern.timeFieldName)?.name, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indexPattern.getTimeField |
||
].filter((fieldName): fieldName is string => Boolean(fieldName)); | ||
|
||
// If a timeRange has been specified and we had at least one timeField available, create range | ||
// filters for that those time fields | ||
if (timeRange && allTimeFields.length > 0) { | ||
timeFilterSearchSource.setField('filter', () => { | ||
return getTime(searchSource.getField('index'), timeRange); | ||
return allTimeFields | ||
.map(fieldName => getTimeForField(indexPattern, timeRange, fieldName)) | ||
.filter((rangeFilter): rangeFilter is RangeFilter => Boolean(rangeFilter)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. //nit |
||
}); | ||
} | ||
|
||
|
@@ -181,11 +207,13 @@ const handleCourierRequest = async ({ | |
|
||
(searchSource as any).finalResponse = resp; | ||
|
||
const parsedTimeRange = timeRange ? getTime(aggs.indexPattern, timeRange) : null; | ||
const parsedTimeRange = timeRange ? calculateBounds(timeRange) : null; | ||
const tabifyParams = { | ||
metricsAtAllLevels, | ||
partialRows, | ||
timeRange: parsedTimeRange ? parsedTimeRange.range : undefined, | ||
timeRange: parsedTimeRange | ||
? { from: parsedTimeRange.min, to: parsedTimeRange.max, timeFields: allTimeFields } | ||
: undefined, | ||
}; | ||
|
||
const tabifyCacheHash = calculateObjectHash({ tabifyAggs: aggs, ...tabifyParams }); | ||
|
@@ -242,6 +270,11 @@ export const esaggs = (): ExpressionFunctionDefinition<typeof name, Input, Argum | |
default: '""', | ||
help: '', | ||
}, | ||
timeField: { | ||
types: ['string'], | ||
help: '', | ||
multi: true, | ||
}, | ||
}, | ||
async fn(input, args, { inspectorAdapters, abortSignal }) { | ||
const indexPatterns = getIndexPatterns(); | ||
|
@@ -260,9 +293,11 @@ export const esaggs = (): ExpressionFunctionDefinition<typeof name, Input, Argum | |
const response = await handleCourierRequest({ | ||
searchSource, | ||
aggs, | ||
indexPattern, | ||
timeRange: get(input, 'timeRange', undefined), | ||
query: get(input, 'query', undefined), | ||
filters: get(input, 'filters', undefined), | ||
timeFields: args.timeField, | ||
forceFetch: true, | ||
metricsAtAllLevels: args.metricsAtAllLevels, | ||
partialRows: args.partialRows, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,11 @@ | |
|
||
import { TabifyBuckets } from './buckets'; | ||
import { AggGroupNames } from '../aggs'; | ||
import moment from 'moment'; | ||
|
||
interface Bucket { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. defining an interface in tests ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous definition is |
||
key: number | string; | ||
} | ||
|
||
describe('Buckets wrapper', () => { | ||
const check = (aggResp: any, count: number, keys: string[]) => { | ||
|
@@ -187,9 +192,9 @@ describe('Buckets wrapper', () => { | |
}, | ||
}; | ||
const timeRange = { | ||
gte: 150, | ||
lte: 350, | ||
name: 'date', | ||
from: moment(150), | ||
to: moment(350), | ||
timeFields: ['date'], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
|
@@ -204,9 +209,9 @@ describe('Buckets wrapper', () => { | |
}, | ||
}; | ||
const timeRange = { | ||
gte: 150, | ||
lte: 350, | ||
name: 'date', | ||
from: moment(150), | ||
to: moment(350), | ||
timeFields: ['date'], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
|
@@ -221,9 +226,9 @@ describe('Buckets wrapper', () => { | |
}, | ||
}; | ||
const timeRange = { | ||
gte: 100, | ||
lte: 400, | ||
name: 'date', | ||
from: moment(100), | ||
to: moment(400), | ||
timeFields: ['date'], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
|
@@ -238,13 +243,47 @@ describe('Buckets wrapper', () => { | |
}, | ||
}; | ||
const timeRange = { | ||
gte: 150, | ||
lte: 350, | ||
name: 'date', | ||
from: moment(150), | ||
to: moment(350), | ||
timeFields: ['date'], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
expect(buckets).toHaveLength(4); | ||
}); | ||
|
||
test('does drop bucket when multiple time fields specified', () => { | ||
const aggParams = { | ||
drop_partials: true, | ||
field: { | ||
name: 'date', | ||
}, | ||
}; | ||
const timeRange = { | ||
from: moment(100), | ||
to: moment(350), | ||
timeFields: ['date', 'other_datefield'], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
expect(buckets.buckets.map((b: Bucket) => b.key)).toEqual([100, 200]); | ||
}); | ||
|
||
test('does not drop bucket when no timeFields have been specified', () => { | ||
const aggParams = { | ||
drop_partials: true, | ||
field: { | ||
name: 'date', | ||
}, | ||
}; | ||
const timeRange = { | ||
from: moment(100), | ||
to: moment(350), | ||
timeFields: [], | ||
}; | ||
const buckets = new TabifyBuckets(aggResp, aggParams, timeRange); | ||
|
||
expect(buckets.buckets.map((b: Bucket) => b.key)).toEqual([0, 100, 200, 300]); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ | |
import { get } from 'lodash'; | ||
import { TabbedAggResponseWriter } from './response_writer'; | ||
import { TabifyBuckets } from './buckets'; | ||
import { TabbedResponseWriterOptions, TabbedRangeFilterParams } from './types'; | ||
import { TabbedResponseWriterOptions } from './types'; | ||
import { AggResponseBucket } from './types'; | ||
import { AggGroupNames, IAggConfigs } from '../aggs'; | ||
|
||
|
@@ -54,7 +54,7 @@ export function tabifyAggResponse( | |
switch (agg.type.type) { | ||
case AggGroupNames.Buckets: | ||
const aggBucket = get(bucket, agg.id); | ||
const tabifyBuckets = new TabifyBuckets(aggBucket, agg.params, timeRange); | ||
const tabifyBuckets = new TabifyBuckets(aggBucket, agg.params, respOpts?.timeRange); | ||
|
||
if (tabifyBuckets.length) { | ||
tabifyBuckets.forEach((subBucket, tabifyBucketKey) => { | ||
|
@@ -153,20 +153,6 @@ export function tabifyAggResponse( | |
doc_count: esResponse.hits.total, | ||
}; | ||
|
||
let timeRange: TabbedRangeFilterParams | undefined; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yay |
||
|
||
// Extract the time range object if provided | ||
if (respOpts && respOpts.timeRange) { | ||
const [timeRangeKey] = Object.keys(respOpts.timeRange); | ||
|
||
if (timeRangeKey) { | ||
timeRange = { | ||
name: timeRangeKey, | ||
...respOpts.timeRange[timeRangeKey], | ||
}; | ||
} | ||
} | ||
|
||
collectBucket(aggConfigs, write, topLevelBucket, '', 1); | ||
|
||
return write.response(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getTimeFilter \ getTimeFilterDefault :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ The naming
getTime
andgetTimeFilter
is a bit unclear.another nit - It would also be nice to maybe add an explanatory comment to each of the functions here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just took a harder look at this, thanks for the prodding to do this. The naming confusion was because both functions were basically 90% similar code with one extra input, so I've condensed these into the same function with an
options
argument instead of positional arguments.