diff --git a/x-pack/plugins/ml/public/util/__tests__/ml_time_buckets.js b/x-pack/plugins/ml/public/util/__tests__/ml_time_buckets.js index 73c84de57eae..57069994ef91 100644 --- a/x-pack/plugins/ml/public/util/__tests__/ml_time_buckets.js +++ b/x-pack/plugins/ml/public/util/__tests__/ml_time_buckets.js @@ -9,7 +9,10 @@ import ngMock from 'ng_mock'; import expect from 'expect.js'; import moment from 'moment'; -import { IntervalHelperProvider, getBoundsRoundedToInterval } from '../ml_time_buckets'; +import { + IntervalHelperProvider, + getBoundsRoundedToInterval, + calcEsInterval } from '../ml_time_buckets'; describe('ML - time buckets', () => { @@ -125,7 +128,7 @@ describe('ML - time buckets', () => { }); - describe('getBoundsRoundedToInterval ', () => { + describe('getBoundsRoundedToInterval', () => { // Must include timezone when creating moments for this test to ensure // checks are correct when running tests in different timezones. const testBounds = { min: moment('2017-01-05T10:11:12.000+00:00'), max: moment('2017-10-26T09:08:07.000+00:00') }; @@ -156,4 +159,24 @@ describe('ML - time buckets', () => { }); + describe('calcEsInterval', () => { + it('returns correct interval for various durations', () => { + expect(calcEsInterval(moment.duration(500, 'ms'))).to.eql({ value: 500, unit: 'ms', expression: '500ms' }); + expect(calcEsInterval(moment.duration(1000, 'ms'))).to.eql({ value: 1, unit: 's', expression: '1s' }); + expect(calcEsInterval(moment.duration(15, 's'))).to.eql({ value: 15, unit: 's', expression: '15s' }); + expect(calcEsInterval(moment.duration(60, 's'))).to.eql({ value: 1, unit: 'm', expression: '1m' }); + expect(calcEsInterval(moment.duration(1, 'm'))).to.eql({ value: 1, unit: 'm', expression: '1m' }); + expect(calcEsInterval(moment.duration(60, 'm'))).to.eql({ value: 1, unit: 'h', expression: '1h' }); + expect(calcEsInterval(moment.duration(3, 'h'))).to.eql({ value: 3, unit: 'h', expression: '3h' }); + expect(calcEsInterval(moment.duration(24, 'h'))).to.eql({ value: 1, unit: 'd', expression: '1d' }); + expect(calcEsInterval(moment.duration(3, 'd'))).to.eql({ value: 3, unit: 'd', expression: '3d' }); + expect(calcEsInterval(moment.duration(7, 'd'))).to.eql({ value: 1, unit: 'w', expression: '1w' }); + expect(calcEsInterval(moment.duration(1, 'w'))).to.eql({ value: 1, unit: 'w', expression: '1w' }); + expect(calcEsInterval(moment.duration(4, 'w'))).to.eql({ value: 28, unit: 'd', expression: '28d' }); + expect(calcEsInterval(moment.duration(1, 'M'))).to.eql({ value: 1, unit: 'M', expression: '1M' }); + expect(calcEsInterval(moment.duration(12, 'M'))).to.eql({ value: 1, unit: 'y', expression: '1y' }); + expect(calcEsInterval(moment.duration(1, 'y'))).to.eql({ value: 1, unit: 'y', expression: '1y' }); + }); + }); + }); diff --git a/x-pack/plugins/ml/public/util/ml_time_buckets.js b/x-pack/plugins/ml/public/util/ml_time_buckets.js index 5db39e022e95..b334962a2417 100644 --- a/x-pack/plugins/ml/public/util/ml_time_buckets.js +++ b/x-pack/plugins/ml/public/util/ml_time_buckets.js @@ -6,17 +6,20 @@ -// custom TimeBuckets which inherits from the standrd kibana TimeBuckets +// custom TimeBuckets which inherits from the standard kibana TimeBuckets // this adds the ability to override the barTarget and maxBars settings // allowing for a more granular visualization interval without having to // modify the global settings stored in the kibana config import _ from 'lodash'; import moment from 'moment'; +import dateMath from '@kbn/datemath'; import { TimeBucketsCalcAutoIntervalProvider } from 'plugins/ml/util/ml_calc_auto_interval'; import { inherits } from 'plugins/ml/util/inherits'; -import { calcEsInterval } from 'ui/time_buckets/calc_es_interval'; + +const unitsDesc = dateMath.unitsDesc; +const largeMax = unitsDesc.indexOf('w'); // Multiple units of week or longer converted to days for ES intervals. import { TimeBuckets } from 'ui/time_buckets'; export function IntervalHelperProvider(Private, timefilter, config) { @@ -147,3 +150,39 @@ export function getBoundsRoundedToInterval(bounds, interval, inclusiveEnd = fals } return { min: moment(adjustedMinMs), max: moment(adjustedMaxMs) }; } + +export function calcEsInterval(duration) { + // Converts a moment.duration into an Elasticsearch compatible interval expression, + // and provides associated metadata. + + // Note this is a copy of Kibana's ui/time_buckets/calc_es_interval, + // but with the definition of a 'large' unit changed from 'M' to 'w', + // bringing it into line with the time units supported by Elasticsearch + for (let i = 0; i < unitsDesc.length; i++) { + const unit = unitsDesc[i]; + const val = duration.as(unit); + // find a unit that rounds neatly + if (val >= 1 && Math.floor(val) === val) { + + // if the unit is "large", like years, but isn't set to 1, ES will throw an error. + // So keep going until we get out of the "large" units. + if (i <= largeMax && val !== 1) { + continue; + } + + return { + value: val, + unit: unit, + expression: val + unit + }; + } + } + + const ms = duration.as('ms'); + return { + value: ms, + unit: 'ms', + expression: ms + 'ms' + }; +} +