From 5161ab4356b69eb3a5b208ec163b53855361350a Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 12 Jun 2017 13:21:52 -0600 Subject: [PATCH 1/5] create range filter for brush event when xaxis field is not index pattern timefield --- src/ui/public/utils/__tests__/brush_event.js | 100 +++++++++++++------ src/ui/public/utils/brush_event.js | 14 ++- 2 files changed, 78 insertions(+), 36 deletions(-) diff --git a/src/ui/public/utils/__tests__/brush_event.js b/src/ui/public/utils/__tests__/brush_event.js index d9992b4786367..dcd9f29c3c38b 100644 --- a/src/ui/public/utils/__tests__/brush_event.js +++ b/src/ui/public/utils/__tests__/brush_event.js @@ -6,6 +6,8 @@ import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logsta import { UtilsBrushEventProvider } from 'ui/utils/brush_event'; describe('brushEvent', function () { + const DAY_IN_MS = 24 * 60 * 60 * 1000; + const JAN_01_2014 = 1388559600000; let brushEventFn; let timefilter; @@ -51,42 +53,74 @@ describe('brushEvent', function () { .not.have.property('$newFilters'); }); - describe('handles an event when the x-axis field is a date', function () { - let dateEvent; - const dateField = { - name: 'dateField', - type: 'date' - }; - - beforeEach(ngMock.inject(function () { - dateEvent = _.cloneDeep(baseEvent); - dateEvent.data.xAxisField = dateField; - })); + describe('handles an event when the x-axis field is a date field', function () { + describe('date field is index pattern timefield', function () { + let dateEvent; + const dateField = { + name: 'time', + type: 'date' + }; + + beforeEach(ngMock.inject(function () { + dateEvent = _.cloneDeep(baseEvent); + dateEvent.data.xAxisField = dateField; + })); + + it('by ignoring the event when range spans zero time', function () { + const event = _.cloneDeep(dateEvent); + event.range = [JAN_01_2014, JAN_01_2014]; + brushEvent(event); + expect($state) + .not.have.property('$newFilters'); + }); - it('by ignoring the event when range spans zero time', function () { - const event = _.cloneDeep(dateEvent); - event.range = [1388559600000, 1388559600000]; - brushEvent(event); - expect($state) - .not.have.property('$newFilters'); + it('by updating the timefilter', function () { + const event = _.cloneDeep(dateEvent); + event.range = [JAN_01_2014, JAN_01_2014 + DAY_IN_MS]; + brushEvent(event); + expect(timefilter.time.mode).to.be('absolute'); + expect(moment.isMoment(timefilter.time.from)) + .to.be(true); + // Set to a baseline timezone for comparison. + expect(timefilter.time.from.utcOffset(0).format('YYYY-MM-DD')) + .to.equal('2014-01-01'); + expect(moment.isMoment(timefilter.time.to)) + .to.be(true); + // Set to a baseline timezone for comparison. + expect(timefilter.time.to.utcOffset(0).format('YYYY-MM-DD')) + .to.equal('2014-01-02'); + }); }); - it('by updating the timefilter', function () { - const event = _.cloneDeep(dateEvent); - const DAY_IN_MS = 24 * 60 * 60 * 1000; - event.range = [1388559600000, 1388559600000 + DAY_IN_MS]; - brushEvent(event); - expect(timefilter.time.mode).to.be('absolute'); - expect(moment.isMoment(timefilter.time.from)) - .to.be(true); - // Set to a baseline timezone for comparison. - expect(timefilter.time.from.utcOffset(0).format('YYYY-MM-DD')) - .to.equal('2014-01-01'); - expect(moment.isMoment(timefilter.time.to)) - .to.be(true); - // Set to a baseline timezone for comparison. - expect(timefilter.time.to.utcOffset(0).format('YYYY-MM-DD')) - .to.equal('2014-01-02'); + describe('date field is not index pattern timefield', function () { + let dateEvent; + const dateField = { + name: 'anotherTimeField', + type: 'date' + }; + + beforeEach(ngMock.inject(function () { + dateEvent = _.cloneDeep(baseEvent); + dateEvent.data.xAxisField = dateField; + })); + + it('by creating a new filter', function () { + const event = _.cloneDeep(dateEvent); + const rangeBegin = JAN_01_2014; + const rangeEnd = rangeBegin + DAY_IN_MS; + event.range = [rangeBegin, rangeEnd]; + brushEvent(event); + expect($state) + .to.have.property('$newFilters'); + expect($state.filters.length) + .to.equal(0); + expect($state.$newFilters.length) + .to.equal(1); + expect($state.$newFilters[0].range.anotherTimeField.gte) + .to.equal(rangeBegin); + expect($state.$newFilters[0].range.anotherTimeField.lt) + .to.equal(rangeEnd); + }); }); }); diff --git a/src/ui/public/utils/brush_event.js b/src/ui/public/utils/brush_event.js index 630fbe83c9228..e08f8155662ee 100644 --- a/src/ui/public/utils/brush_event.js +++ b/src/ui/public/utils/brush_event.js @@ -3,14 +3,22 @@ import moment from 'moment'; import { buildRangeFilter } from 'ui/filter_manager/lib/range'; export function UtilsBrushEventProvider(timefilter) { + const RANGE_FILTER = 'range'; + const TIME_FILTER = 'timefilter'; return $state => { return event => { if (!event.data.xAxisField) { return; } - switch (event.data.xAxisField.type) { - case 'date': + let filterType = RANGE_FILTER; + if (event.data.xAxisField.type === 'date' && + event.data.xAxisField.name === event.data.indexPattern.timeFieldName) { + filterType = TIME_FILTER; + } + + switch (filterType) { + case TIME_FILTER: const from = moment(event.range[0]); const to = moment(event.range[1]); @@ -21,7 +29,7 @@ export function UtilsBrushEventProvider(timefilter) { timefilter.time.mode = 'absolute'; break; - case 'number': + case RANGE_FILTER: if (event.range.length <= 1) return; const existingFilter = $state.filters.find(filter => ( From 6f96bb3f70b598e5d6a6b059e01e29541db54b25 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 13 Jun 2017 14:07:50 -0600 Subject: [PATCH 2/5] remove switch statement --- src/ui/public/utils/brush_event.js | 75 +++++++++++++----------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/src/ui/public/utils/brush_event.js b/src/ui/public/utils/brush_event.js index e08f8155662ee..85af1a11b362c 100644 --- a/src/ui/public/utils/brush_event.js +++ b/src/ui/public/utils/brush_event.js @@ -3,57 +3,46 @@ import moment from 'moment'; import { buildRangeFilter } from 'ui/filter_manager/lib/range'; export function UtilsBrushEventProvider(timefilter) { - const RANGE_FILTER = 'range'; - const TIME_FILTER = 'timefilter'; return $state => { return event => { if (!event.data.xAxisField) { return; } - let filterType = RANGE_FILTER; if (event.data.xAxisField.type === 'date' && event.data.xAxisField.name === event.data.indexPattern.timeFieldName) { - filterType = TIME_FILTER; - } - - switch (filterType) { - case TIME_FILTER: - const from = moment(event.range[0]); - const to = moment(event.range[1]); - - if (to - from === 0) return; - - timefilter.time.from = from; - timefilter.time.to = to; - timefilter.time.mode = 'absolute'; - break; - - case RANGE_FILTER: - if (event.range.length <= 1) return; - - const existingFilter = $state.filters.find(filter => ( - filter.meta && filter.meta.key === event.data.xAxisField.name - )); - - const min = event.range[0]; - const max = event.range[event.range.length - 1]; - const range = { gte: min, lt: max }; - if (_.has(existingFilter, 'range')) { - existingFilter.range[event.data.xAxisField.name] = range; - } else if (_.has(existingFilter, 'script.script.params.gte') - && _.has(existingFilter, 'script.script.params.lt')) { - existingFilter.script.script.params.gte = min; - existingFilter.script.script.params.lt = max; - } else { - const newFilter = buildRangeFilter( - event.data.xAxisField, - range, - event.data.indexPattern, - event.data.xAxisFormatter); - $state.$newFilters = [newFilter]; - } - break; + const from = moment(event.range[0]); + const to = moment(event.range[1]); + + if (to - from === 0) return; + + timefilter.time.from = from; + timefilter.time.to = to; + timefilter.time.mode = 'absolute'; + } else if (event.data.xAxisField.type === 'date' || event.data.xAxisField.type === 'number') { + if (event.range.length <= 1) return; + + const existingFilter = $state.filters.find(filter => ( + filter.meta && filter.meta.key === event.data.xAxisField.name + )); + + const min = event.range[0]; + const max = event.range[event.range.length - 1]; + const range = { gte: min, lt: max }; + if (_.has(existingFilter, 'range')) { + existingFilter.range[event.data.xAxisField.name] = range; + } else if (_.has(existingFilter, 'script.script.params.gte') + && _.has(existingFilter, 'script.script.params.lt')) { + existingFilter.script.script.params.gte = min; + existingFilter.script.script.params.lt = max; + } else { + const newFilter = buildRangeFilter( + event.data.xAxisField, + range, + event.data.indexPattern, + event.data.xAxisFormatter); + $state.$newFilters = [newFilter]; + } } }; }; From 90ca50ed0cf60ce32d58853b8c0fe40a6e3a84f8 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 13 Jun 2017 14:59:00 -0600 Subject: [PATCH 3/5] move logic to functions --- src/ui/public/utils/brush_event.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ui/public/utils/brush_event.js b/src/ui/public/utils/brush_event.js index 85af1a11b362c..09691e9812d8d 100644 --- a/src/ui/public/utils/brush_event.js +++ b/src/ui/public/utils/brush_event.js @@ -11,6 +11,12 @@ export function UtilsBrushEventProvider(timefilter) { if (event.data.xAxisField.type === 'date' && event.data.xAxisField.name === event.data.indexPattern.timeFieldName) { + setTimefilter(); + } else if (event.data.xAxisField.type === 'date' || event.data.xAxisField.type === 'number') { + setRange(); + } + + function setTimefilter() { const from = moment(event.range[0]); const to = moment(event.range[1]); @@ -19,7 +25,9 @@ export function UtilsBrushEventProvider(timefilter) { timefilter.time.from = from; timefilter.time.to = to; timefilter.time.mode = 'absolute'; - } else if (event.data.xAxisField.type === 'date' || event.data.xAxisField.type === 'number') { + } + + function setRange() { if (event.range.length <= 1) return; const existingFilter = $state.filters.find(filter => ( From f21f25a4eb21dd5943a73fc647beb2d0cd80cdd7 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 23 Jun 2017 13:20:46 -0600 Subject: [PATCH 4/5] convert Dates to milliseconds --- src/ui/public/utils/__tests__/brush_event.js | 22 +++++++++++++++++++- src/ui/public/utils/brush_event.js | 11 ++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/ui/public/utils/__tests__/brush_event.js b/src/ui/public/utils/__tests__/brush_event.js index dcd9f29c3c38b..6006b23d51617 100644 --- a/src/ui/public/utils/__tests__/brush_event.js +++ b/src/ui/public/utils/__tests__/brush_event.js @@ -104,7 +104,7 @@ describe('brushEvent', function () { dateEvent.data.xAxisField = dateField; })); - it('by creating a new filter', function () { + it('creates a new range filter', function () { const event = _.cloneDeep(dateEvent); const rangeBegin = JAN_01_2014; const rangeEnd = rangeBegin + DAY_IN_MS; @@ -121,6 +121,26 @@ describe('brushEvent', function () { expect($state.$newFilters[0].range.anotherTimeField.lt) .to.equal(rangeEnd); }); + + it('converts Date fields to milliseconds', function () { + const event = _.cloneDeep(dateEvent); + const rangeBeginMs = JAN_01_2014; + const rangeEndMs = rangeBeginMs + DAY_IN_MS; + const rangeBegin = new Date(rangeBeginMs); + const rangeEnd = new Date(rangeEndMs); + event.range = [rangeBegin, rangeEnd]; + brushEvent(event); + expect($state) + .to.have.property('$newFilters'); + expect($state.filters.length) + .to.equal(0); + expect($state.$newFilters.length) + .to.equal(1); + expect($state.$newFilters[0].range.anotherTimeField.gte) + .to.equal(rangeBeginMs); + expect($state.$newFilters[0].range.anotherTimeField.lt) + .to.equal(rangeEndMs); + }); }); }); diff --git a/src/ui/public/utils/brush_event.js b/src/ui/public/utils/brush_event.js index 09691e9812d8d..682c0c83d08f1 100644 --- a/src/ui/public/utils/brush_event.js +++ b/src/ui/public/utils/brush_event.js @@ -34,8 +34,15 @@ export function UtilsBrushEventProvider(timefilter) { filter.meta && filter.meta.key === event.data.xAxisField.name )); - const min = event.range[0]; - const max = event.range[event.range.length - 1]; + let min = event.range[0]; + let max = event.range[event.range.length - 1]; + // Convert Dates to MS to avoid ES "parse date field" errors + if (min instanceof Date) { + min = min.getTime(); + } + if (max instanceof Date) { + max = max.getTime(); + } const range = { gte: min, lt: max }; if (_.has(existingFilter, 'range')) { existingFilter.range[event.data.xAxisField.name] = range; From eb7124c725dfc4733bbdb761c9c06d79d35901cd Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 26 Jun 2017 07:43:47 -0600 Subject: [PATCH 5/5] add format:epoch_millis to date range filter --- src/ui/public/utils/__tests__/brush_event.js | 4 +++ src/ui/public/utils/brush_event.js | 31 +++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/ui/public/utils/__tests__/brush_event.js b/src/ui/public/utils/__tests__/brush_event.js index 6006b23d51617..5bc1fe1564441 100644 --- a/src/ui/public/utils/__tests__/brush_event.js +++ b/src/ui/public/utils/__tests__/brush_event.js @@ -120,6 +120,9 @@ describe('brushEvent', function () { .to.equal(rangeBegin); expect($state.$newFilters[0].range.anotherTimeField.lt) .to.equal(rangeEnd); + expect($state.$newFilters[0].range.anotherTimeField).to.have.property('format'); + expect($state.$newFilters[0].range.anotherTimeField.format) + .to.equal('epoch_millis'); }); it('converts Date fields to milliseconds', function () { @@ -178,6 +181,7 @@ describe('brushEvent', function () { .to.equal(1); expect($state.$newFilters[0].range.numberField.lt) .to.equal(4); + expect($state.$newFilters[0].range.numberField).not.to.have.property('format'); }); it('by updating the existing range filter', function () { diff --git a/src/ui/public/utils/brush_event.js b/src/ui/public/utils/brush_event.js index 682c0c83d08f1..f474461b8d946 100644 --- a/src/ui/public/utils/brush_event.js +++ b/src/ui/public/utils/brush_event.js @@ -9,10 +9,13 @@ export function UtilsBrushEventProvider(timefilter) { return; } - if (event.data.xAxisField.type === 'date' && + const isDate = event.data.xAxisField.type === 'date'; + const isNumber = event.data.xAxisField.type === 'number'; + + if (isDate && event.data.xAxisField.name === event.data.indexPattern.timeFieldName) { setTimefilter(); - } else if (event.data.xAxisField.type === 'date' || event.data.xAxisField.type === 'number') { + } else if (isDate || isNumber) { setRange(); } @@ -34,16 +37,22 @@ export function UtilsBrushEventProvider(timefilter) { filter.meta && filter.meta.key === event.data.xAxisField.name )); - let min = event.range[0]; - let max = event.range[event.range.length - 1]; - // Convert Dates to MS to avoid ES "parse date field" errors - if (min instanceof Date) { - min = min.getTime(); - } - if (max instanceof Date) { - max = max.getTime(); + const min = event.range[0]; + const max = event.range[event.range.length - 1]; + let range; + if (isDate) { + range = { + gte: moment(min).valueOf(), + lt: moment(max).valueOf(), + format: 'epoch_millis' + }; + } else { + range = { + gte: min, + lt: max + }; } - const range = { gte: min, lt: max }; + if (_.has(existingFilter, 'range')) { existingFilter.range[event.data.xAxisField.name] = range; } else if (_.has(existingFilter, 'script.script.params.gte')