From 4c83ab56102b337421018661b7044f39834216f5 Mon Sep 17 00:00:00 2001 From: Giordano Ricci Date: Mon, 10 May 2021 14:28:47 +0100 Subject: [PATCH] Elasticsearch: automatically set date_histogram field based on data source configuration (#33840) --- .../state/reducer.test.ts | 41 +++++++++++++------ .../BucketAggregationsEditor/state/reducer.ts | 7 ++-- .../QueryEditor/ElasticsearchQueryContext.tsx | 4 +- .../datasource/elasticsearch/query_def.ts | 4 +- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.test.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.test.ts index b9e5554ea07..aea19cff4dc 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.test.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.test.ts @@ -10,7 +10,7 @@ import { changeBucketAggregationType, removeBucketAggregation, } from './actions'; -import { reducer } from './reducer'; +import { createReducer } from './reducer'; import { initQuery } from '../../state'; import { ElasticsearchQuery } from 'app/plugins/datasource/elasticsearch/types'; @@ -29,7 +29,7 @@ describe('Bucket Aggregations Reducer', () => { }; reducerTester() - .givenReducer(reducer, []) + .givenReducer(createReducer('@timestamp'), []) .whenActionIsDispatched(addBucketAggregation(firstAggregation.id)) .thenStateShouldEqual([firstAggregation]) .whenActionIsDispatched(addBucketAggregation(secondAggregation.id)) @@ -48,7 +48,7 @@ describe('Bucket Aggregations Reducer', () => { }; reducerTester() - .givenReducer(reducer, [firstAggregation, secondAggregation]) + .givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation]) .whenActionIsDispatched(removeBucketAggregation(firstAggregation.id)) .thenStateShouldEqual([secondAggregation]); }); @@ -70,7 +70,7 @@ describe('Bucket Aggregations Reducer', () => { }; reducerTester() - .givenReducer(reducer, [firstAggregation, secondAggregation]) + .givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation]) .whenActionIsDispatched(changeBucketAggregationType(secondAggregation.id, expectedSecondAggregation.type)) .thenStateShouldEqual([firstAggregation, expectedSecondAggregation]); }); @@ -91,7 +91,7 @@ describe('Bucket Aggregations Reducer', () => { }; reducerTester() - .givenReducer(reducer, [firstAggregation, secondAggregation]) + .givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation]) .whenActionIsDispatched(changeBucketAggregationField(secondAggregation.id, expectedSecondAggregation.field)) .thenStateShouldEqual([firstAggregation, expectedSecondAggregation]); }); @@ -106,7 +106,7 @@ describe('Bucket Aggregations Reducer', () => { ]; reducerTester() - .givenReducer(reducer, initialState) + .givenReducer(createReducer('@timestamp'), initialState) // If the new metric aggregation is `isSingleMetric` we should remove all bucket aggregations. .whenActionIsDispatched(changeMetricType('Some id', 'raw_data')) .thenStatePredicateShouldEqual((newState) => newState?.length === 0) @@ -137,17 +137,34 @@ describe('Bucket Aggregations Reducer', () => { }; reducerTester() - .givenReducer(reducer, [firstAggregation, secondAggregation]) + .givenReducer(createReducer('@timestamp'), [firstAggregation, secondAggregation]) .whenActionIsDispatched( changeBucketAggregationSetting(firstAggregation, 'min_doc_count', expectedSettings.min_doc_count!) ) .thenStateShouldEqual([{ ...firstAggregation, settings: expectedSettings }, secondAggregation]); }); - it('Should correctly initialize first Bucket Aggregation', () => { - reducerTester() - .givenReducer(reducer, []) - .whenActionIsDispatched(initQuery()) - .thenStateShouldEqual([defaultBucketAgg('2')]); + describe('Initialization', () => { + it('Correctly adds a default Date Histogram if there is no aggregation', () => { + const defaultTimeField = '@timestamp'; + + reducerTester() + .givenReducer(createReducer(defaultTimeField), []) + .whenActionIsDispatched(initQuery()) + .thenStateShouldEqual([{ ...defaultBucketAgg('2'), field: defaultTimeField }]); + }); + + it('Does NOT change aggregations if there is already one', () => { + const bucketAgg: DateHistogram = { + id: '18', + type: 'date_histogram', + field: '@my_time_field', + }; + + reducerTester() + .givenReducer(createReducer('@timestamp'), [bucketAgg]) + .whenActionIsDispatched(initQuery()) + .thenStateShouldEqual([bucketAgg]); + }); }); }); diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts index a440b6e03d4..460f28f1918 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/BucketAggregationsEditor/state/reducer.ts @@ -15,7 +15,7 @@ import { import { bucketAggregationConfig } from '../utils'; import { removeEmpty } from '../../../../utils'; -export const reducer = ( +export const createReducer = (defaultTimeField: string) => ( state: ElasticsearchQuery['bucketAggs'], action: BucketAggregationAction | ChangeMetricTypeAction | InitAction ): ElasticsearchQuery['bucketAggs'] => { @@ -78,7 +78,7 @@ export const reducer = ( // Else, if there are no bucket aggregations we restore a default one. // This happens when switching from a metric that requires the absence of bucket aggregations to // one that requires it. - return [defaultBucketAgg()]; + return [{ ...defaultBucketAgg('2'), field: defaultTimeField }]; } return state; @@ -105,7 +105,8 @@ export const reducer = ( if (state?.length || 0 > 0) { return state; } - return [defaultBucketAgg('2')]; + + return [{ ...defaultBucketAgg('2'), field: defaultTimeField }]; default: return state; diff --git a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx index 8b3a03861e1..03fa1de7a6b 100644 --- a/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx +++ b/public/app/plugins/datasource/elasticsearch/components/QueryEditor/ElasticsearchQueryContext.tsx @@ -4,7 +4,7 @@ import { combineReducers, useStatelessReducer, DispatchContext } from '../../hoo import { ElasticsearchQuery } from '../../types'; import { reducer as metricsReducer } from './MetricAggregationsEditor/state/reducer'; -import { reducer as bucketAggsReducer } from './BucketAggregationsEditor/state/reducer'; +import { createReducer as createBucketAggsReducer } from './BucketAggregationsEditor/state/reducer'; import { aliasPatternReducer, queryReducer, initQuery } from './state'; import { TimeRange } from '@grafana/data'; @@ -40,7 +40,7 @@ export const ElasticsearchProvider = ({ query: queryReducer, alias: aliasPatternReducer, metrics: metricsReducer, - bucketAggs: bucketAggsReducer, + bucketAggs: createBucketAggsReducer(datasource.timeField), }); const dispatch = useStatelessReducer( diff --git a/public/app/plugins/datasource/elasticsearch/query_def.ts b/public/app/plugins/datasource/elasticsearch/query_def.ts index 6d7fb8377fa..55d02d21065 100644 --- a/public/app/plugins/datasource/elasticsearch/query_def.ts +++ b/public/app/plugins/datasource/elasticsearch/query_def.ts @@ -1,4 +1,4 @@ -import { BucketAggregation } from './components/QueryEditor/BucketAggregationsEditor/aggregations'; +import { DateHistogram } from './components/QueryEditor/BucketAggregationsEditor/aggregations'; import { ExtendedStat, MetricAggregation, @@ -36,7 +36,7 @@ export function defaultMetricAgg(id = '1'): MetricAggregation { return { type: 'count', id }; } -export function defaultBucketAgg(id = '1'): BucketAggregation { +export function defaultBucketAgg(id = '1'): DateHistogram { return { type: 'date_histogram', id, settings: { interval: 'auto' } }; }