import { get , noop , find , every } from 'lodash' ; import moment from 'moment-timezone' ; import { i18n } from '@osd/i18n' ; import { OSD_FIELD_TYPES , TimeRange , TimeRangeBounds , UI_SETTINGS } from '../../../../common' ; import { intervalOptions , autoInterval , isAutoInterval } from './_interval_options' ; import { createFilterDateHistogram } from './create_filter/date_histogram' ; import { BucketAggType , IBucketAggConfig } from './bucket_agg_type' ; import { BUCKET_TYPES } from './bucket_agg_types' ; import { ExtendedBounds } from './lib/extended_bounds' ; import { TimeBuckets } from './lib/time_buckets' ; import { writeParams } from '../agg_params' ; import { isMetricAggType } from '../metrics/metric_agg_type' ; import { BaseAggParams } from '../types' ; import { dateHistogramInterval } from '../utils' ; internal export type CalculateBoundsFn = ( timeRange : TimeRange ) => TimeRangeBounds ; const updateTimeBuckets = ( agg , calculateBounds , customBuckets ? ) => { const bounds = agg . params . timeRange && ( agg . fieldIsTimeField ( ) || isAutoInterval ( agg . params . interval ) ) ? calculateBounds ( agg . params . timeRange ) : undefined ; const buckets = customBuckets || agg . buckets ; buckets . setBounds ( bounds ) ; buckets . setInterval ( agg . params . interval ) ; } ; export interface DateHistogramBucketAggDependencies { calculateBounds : CalculateBoundsFn ; isDefaultTimezone : ( ) => boolean ; getConfig : < T = any > ( key : string ) => T ; } export interface IBucketDateHistogramAggConfig extends IBucketAggConfig { buckets : TimeBuckets ; } export function isDateHistogramBucketAggConfig ( agg ) { return Boolean ( agg . buckets ) ; } export interface AggParamsDateHistogram extends BaseAggParams { field ? : string ; timeRange ? : TimeRange ; useNormalizedOpenSearchInterval ? : boolean ; scaleMetricValues ? : boolean ; interval ? : string ; time_zone ? : string ; drop_partials ? : boolean ; format ? : string ; min_doc_count ? : number ; extended_bounds ? : ExtendedBounds ; } export const getDateHistogramBucketAgg = ( { calculateBounds , isDefaultTimezone , getConfig , } ) => new BucketAggType < IBucketDateHistogramAggConfig > ( { name : BUCKET_TYPES . DATE_HISTOGRAM , title : i18n . translate ( 'data.search.aggs.buckets.dateHistogramTitle' , { defaultMessage : 'Date Histogram' , } ) , ordered : { date : true , } , makeLabel ( agg ) { let output = { } ; if ( this . params ) { output = writeParams ( this . params , agg ) ; } const field = agg . getFieldDisplayName ( ) ; return i18n . translate ( 'data.search.aggs.buckets.dateHistogramLabel' , { defaultMessage : '{fieldName} per {intervalDescription}' , values : { fieldName : field , intervalDescription : output . metricScaleText || output . bucketInterval . description , } , } ) ; } , createFilter : createFilterDateHistogram , decorateAggConfig ( ) { let buckets ; return { buckets : { configurable : true , get ( ) { if ( buckets ) return buckets ; buckets = new TimeBuckets ( { 'histogram:maxBars' : getConfig ( UI_SETTINGS . HISTOGRAM_MAX_BARS ) , 'histogram:barTarget' : getConfig ( UI_SETTINGS . HISTOGRAM_BAR_TARGET ) , dateFormat : getConfig ( 'dateFormat' ) , 'dateFormat:scaled' : getConfig ( 'dateFormat:scaled' ) , } ) ; updateTimeBuckets ( this , calculateBounds , buckets ) ; return buckets ; } , } as any , } ; } , getSerializedFormat ( agg ) { return { id : 'date' , params : { pattern : agg . buckets . getScaledDateFormat ( ) , } , } ; } , params : [ { name : 'field' , type : 'field' , filterFieldTypes : OSD_FIELD_TYPES . DATE , default ( agg ) { return agg . getIndexPattern ( ) . timeFieldName ; } , onChange ( agg ) { if ( isAutoInterval ( get ( agg , 'params.interval' ) ) && ! agg . fieldIsTimeField ( ) ) { delete agg . params . interval ; } } , } , { name : 'timeRange' , default : null , write : noop , } , { name : 'useNormalizedOpenSearchInterval' , default : true , write : noop , } , { name : 'scaleMetricValues' , default : false , write : noop , advanced : true , } , { name : 'interval' , deserialize ( state , agg ) { if ( state === 'custom' ) { return get ( agg , 'params.customInterval' ) ; } const interval = find ( intervalOptions , { val : state } ) ; if ( ! interval && state === 'year' ) { return 'y' ; } return state ; } , default : autoInterval , options : intervalOptions , write ( agg , output , aggs ) { updateTimeBuckets ( agg , calculateBounds ) ; const { useNormalizedOpenSearchInterval , scaleMetricValues } = agg . params ; const interval = agg . buckets . getInterval ( useNormalizedOpenSearchInterval ) ; output . bucketInterval = interval ; if ( interval . expression === '0ms' ) { return ; } output . params = { ... output . params , ... dateHistogramInterval ( interval . expression ) , } ; const scaleMetrics = scaleMetricValues && interval . scaled && interval . scale && interval . scale < 1 ; if ( scaleMetrics && aggs ) { const metrics = aggs . aggs . filter ( ( a ) => isMetricAggType ( a . type ) ) ; const all = every ( metrics , ( a ) => { const { type } = a ; if ( isMetricAggType ( type ) ) { return type . isScalable ( ) ; } } ) ; if ( all ) { output . metricScale = interval . scale ; output . metricScaleText = interval . preScaled ?. description || '' ; } } } , } , { name : 'time_zone' , default : undefined , serialize : noop , write ( agg , output ) { let tz = agg . params . time_zone ; if ( ! tz && agg . params . field ) { tz = get ( agg . getIndexPattern ( ) , [ 'typeMeta' , 'aggs' , 'date_histogram' , agg . params . field . name , 'time_zone' , ] ) ; } if ( ! tz ) { const detectedTimezone = moment . tz . guess ( ) ; const tzOffset = moment ( ) . format ( 'Z' ) ; tz = isDefaultTimezone ( ) ? detectedTimezone || tzOffset : getConfig ( 'dateFormat:tz' ) ; } output . params . time_zone = tz ; } , } , { name : 'drop_partials' , default : false , write : noop , shouldShow : ( agg ) => { const field = agg . params . field ; return field && field . name && field . name === agg . getIndexPattern ( ) . timeFieldName ; } , } , { name : 'format' , } , { name : 'min_doc_count' , default : 1 , } , { name : 'extended_bounds' , default : { } , write ( agg , output ) { const val = agg . params . extended_bounds ; if ( val . min != null || val . max != null ) { output . params . extended_bounds = { min : moment ( val . min ) . valueOf ( ) , max : moment ( val . max ) . valueOf ( ) , } ; return ; } } , } , ] , } ) ;