import { isAutoInterval } from '../_interval_options' ; import { OPENSEARCH_FIELD_TYPES } from '../../../../types' ; interface IntervalValuesRange { min : number ; max : number ; } export interface CalculateHistogramIntervalParams { interval : string ; maxBucketsUiSettings : number ; maxBucketsUserInput ? : number | '' ; esTypes : OPENSEARCH_FIELD_TYPES [ ] ; intervalBase ? : number ; values ? : IntervalValuesRange ; } /**
 * Round interval by order of magnitude to provide clean intervals
 */ const roundInterval = ( minInterval ) => { const orderOfMagnitude = Math . pow ( 10 , Math . floor ( Math . log10 ( minInterval ) ) ) ; let interval = orderOfMagnitude ; while ( interval < minInterval ) { interval += orderOfMagnitude ; } return interval ; } ; const calculateForGivenInterval = ( diff , interval , maxBucketsUiSettings ) => { const bars = diff / interval ; if ( bars > maxBucketsUiSettings ) { const minInterval = diff / maxBucketsUiSettings ; return roundInterval ( minInterval ) ; } return interval ; } ; /**
 * Algorithm for determining auto-interval

   1. Define maxBars as Math.min(<user input>, <max buckets setting>)
   2. Find the min and max values in the data
   3. Subtract the min from max to get diff
   4. Set exactInterval to diff / maxBars
   5. Based on exactInterval, find the power of 10 that's lower and higher
   6. Find the number of expected buckets that lowerPower would create: diff / lowerPower
   7. Find the number of expected buckets that higherPower would create: diff / higherPower
   8. There are three possible final intervals, pick the one that's closest to maxBars:
     - The lower power of 10
     - The lower power of 10, times 2
     - The lower power of 10, times 5
 **/ const calculateAutoInterval = ( diff , maxBars , esTypes ) => { const exactInterval = diff / maxBars ; if ( diff < maxBars && esTypes . every ( ( opensearchType ) => [ OPENSEARCH_FIELD_TYPES . INTEGER , OPENSEARCH_FIELD_TYPES . LONG , OPENSEARCH_FIELD_TYPES . SHORT , OPENSEARCH_FIELD_TYPES . BYTE , ] . includes ( opensearchType ) ) ) { return 1 ; } const lowerPower = Math . pow ( 10 , Math . floor ( Math . log10 ( exactInterval ) ) ) ; const autoBuckets = diff / lowerPower ; if ( autoBuckets > maxBars ) { if ( autoBuckets / 2 <= maxBars ) { return lowerPower * 2 ; } else if ( autoBuckets / 5 <= maxBars ) { return lowerPower * 5 ; } else { return lowerPower * 10 ; } } return lowerPower ; } ; export const calculateHistogramInterval = ( { interval , maxBucketsUiSettings , maxBucketsUserInput , intervalBase , values , esTypes , } ) => { const isAuto = isAutoInterval ( interval ) ; let calculatedInterval = isAuto ? 0 : parseFloat ( interval ) ; if ( Number . isNaN ( calculatedInterval ) ) { return calculatedInterval ; } if ( values ) { const diff = values . max - values . min ; if ( diff ) { calculatedInterval = isAuto ? calculateAutoInterval ( diff , Math . min ( maxBucketsUiSettings , maxBucketsUserInput || maxBucketsUiSettings ) , esTypes ) : calculateForGivenInterval ( diff , calculatedInterval , maxBucketsUiSettings ) ; } } if ( intervalBase ) { if ( calculatedInterval < intervalBase ) { calculatedInterval = intervalBase ; } else if ( calculatedInterval % intervalBase !== 0 ) { calculatedInterval = Math . round ( calculatedInterval / intervalBase ) * intervalBase ; } } const defaultValueForUnspecifiedInterval = 1 ; return calculatedInterval || defaultValueForUnspecifiedInterval ; } ;