diff --git a/src/kibana/components/courier/data_source/_abstract.js b/src/kibana/components/courier/data_source/_abstract.js index 253fa2e7f943b..148d714e7bee6 100644 --- a/src/kibana/components/courier/data_source/_abstract.js +++ b/src/kibana/components/courier/data_source/_abstract.js @@ -266,6 +266,14 @@ define(function (require) { } flatState.body.fields = ['*', '_source']; + _.each(flatState.index.fields.byType['date'], function (field) { + if (field.indexed) { + flatState.body.script_fields = flatState.body.script_fields || {}; + flatState.body.script_fields[field.name] = { + script: 'doc["' + field.name + '"].value' + }; + } + }); /** * Create a filter that can be reversed for filters with negate set diff --git a/src/kibana/components/index_patterns/_field_formats.js b/src/kibana/components/index_patterns/_field_formats.js index f9bdbdc3c7967..a78d6e6f9f20a 100644 --- a/src/kibana/components/index_patterns/_field_formats.js +++ b/src/kibana/components/index_patterns/_field_formats.js @@ -48,15 +48,17 @@ define(function (require) { ], name: 'string', convert: function (val) { - if (_.isObject(val)) { - return JSON.stringify(val); - } - else if (val == null) { - return ''; - } - else { - return '' + val; - } + return formatField(val, function (val) { + if (_.isObject(val)) { + return JSON.stringify(val); + } + else if (val == null) { + return ''; + } + else { + return '' + val; + } + }); } }, { @@ -65,7 +67,13 @@ define(function (require) { ], name: 'date', convert: function (val) { - return moment(val).format(config.get('dateFormat')); + return formatField(val, function (val) { + if (_.isNumber(val) || _.isDate(val)) { + return moment(val).format(config.get('dateFormat')); + } else { + return val; + } + }); } }, { @@ -73,9 +81,11 @@ define(function (require) { 'ip' ], name: 'ip', - convert: function (ip) { - if (!isFinite(ip)) return ip; - return [ip >>> 24, ip >>> 16 & 0xFF, ip >>> 8 & 0xFF, ip & 0xFF].join('.'); + convert: function (val) { + return formatField(val, function (val) { + if (!isFinite(val)) return val; + return [val >>> 24, val >>> 16 & 0xFF, val >>> 8 & 0xFF, val & 0xFF].join('.'); + }); } }, { @@ -84,11 +94,25 @@ define(function (require) { ], name: 'kilobytes', convert: function (val) { - return (val / 1024).toFixed(3) + ' kb'; + return formatField(val, function (val) { + return (val / 1024).toFixed(3) + ' kb'; + }); } } ]; + function formatField(value, fn) { + if (_.isArray(value)) { + if (value.length === 1) { + return fn(value[0]); + } else { + return JSON.stringify(_.map(value, fn)); + } + } else { + return fn(value); + } + } + formats.byType = _.transform(formats, function (byType, formatter) { formatter.types.forEach(function (type) { var list = byType[type] || (byType[type] = []); diff --git a/test/unit/specs/components/index_pattern/_field_formats.js b/test/unit/specs/components/index_pattern/_field_formats.js new file mode 100644 index 0000000000000..b9fd3063c9be9 --- /dev/null +++ b/test/unit/specs/components/index_pattern/_field_formats.js @@ -0,0 +1,150 @@ +define(function (require) { + return ['Field Formatters', function () { + var _ = require('lodash'); + var moment = require('moment'); + + var _config; + var formatters; + var formatter; + var types = [ + 'number', + 'boolean', + 'date', + 'ip', + 'attachment', + 'geo_point', + 'geo_shape', + 'string', + 'conflict' + ]; + + function formatFn(typeOrName) { + return (formatters.byName[typeOrName] || formatters.defaultByType[typeOrName]).convert; + } + + beforeEach(module('kibana')); + beforeEach(inject(function (Private, $injector, config) { + _config = config; + formatters = Private(require('components/index_patterns/_field_formats')); + })); + + it('should be an Object', function () { + expect(formatters).to.be.an(Object); + }); + + it('should have formatters indexed by type and by name', function () { + expect(formatters.byType).to.be.an(Object); + expect(formatters.byName).to.be.an(Object); + }); + + it('should have 1 or more formatters for each of ' + types.join(','), function () { + _.each(types, function (type) { + expect(formatters.byType[type]).to.be.an(Array); + _.each(formatters.byType[type], function (formatter) { + expect(formatter.convert).to.be.a(Function); + }); + }); + }); + + it('should expose default formatters for each type', function () { + _.each(types, function (type) { + expect(formatters.defaultByType[type]).to.be.an(Object); + }); + }); + + describe('Array handling', function () { + + it('should unwrap single item arrays', function () { + formatter = formatFn('string'); + expect(formatter(['foo'])).to.not.be.an(Array); + expect(formatter(['foo'])).to.be('foo'); + }); + + it('should stringify arrays longer than 1 element', function () { + formatter = formatFn('ip'); + expect(formatter([0, 2130706433])).to.not.be.an(Array); + expect(formatter([0, 2130706433])).to.be('["0.0.0.0","127.0.0.1"]'); + }); + }); + + + describe('string formatter', function () { + + beforeEach(function () { + formatter = formatFn('string'); + }); + + it('should the string value of the field', function () { + expect(formatter('foo')).to.be('foo'); + expect(formatter(5)).to.be('5'); + }); + + it('should return JSON for objects', function () { + expect(formatter({foo: true})).to.be('{"foo":true}'); + }); + + it('should return an empty string for null', function () { + expect(formatter(null)).to.be(''); + }); + + }); + + describe('date formatter', function () { + + var dateFormat = 'YYYY-MM-DD'; + beforeEach(function () { + _config.set('dateFormat', dateFormat); + formatter = formatFn('date'); + }); + + it('should format numbers', function () { + expect(formatter(0)).to.be(moment(0).format(dateFormat)); + }); + + it('should format dates', function () { + expect(formatter(new Date(0))).to.be(moment(0).format(dateFormat)); + }); + + it('should not format strings', function () { + expect(formatter('2014-11')).to.be('2014-11'); + }); + + }); + + + describe('ip formatter', function () { + + beforeEach(function () { + formatter = formatFn('ip'); + }); + + it('should format numbers', function () { + expect(formatter(2130706433)).to.be('127.0.0.1'); + }); + + it('should coerce numbers that are strings', function () { + expect(formatter('2130706433')).to.be('127.0.0.1'); + }); + + it('should not coerce strings that are not numbers', function () { + expect(formatter('foo')).to.be('foo'); + }); + + }); + + describe('kilobyte formatter', function () { + beforeEach(function () { + formatter = formatFn('kilobytes'); + }); + + it('should be a function', function () { + expect(formatter).to.be.a(Function); + }); + + it('should format a number as kilobytes', function () { + expect(formatter(1024)).to.be('1.000 kb'); + }); + }); + + }]; +}); \ No newline at end of file diff --git a/test/unit/specs/components/index_pattern/index.js b/test/unit/specs/components/index_pattern/index.js index df54955f18be2..59fcc306cabe1 100644 --- a/test/unit/specs/components/index_pattern/index.js +++ b/test/unit/specs/components/index_pattern/index.js @@ -3,6 +3,7 @@ define(function (require) { run(require('specs/components/index_pattern/_cast_mapping_type')); run(require('specs/components/index_pattern/_map_field')); run(require('specs/components/index_pattern/_pattern_to_wildcard')); + run(require('specs/components/index_pattern/_field_formats')); function run(mod) { describe(mod[0], mod[1]); } }); }); \ No newline at end of file