Skip to content

Commit c5e643b

Browse files
authored
Merge pull request #111 from quickwit-oss/fmassot/fix-time-range-get-tag-keys-values
Fix time range for getTagKeys and getTagValues function.
2 parents 7a328e7 + 320e733 commit c5e643b

File tree

9 files changed

+1310
-2194
lines changed

9 files changed

+1310
-2194
lines changed

package-lock.json

Lines changed: 1266 additions & 2146 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"license": "AGPL-3.0",
2222
"devDependencies": {
2323
"@babel/core": "^7.21.4",
24-
"@grafana/e2e": "10.0.3",
25-
"@grafana/e2e-selectors": "10.0.3",
24+
"@grafana/e2e": "10.1.9",
25+
"@grafana/e2e-selectors": "10.1.9",
2626
"@grafana/eslint-config": "^6.0.0",
2727
"@grafana/tsconfig": "^1.2.0-rc1",
2828
"@storybook/addon-essentials": "^7.6.17",
@@ -74,10 +74,10 @@
7474
},
7575
"dependencies": {
7676
"@emotion/css": "^11.11",
77-
"@grafana/data": "10.0.3",
78-
"@grafana/experimental": "^1.7.6",
79-
"@grafana/runtime": "10.0.3",
80-
"@grafana/ui": "10.0.3",
77+
"@grafana/data": "10.1.9",
78+
"@grafana/experimental": "^1.7.10",
79+
"@grafana/runtime": "10.1.9",
80+
"@grafana/ui": "10.1.9",
8181
"@reduxjs/toolkit": "^1.9.5",
8282
"@uiw/react-codemirror": "^4.21.21",
8383
"lucene": "^2.1.1",

src/LogContext/LogContextProvider.ts

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
dateTime,
1212
LogRowModel,
1313
rangeUtil,
14+
TimeRange,
1415
} from '@grafana/data';
1516

1617
import { ElasticsearchQuery, Logs, LogsSortDirection} from '../types';
@@ -26,20 +27,16 @@ export enum LogRowContextQueryDirection {
2627
Forward = 'FORWARD',
2728
}
2829

29-
function createContextTimeRange(rowTimeEpochMs: number, direction: string) {
30+
export function createContextTimeRange(rowTimeEpochMs: number, direction?: LogRowContextQueryDirection): TimeRange {
3031
const offset = 7;
31-
// For log context, we want to request data from 7 subsequent/previous indices
32-
if (direction === LogRowContextQueryDirection.Forward) {
33-
return {
34-
from: dateTime(rowTimeEpochMs).utc(),
35-
to: dateTime(rowTimeEpochMs).add(offset, 'hours').utc(),
36-
};
37-
} else {
38-
return {
39-
from: dateTime(rowTimeEpochMs).subtract(offset, 'hours').utc(),
40-
to: dateTime(rowTimeEpochMs).utc(),
41-
};
32+
const timeFrom = dateTime(rowTimeEpochMs)
33+
const timeTo = dateTime(rowTimeEpochMs)
34+
35+
const timeRange = {
36+
from: (direction === LogRowContextQueryDirection.Forward) ? timeFrom.utc() : timeFrom.subtract(offset, 'hours').utc(),
37+
to: (direction === LogRowContextQueryDirection.Backward) ? timeTo.utc() : timeTo.add(offset, 'hours').utc(),
4238
}
39+
return { ...timeRange, raw:timeRange }
4340
}
4441

4542
export class LogContextProvider {
@@ -76,12 +73,7 @@ export class LogContextProvider {
7673
query: this.contextQuery == null ? origQuery?.query : this.contextQuery,
7774
};
7875

79-
const timeRange = createContextTimeRange(row.timeEpochMs, direction);
80-
const range = {
81-
from: timeRange.from,
82-
to: timeRange.to,
83-
raw: timeRange,
84-
};
76+
const range = createContextTimeRange(row.timeEpochMs, direction);
8577

8678
const interval = rangeUtil.calculateInterval(range, 1);
8779

src/LogContext/components/LogContextUI.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { DatasourceContext } from "@/components/QueryEditor/ElasticsearchQueryCo
1111
import { BaseQuickwitDataSource } from "@/datasource/base";
1212
import { useDatasourceFields } from "@/datasource/utils";
1313
import { Field, FieldContingency, Filter } from "../types";
14+
import { createContextTimeRange } from "LogContext/LogContextProvider";
1415

1516
// TODO : define sensible defaults here
1617
// const excludedFields = [
@@ -51,7 +52,9 @@ export function LogContextUI(props: LogContextUIProps ){
5152
const {query, parsedQuery, setQuery, setParsedQuery} = builder;
5253
const [canRunQuery, setCanRunQuery] = useState<boolean>(false);
5354
const {row, origQuery, updateQuery, runContextQuery } = props;
54-
const {fields, getSuggestions} = useDatasourceFields(props.datasource);
55+
56+
const fieldsSuggestionTimeRange = useMemo(()=>createContextTimeRange(row.timeEpochMs), [row])
57+
const {fields, getSuggestions} = useDatasourceFields(props.datasource, fieldsSuggestionTimeRange);
5558

5659
useEffect(()=>{
5760
setQuery(origQuery?.query || '')

src/components/QueryEditor/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { useDispatch } from '@/hooks/useStatelessReducer';
1313
import { ElasticsearchQuery } from '@/types';
1414

1515
import { BucketAggregationsEditor } from './BucketAggregationsEditor';
16-
import { ElasticsearchProvider, useDatasource } from './ElasticsearchQueryContext';
16+
import { ElasticsearchProvider, useDatasource, useRange } from './ElasticsearchQueryContext';
1717
import { MetricAggregationsEditor } from './MetricAggregationsEditor';
1818
import { metricAggregationConfig } from './MetricAggregationsEditor/utils';
1919
import { changeQuery } from './state';
@@ -67,7 +67,8 @@ type ElasticSearchQueryFieldProps = {
6767
export const ElasticSearchQueryField = ({ value, onChange, onSubmit }: ElasticSearchQueryFieldProps) => {
6868
const styles = useStyles2(getStyles);
6969
const datasource = useDatasource()
70-
const { getSuggestions } = useDatasourceFields(datasource);
70+
const range = useRange();
71+
const { getSuggestions } = useDatasourceFields(datasource, range);
7172

7273
return (
7374
<div className={styles.queryItem}>

src/datasource/base.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ type FieldCapsSpec = {
4646
aggregatable?: boolean,
4747
searchable?: boolean,
4848
type?: string[],
49-
_range?: TimeRange
49+
range?: TimeRange
5050
}
5151

5252
export class BaseQuickwitDataSource
@@ -163,7 +163,8 @@ export class BaseQuickwitDataSource
163163
)
164164
}
165165

166-
getFields(spec: FieldCapsSpec={}, range = getDefaultTimeRange()): Observable<MetricFindValue[]> {
166+
getFields(spec: FieldCapsSpec={}): Observable<MetricFindValue[]> {
167+
const range = spec.range || getDefaultTimeRange();
167168
return from(this.getResource('_elastic/' + this.index + '/_field_caps', {
168169
start_timestamp: Math.floor(range.from.valueOf()/SECOND),
169170
end_timestamp: Math.ceil(range.to.valueOf()/SECOND),
@@ -209,17 +210,16 @@ export class BaseQuickwitDataSource
209210
/**
210211
* Get tag keys for adhoc filters
211212
*/
212-
getTagKeys(spec?: FieldCapsSpec) {
213-
const fields = this.getFields(spec)
213+
getTagKeys(options: any) {
214+
const fields = this.getFields({aggregatable:true, range: options.timeRange})
214215
return lastValueFrom(fields, {defaultValue:[]});
215216
}
216217

217218
/**
218219
* Get tag values for adhoc filters
219220
*/
220221
getTagValues(options: any) {
221-
const range = getDefaultTimeRange();
222-
const terms = this.getTerms({ field: options.key }, range)
222+
const terms = this.getTerms({ field: options.key }, options.timeRange)
223223
return lastValueFrom(terms, {defaultValue:[]});
224224
}
225225

@@ -292,7 +292,7 @@ export class BaseQuickwitDataSource
292292
if (query) {
293293
if (parsedQuery.find === 'fields') {
294294
parsedQuery.type = this.interpolateLuceneQuery(parsedQuery.type);
295-
return lastValueFrom(this.getFields({aggregatable:true, type:parsedQuery.type, _range:range}), {defaultValue:[]});
295+
return lastValueFrom(this.getFields({aggregatable:true, type:parsedQuery.type, range:range}), {defaultValue:[]});
296296
}
297297
if (parsedQuery.find === 'terms') {
298298
parsedQuery.field = this.interpolateLuceneQuery(parsedQuery.field);

src/datasource/utils.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BaseQuickwitDataSource } from "./base";
22
import { useState, useEffect, useCallback } from "react";
3-
import{ MetricFindValue } from '@grafana/data';
3+
import{ MetricFindValue, TimeRange } from '@grafana/data';
44

55
/**
66
* Provide suggestions based on datasource fields
@@ -15,22 +15,22 @@ export type Suggestion = {
1515
}>
1616
}
1717

18-
export function useDatasourceFields(datasource: BaseQuickwitDataSource) {
18+
export function useDatasourceFields(datasource: BaseQuickwitDataSource, range: TimeRange) {
1919
const [fields, setFields] = useState<MetricFindValue[]>([]);
2020

2121
useEffect(() => {
2222
if (datasource.getTagKeys) {
23-
datasource.getTagKeys({ searchable: true }).then(setFields);
23+
datasource.getTagKeys({ searchable: true, timeRange: range}).then(setFields);
2424
}
25-
}, [datasource, setFields]);
25+
}, [datasource, range, setFields]);
2626

2727
const getSuggestions = useCallback(async (word: string): Promise<Suggestion> => {
2828
let suggestions: Suggestion = { from: 0, options: [] };
2929

3030
const wordIsField = word.match(/([^:\s]+):"?([^"\s]*)"?/);
3131
if (wordIsField?.length) {
3232
const [_match, fieldName, _fieldValue] = wordIsField;
33-
const candidateValues = await datasource.getTagValues({ key: fieldName });
33+
const candidateValues = await datasource.getTagValues({ key: fieldName, timeRange: range });
3434
suggestions.from = fieldName.length + 1; // Replace only the value part
3535
suggestions.options = candidateValues.map(v => ({
3636
type: 'text',
@@ -47,7 +47,7 @@ export function useDatasourceFields(datasource: BaseQuickwitDataSource) {
4747
}
4848
return suggestions;
4949

50-
}, [datasource, fields]);
50+
}, [datasource, fields, range]);
5151

5252
return {fields, getSuggestions}
5353
}

src/hooks/useFields.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,39 +50,39 @@ describe('useFields hook', () => {
5050
{ wrapper, initialProps: 'cardinality' }
5151
);
5252
result.current();
53-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:[], _range:timeRange});
53+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:[], range:timeRange});
5454

5555
// All other metric aggregations only work on numbers
5656
rerender('avg');
5757
result.current();
58-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], _range:timeRange});
58+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], range:timeRange});
5959

6060
//
6161
// BUCKET AGGREGATIONS
6262
//
6363
// Date Histrogram only works on dates
6464
rerender('date_histogram');
6565
result.current();
66-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['date'], _range:timeRange});
66+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['date'], range:timeRange});
6767

6868
// Histrogram only works on numbers
6969
rerender('histogram');
7070
result.current();
71-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], _range:timeRange});
71+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], range:timeRange});
7272

7373
// Geohash Grid only works on geo_point data
7474
rerender('geohash_grid');
7575
result.current();
76-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['geo_point'], _range:timeRange});
76+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['geo_point'], range:timeRange});
7777

7878
// All other bucket aggregation work on any kind of data
7979
rerender('terms');
8080
result.current();
81-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:[], _range:timeRange});
81+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:[], range:timeRange});
8282

8383
// top_metrics work on only on numeric data in 7.7
8484
rerender('top_metrics');
8585
result.current();
86-
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], _range:timeRange});
86+
expect(getFields).toHaveBeenLastCalledWith({aggregatable:true, type:['number'], range:timeRange});
8787
});
8888
});

src/hooks/useFields.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const useFields = (type: AggregationType | string[]) => {
6262
return async (q?: string) => {
6363
// _mapping doesn't support filtering, we avoid sending a request everytime q changes
6464
if (!rawFields) {
65-
rawFields = await lastValueFrom(datasource.getFields({aggregatable:true, type:filter, _range:range}));
65+
rawFields = await lastValueFrom(datasource.getFields({aggregatable:true, type:filter, range:range}));
6666
}
6767

6868
return rawFields.filter(({ text }) => q === undefined || text.includes(q)).map(toSelectableValue);

0 commit comments

Comments
 (0)