mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Summary of Changes:
- rounded intervals now come with a "description" property that gives a simple human-readable description of the interval. (ie. "5 hours", "10 sec") - auto and scaled buckets now display the interval for the measurements - expanded the parsing abilities of `toMs()` - renamed `toMS()` to `toMs()` - histogram will now use the field's converter for creating the tooltip. - date field format added
This commit is contained in:
parent
60f88dae8b
commit
dc19e13227
6 changed files with 150 additions and 100 deletions
|
@ -9,7 +9,7 @@ define(function (require) {
|
|||
|
||||
var pickInterval = function (bounds) {
|
||||
bounds || (bounds = timefilter.getBounds());
|
||||
return interval.calculate(bounds.min, bounds.max, 100).interval;
|
||||
return interval.calculate(bounds.min, bounds.max, 100);
|
||||
};
|
||||
|
||||
var agg = this;
|
||||
|
@ -67,9 +67,12 @@ define(function (require) {
|
|||
|
||||
write: function (selection, output) {
|
||||
var bounds = timefilter.getBounds();
|
||||
var auto;
|
||||
|
||||
if (selection.val === 'auto') {
|
||||
output.aggParams.interval = pickInterval(bounds) + 'ms';
|
||||
auto = pickInterval(bounds);
|
||||
output.aggParams.interval = auto.interval + 'ms';
|
||||
output.metricScaleText = auto.description;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,9 +80,10 @@ define(function (require) {
|
|||
var buckets = Math.ceil((bounds.max - bounds.min) / ms);
|
||||
if (buckets > 150) {
|
||||
// we should round these buckets out, and scale back the y values
|
||||
var msPerBucket = pickInterval(bounds);
|
||||
output.aggParams.interval = msPerBucket + 'ms';
|
||||
output.metricScale = ms / msPerBucket;
|
||||
auto = pickInterval(bounds);
|
||||
output.aggParams.interval = auto.interval + 'ms';
|
||||
output.metricScale = ms / auto.interval;
|
||||
output.metricScaleText = selection.val || auto.description;
|
||||
} else {
|
||||
output.aggParams.interval = selection.val;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ define(function (require) {
|
|||
chart.ordered = {
|
||||
date: true,
|
||||
min: timeBounds.min.valueOf(),
|
||||
max: timeBounds.max.valueOf()
|
||||
max: timeBounds.max.valueOf(),
|
||||
interval: interval.toMs(colX.aggParams.interval)
|
||||
};
|
||||
} else {
|
||||
chart.ordered = {};
|
||||
|
@ -56,29 +57,43 @@ define(function (require) {
|
|||
|
||||
// X-axis description
|
||||
chart.xAxisLabel = colX.label;
|
||||
if (colX.field) chart.xAxisFormatter = colX.field.format.convert;
|
||||
if (aggX.name === 'date_histogram') {
|
||||
chart.xAxisFormatter = function (thing) {
|
||||
chart.xAxisFormatter = (function () {
|
||||
var bounds = timefilter.getBounds();
|
||||
return moment(thing).format(interval.calculate(
|
||||
var format = interval.calculate(
|
||||
moment(bounds.min.valueOf()),
|
||||
moment(bounds.max.valueOf()),
|
||||
rows.length)
|
||||
.format);
|
||||
};
|
||||
}
|
||||
rows.length
|
||||
).format;
|
||||
|
||||
chart.tooltipFormatter = function (datapoint) {
|
||||
if (aggX.name === 'date_histogram') {
|
||||
return moment(datapoint.x).format();
|
||||
}
|
||||
return datapoint.x;
|
||||
};
|
||||
return function (thing) {
|
||||
return moment(thing).format(format);
|
||||
};
|
||||
}());
|
||||
}
|
||||
else if (colX.field) chart.xAxisFormatter = colX.field.format.convert;
|
||||
|
||||
// Y-axis description
|
||||
chart.yAxisLabel = colY.label;
|
||||
if (colY.field) chart.yAxisFormatter = colY.field.format.convert;
|
||||
|
||||
|
||||
|
||||
// setup the formatter for the label
|
||||
chart.tooltipFormatter = function (datapoint) {
|
||||
var x = datapoint.x;
|
||||
var y = datapoint.y;
|
||||
|
||||
if (colX.field) x = colX.field.format.convert(x);
|
||||
if (colY.field) y = colY.field.format.convert(y);
|
||||
|
||||
if (colX.metricScaleText) {
|
||||
y += ' per ' + colX.metricScaleText;
|
||||
}
|
||||
|
||||
return x + ': ' + y;
|
||||
};
|
||||
|
||||
var series = chart.series = [];
|
||||
var seriesByLabel = {};
|
||||
|
||||
|
|
|
@ -31,13 +31,14 @@ Currently, the [histogram formatter](https://github.com/elasticsearch/kibana4/bl
|
|||
define(function (require) {
|
||||
return function FieldFormattingService() {
|
||||
var _ = require('lodash');
|
||||
var moment = require('moment');
|
||||
|
||||
var formats = [
|
||||
{
|
||||
types: [
|
||||
'number',
|
||||
'date',
|
||||
'boolean',
|
||||
'date',
|
||||
'ip',
|
||||
'attachment',
|
||||
'geo_point',
|
||||
|
@ -53,6 +54,15 @@ define(function (require) {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
types: [
|
||||
'date'
|
||||
],
|
||||
name: 'date',
|
||||
convert: function (val) {
|
||||
return moment(val).format();
|
||||
}
|
||||
},
|
||||
{
|
||||
types: [
|
||||
'number'
|
||||
|
@ -75,7 +85,7 @@ define(function (require) {
|
|||
|
||||
formats.defaultByType = {
|
||||
number: formats.byName.string,
|
||||
date: formats.byName.string,
|
||||
date: formats.byName.date,
|
||||
boolean: formats.byName.string,
|
||||
ip: formats.byName.string,
|
||||
attachment: formats.byName.string,
|
||||
|
|
|
@ -19,66 +19,87 @@ define(function (require) {
|
|||
from = datemath.parse(from).valueOf();
|
||||
to = datemath.parse(to, true).valueOf();
|
||||
rawInterval = ((to - from) / target);
|
||||
return round ? roundInterval(rawInterval) : rawInterval;
|
||||
var rounded = roundInterval(rawInterval);
|
||||
if (!round) rounded.interval = rawInterval;
|
||||
return rounded;
|
||||
};
|
||||
|
||||
// interval should be passed in ms
|
||||
// these are the rounding rules used by roundInterval()
|
||||
var roundingRules = [
|
||||
// bound, interval/desc, format
|
||||
['500ms', '100 ms', 'hh:mm:ss.SSS'],
|
||||
['5s', 'second', 'HH:mm:ss'],
|
||||
['7.5s', '5 sec', 'HH:mm:ss'],
|
||||
['15s', '10 sec', 'HH:mm:ss'],
|
||||
['45s', '30 sec', 'HH:mm:ss'],
|
||||
['3m', 'minute', 'HH:mm'],
|
||||
['9m', '5 min', 'HH:mm'],
|
||||
['20m', '10 min', 'HH:mm'],
|
||||
['45m', '30 min', 'YYYY-MM-DD HH:mm'],
|
||||
['2h', 'hour', 'YYYY-MM-DD HH:mm'],
|
||||
['6h', '3 hours', 'YYYY-MM-DD HH:mm'],
|
||||
['24h', '12 hours', 'YYYY-MM-DD HH:mm'],
|
||||
['1w', '1 day', 'YYYY-MM-DD'],
|
||||
['3w', '1 week', 'YYYY-MM-DD'],
|
||||
['1y', '1 month', 'YYYY-MM'],
|
||||
[null, '1 year', 'YYYY'] // default
|
||||
];
|
||||
var boundCache = {};
|
||||
|
||||
/**
|
||||
* Round a millisecond interval to the closest "clean" interval,
|
||||
*
|
||||
* @param {ms} interval - interval in milliseconds
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
var roundInterval = function (interval) {
|
||||
switch (true) {
|
||||
case (interval <= toMS('500ms')):
|
||||
return {interval: toMS('100ms'), format: 'hh:mm:ss.SSS'};
|
||||
case (interval <= toMS('5s')):
|
||||
return {interval: toMS('1s'), format: 'HH:mm:ss'};
|
||||
case (interval <= toMS('7.5s')):
|
||||
return {interval: toMS('5s'), format: 'HH:mm:ss'};
|
||||
case (interval <= toMS('15s')):
|
||||
return {interval: toMS('10s'), format: 'HH:mm:ss'};
|
||||
case (interval <= toMS('45s')):
|
||||
return {interval: toMS('30s'), format: 'HH:mm:ss'};
|
||||
case (interval <= toMS('3m')):
|
||||
return {interval: toMS('1m'), format: 'HH:mm'};
|
||||
case (interval <= toMS('9m')):
|
||||
return {interval: toMS('5m'), format: 'HH:mm'};
|
||||
case (interval <= toMS('20m')):
|
||||
return {interval: toMS('10m'), format: 'HH:mm'};
|
||||
case (interval <= toMS('45m')):
|
||||
return {interval: toMS('30m'), format: 'YYYY-MM-DD HH:mm'};
|
||||
case (interval <= toMS('2h')):
|
||||
return {interval: toMS('1h'), format: 'YYYY-MM-DD HH:mm'};
|
||||
case (interval <= toMS('6h')):
|
||||
return {interval: toMS('3h'), format: 'YYYY-MM-DD HH:mm'};
|
||||
case (interval <= toMS('24h')):
|
||||
return {interval: toMS('12h'), format: 'YYYY-MM-DD HH:mm'};
|
||||
case (interval <= toMS('1w')):
|
||||
return {interval: toMS('1d'), format: 'YYYY-MM-DD'};
|
||||
case (interval <= toMS('3w')):
|
||||
return {interval: toMS('1w'), format: 'YYYY-MM-DD'};
|
||||
case (interval < toMS('1y')):
|
||||
return {interval: toMS('1M'), format: 'YYYY-MM'};
|
||||
default:
|
||||
return {interval: toMS('1y'), format: 'YYYY'};
|
||||
}
|
||||
var rule = _.find(roundingRules, function (rule, i, rules) {
|
||||
var remaining = rules.length - i - 1;
|
||||
// no bound? then succeed
|
||||
if (!rule[0]) return true;
|
||||
|
||||
var bound = boundCache[rule[0]] || (boundCache[rule[0]] = toMs(rule[0]));
|
||||
// check that we are below or equal to the bounds
|
||||
if (remaining > 1 && interval <= bound) return true;
|
||||
// the last rule before the default shouldn't include the default (which is the bound)
|
||||
if (remaining === 1 && interval < bound) return true;
|
||||
});
|
||||
return {
|
||||
description: rule[1],
|
||||
interval: toMs(rule[1]),
|
||||
format: rule[2]
|
||||
};
|
||||
};
|
||||
|
||||
var toMS = function (interval) {
|
||||
var _p = interval.match(/([0-9.]+)([a-zA-Z]+)/);
|
||||
if (_p.length !== 3) return undefined;
|
||||
return moment.duration(parseFloat(_p[1]), shorthand[_p[2]]).valueOf();
|
||||
};
|
||||
// map of moment's short/long unit ids and elasticsearch's long unit ids
|
||||
// to their value in milliseconds
|
||||
var vals = _.transform([
|
||||
['ms', 'milliseconds', 'millisecond'],
|
||||
['s', 'seconds', 'second', 'sec'],
|
||||
['m', 'minutes', 'minute', 'min'],
|
||||
['h', 'hours', 'hour'],
|
||||
['d', 'days', 'day'],
|
||||
['w', 'weeks', 'week'],
|
||||
['M', 'months', 'month'],
|
||||
['quarter'],
|
||||
['y', 'years', 'year']
|
||||
], function (vals, units) {
|
||||
var normal = moment.normalizeUnits(units[0]);
|
||||
var val = moment.duration(1, normal).asMilliseconds();
|
||||
[].concat(normal, units).forEach(function (unit) {
|
||||
vals[unit] = val;
|
||||
});
|
||||
}, {});
|
||||
// match any key from the vals object prececed by an optional number
|
||||
var parseRE = new RegExp('^(\\d+(?:\\.\\d*)?)?\\s*(' + _.keys(vals).join('|') + ')$');
|
||||
|
||||
var shorthand = {
|
||||
ms: 'milliseconds',
|
||||
s: 'seconds',
|
||||
m: 'minutes',
|
||||
h: 'hours',
|
||||
d: 'days',
|
||||
w: 'weeks',
|
||||
M: 'months',
|
||||
y: 'years',
|
||||
var toMs = function (expr) {
|
||||
var match = expr.match(parseRE);
|
||||
if (match) return parseFloat(match[1] || 1) * vals[match[2]];
|
||||
};
|
||||
|
||||
return {
|
||||
toMS: toMS,
|
||||
toMs: toMs,
|
||||
calculate: calculate
|
||||
};
|
||||
});
|
|
@ -5,7 +5,8 @@ module.exports = function (config) {
|
|||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'<%= src %>/**/*.js',
|
||||
'<%= src %>/*.js',
|
||||
'<%= src %>/kibana/**/*.js',
|
||||
'<%= unitTestDir %>/**/*.js',
|
||||
'<%= root %>/tasks/**/*.js'
|
||||
]
|
||||
|
@ -16,8 +17,7 @@ module.exports = function (config) {
|
|||
ignores: [
|
||||
'node_modules/*',
|
||||
'dist/*',
|
||||
'sample/*',
|
||||
'<%= root %>/src/bower_components/**/*'
|
||||
'sample/*'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,16 +6,16 @@ define(function (require) {
|
|||
|
||||
describe('interval', function () {
|
||||
|
||||
describe('toMS', function () {
|
||||
describe('toMs', function () {
|
||||
it('return the number of milliseconds represented by the string', function () {
|
||||
expect(interval.toMS('1ms')).to.be(1);
|
||||
expect(interval.toMS('1s')).to.be(1000);
|
||||
expect(interval.toMS('1m')).to.be(60000);
|
||||
expect(interval.toMS('1h')).to.be(3600000);
|
||||
expect(interval.toMS('1d')).to.be(86400000);
|
||||
expect(interval.toMS('1w')).to.be(604800000);
|
||||
expect(interval.toMS('1M')).to.be(2592000000); // actually 30d
|
||||
expect(interval.toMS('1y')).to.be(31536000000); // 1000*60*60*24*365
|
||||
expect(interval.toMs('1ms')).to.be(1);
|
||||
expect(interval.toMs('1s')).to.be(1000);
|
||||
expect(interval.toMs('1m')).to.be(60000);
|
||||
expect(interval.toMs('1h')).to.be(3600000);
|
||||
expect(interval.toMs('1d')).to.be(86400000);
|
||||
expect(interval.toMs('1w')).to.be(604800000);
|
||||
expect(interval.toMs('1M')).to.be(2592000000); // actually 30d
|
||||
expect(interval.toMs('1y')).to.be(31536000000); // 1000*60*60*24*365
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -34,83 +34,83 @@ define(function (require) {
|
|||
|
||||
it('should calculate an appropriate interval for 10s', function () {
|
||||
var _t = then.subtract(10, 'seconds');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('100ms'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('100ms'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 1m', function () {
|
||||
var _t = then.subtract(1, 'minute');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1s'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1s'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 10m', function () {
|
||||
var _t = then.subtract(10, 'minutes');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('5s'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('5s'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 15m', function () {
|
||||
var _t = then.subtract(15, 'minutes');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('10s'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('10s'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 1h', function () {
|
||||
var _t = then.subtract(1, 'hour');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('30s'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('30s'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 90m', function () {
|
||||
var _t = then.subtract(90, 'minutes');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1m'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1m'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 6h', function () {
|
||||
var _t = then.subtract(6, 'hours');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('5m'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('5m'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 24h', function () {
|
||||
var _t = then.subtract(24, 'hours');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('10m'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('10m'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 3d', function () {
|
||||
var _t = then.subtract(3, 'days');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('30m'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('30m'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 1w', function () {
|
||||
var _t = then.subtract(1, 'week');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1h'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1h'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 2w', function () {
|
||||
var _t = then.subtract(2, 'weeks');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('3h'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('3h'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 1M', function () {
|
||||
var _t = then.subtract(1, 'month');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('12h'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('12h'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 4M', function () {
|
||||
var _t = then.subtract(4, 'months');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1d'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1d'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 2y', function () {
|
||||
var _t = then.subtract(2, 'years');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1w'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1w'));
|
||||
});
|
||||
|
||||
it('should calculate an appropriate interval for 25y', function () {
|
||||
var _t = then.subtract(25, 'years');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1M'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1M'));
|
||||
});
|
||||
|
||||
|
||||
it('should calculate an appropriate interval for a 100y', function () {
|
||||
var _t = then.subtract(100, 'years');
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMS('1y'));
|
||||
expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1y'));
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue