diff --git a/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js b/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js index 57db9b330962d1..a4384ae2a1fea9 100644 --- a/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js +++ b/src/core_plugins/kbn_vislib_vis_types/public/editors/__tests__/point_series.js @@ -87,7 +87,7 @@ describe('point series editor', function () { }); it('should update series when new agg is added', function () { - const aggConfig = new AggConfig($parentScope.vis, { type: 'avg', schema: 'metric', params: { field: 'bytes' } }); + const aggConfig = new AggConfig($parentScope.vis.aggs, { type: 'avg', schema: 'metric', params: { field: 'bytes' } }); $parentScope.vis.aggs.push(aggConfig); $parentScope.$digest(); expect($parentScope.editorState.params.seriesParams.length).to.be(2); diff --git a/src/core_plugins/kibana/public/discover/controllers/discover.js b/src/core_plugins/kibana/public/discover/controllers/discover.js index b2b1ef1bbe152e..fee5c13ff5304a 100644 --- a/src/core_plugins/kibana/public/discover/controllers/discover.js +++ b/src/core_plugins/kibana/public/discover/controllers/discover.js @@ -768,7 +768,8 @@ function discoverController( schema: 'segment', params: { field: $scope.opts.timefield, - interval: $state.interval + interval: $state.interval, + timeRange: timefilter.getTime(), } } ]; diff --git a/src/core_plugins/tile_map/public/coordinate_maps_visualization.js b/src/core_plugins/tile_map/public/coordinate_maps_visualization.js index 2cb2da826e83e5..7018db87c89d22 100644 --- a/src/core_plugins/tile_map/public/coordinate_maps_visualization.js +++ b/src/core_plugins/tile_map/public/coordinate_maps_visualization.js @@ -186,7 +186,7 @@ export function CoordinateMapsVisualizationProvider(Notifier, Private) { return; } - const indexPatternName = agg._indexPattern.id; + const indexPatternName = agg.getIndexPattern().id; const field = agg.fieldName(); const filter = { meta: { negate: false, index: indexPatternName } }; filter[filterName] = { ignore_unmapped: true }; diff --git a/src/core_plugins/tile_map/public/coordinatemap_response_handler.js b/src/core_plugins/tile_map/public/coordinatemap_response_handler.js index f742a651ed6a80..e1bc9b9592107c 100644 --- a/src/core_plugins/tile_map/public/coordinatemap_response_handler.js +++ b/src/core_plugins/tile_map/public/coordinatemap_response_handler.js @@ -34,7 +34,7 @@ export function makeGeoJsonResponseHandler() { //double conversion, first to table, then to geojson //This is to future-proof this code for Canvas-refactoring - const tabifiedResponse = tabifyAggResponse(vis.getAggConfig(), esResponse); + const tabifiedResponse = tabifyAggResponse(vis.getAggConfig(), esResponse, { partialRows: true }); lastGeoJsonResponse = convertToGeoJson(tabifiedResponse); return lastGeoJsonResponse; diff --git a/src/ui/public/agg_response/point_series/__tests__/_add_to_siri.js b/src/ui/public/agg_response/point_series/__tests__/_add_to_siri.js index b7e97639d05254..d7fbb223a2210f 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_add_to_siri.js +++ b/src/ui/public/agg_response/point_series/__tests__/_add_to_siri.js @@ -18,16 +18,9 @@ */ import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesAddToSiriProvider } from '../_add_to_siri'; +import { addToSiri } from '../_add_to_siri'; describe('addToSiri', function () { - let addToSiri; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - addToSiri = Private(PointSeriesAddToSiriProvider); - })); it('creates a new series the first time it sees an id', function () { const series = new Map(); diff --git a/src/ui/public/agg_response/point_series/__tests__/_fake_x_aspect.js b/src/ui/public/agg_response/point_series/__tests__/_fake_x_aspect.js index ecd374871fe731..3b6ac3b31d9e68 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_fake_x_aspect.js +++ b/src/ui/public/agg_response/point_series/__tests__/_fake_x_aspect.js @@ -18,36 +18,20 @@ */ import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { VisProvider } from '../../../vis'; -import { AggConfig } from '../../../vis/agg_config'; import { AggType } from '../../../agg_types/agg_type'; -import { PointSeriesFakeXAxisProvider } from '../_fake_x_aspect'; +import { makeFakeXAspect } from '../_fake_x_aspect'; describe('makeFakeXAspect', function () { - let makeFakeXAspect; - let Vis; - let indexPattern; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - Vis = Private(VisProvider); - indexPattern = Private(VisProvider); - makeFakeXAspect = Private(PointSeriesFakeXAxisProvider); - })); - it('creates an object that looks like an aspect', function () { - const vis = new Vis(indexPattern, { type: 'histogram' }); - const aspect = makeFakeXAspect(vis); + const aspect = makeFakeXAspect(); expect(aspect) .to.have.property('i', -1) - .and.have.property('aggConfig') - .and.have.property('title'); + .and.have.property('aggConfig'); expect(aspect.aggConfig) - .to.be.an(AggConfig) + .to.have.property('fieldFormatter') .and.to.have.property('type'); expect(aspect.aggConfig.type) diff --git a/src/ui/public/agg_response/point_series/__tests__/_get_aspects.js b/src/ui/public/agg_response/point_series/__tests__/_get_aspects.js index 8dabce275d5073..21a61b4d276ad3 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_get_aspects.js +++ b/src/ui/public/agg_response/point_series/__tests__/_get_aspects.js @@ -22,19 +22,16 @@ import moment from 'moment'; import expect from 'expect.js'; import ngMock from 'ng_mock'; import { VisProvider } from '../../../vis'; -import { AggConfig } from '../../../vis/agg_config'; -import { PointSeriesGetAspectsProvider } from '../_get_aspects'; +import { getAspects } from '../_get_aspects'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('getAspects', function () { let Vis; let indexPattern; - let getAspects; beforeEach(ngMock.module('kibana')); beforeEach(ngMock.inject(function (Private) { Vis = Private(VisProvider); - getAspects = Private(PointSeriesGetAspectsProvider); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); })); @@ -114,7 +111,7 @@ describe('getAspects', function () { it('produces an aspect object for each of the aspect types found in the columns', function () { init(1, 1, 1); - const aspects = getAspects(vis, table); + const aspects = getAspects(table); validate(aspects.x, 0); validate(aspects.series, 1); validate(aspects.y, 2); @@ -123,7 +120,7 @@ describe('getAspects', function () { it('uses arrays only when there are more than one aspect of a specific type', function () { init(0, 1, 2); - const aspects = getAspects(vis, table); + const aspects = getAspects(table); validate(aspects.x, 0); expect(aspects.series == null).to.be(true); @@ -137,14 +134,14 @@ describe('getAspects', function () { init(0, 2, 1); expect(function () { - getAspects(vis, table); + getAspects(table); }).to.throwError(TypeError); }); it('creates a fake x aspect if the column does not exist', function () { init(0, 0, 1); - const aspects = getAspects(vis, table); + const aspects = getAspects(table); expect(aspects.x) .to.be.an('object') @@ -152,7 +149,5 @@ describe('getAspects', function () { .and.have.property('aggConfig') .and.have.property('title'); - expect(aspects.x.aggConfig).to.be.an(AggConfig); - }); }); diff --git a/src/ui/public/agg_response/point_series/__tests__/_get_point.js b/src/ui/public/agg_response/point_series/__tests__/_get_point.js index 92a1fc871e694f..24c0a0ce3768a0 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_get_point.js +++ b/src/ui/public/agg_response/point_series/__tests__/_get_point.js @@ -19,21 +19,13 @@ import _ from 'lodash'; import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesGetPointProvider } from '../_get_point'; +import { getPoint } from '../_get_point'; describe('getPoint', function () { - let getPoint; - const truthFormatted = { fieldFormatter: _.constant(_.constant(true)) }; const identFormatted = { fieldFormatter: _.constant(_.identity) }; - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - getPoint = Private(PointSeriesGetPointProvider); - })); - describe('Without series aspect', function () { let seriesAspect; let xAspect; diff --git a/src/ui/public/agg_response/point_series/__tests__/_get_series.js b/src/ui/public/agg_response/point_series/__tests__/_get_series.js index 67c15222992533..a63ec0c3c753d3 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_get_series.js +++ b/src/ui/public/agg_response/point_series/__tests__/_get_series.js @@ -19,19 +19,11 @@ import _ from 'lodash'; import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesGetSeriesProvider } from '../_get_series'; +import { getSeries } from '../_get_series'; describe('getSeries', function () { - let getSeries; - const agg = { fieldFormatter: _.constant(_.identity) }; - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - getSeries = Private(PointSeriesGetSeriesProvider); - })); - function wrapRows(row) { return row.map(function (v) { return { value: v }; diff --git a/src/ui/public/agg_response/point_series/__tests__/_init_x_axis.js b/src/ui/public/agg_response/point_series/__tests__/_init_x_axis.js index 0389a393ad15a9..43872dba8af54e 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_init_x_axis.js +++ b/src/ui/public/agg_response/point_series/__tests__/_init_x_axis.js @@ -19,17 +19,12 @@ import _ from 'lodash'; import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesInitXAxisProvider } from '../_init_x_axis'; +import { initXAxis } from '../_init_x_axis'; describe('initXAxis', function () { - let initXAxis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - initXAxis = Private(PointSeriesInitXAxisProvider); - })); + const field = {}; + const indexPattern = {}; const baseChart = { aspects: { @@ -37,14 +32,16 @@ describe('initXAxis', function () { aggConfig: { fieldFormatter: _.constant({}), write: _.constant({ params: {} }), + aggConfigs: {}, + getIndexPattern: () => { + return indexPattern; + }, type: {} }, title: 'label' } } }; - const field = {}; - const indexPattern = {}; it('sets the xAxisFormatter if the agg is not ordered', function () { const chart = _.cloneDeep(baseChart); @@ -60,9 +57,7 @@ describe('initXAxis', function () { chart.aspects.x.aggConfig.params = { field: field }; - chart.aspects.x.aggConfig.vis = { - indexPattern: indexPattern - }; + chart.aspects.x.aggConfig.aggConfigs.indexPattern = indexPattern; initXAxis(chart); expect(chart) @@ -84,9 +79,7 @@ describe('initXAxis', function () { chart.aspects.x.aggConfig.params = { field: field }; - chart.aspects.x.aggConfig.vis = { - indexPattern: indexPattern - }; + chart.aspects.x.aggConfig.aggConfigs.indexPattern = indexPattern; initXAxis(chart); expect(chart) diff --git a/src/ui/public/agg_response/point_series/__tests__/_init_y_axis.js b/src/ui/public/agg_response/point_series/__tests__/_init_y_axis.js index d8e06f2b355a20..92776918005d27 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_init_y_axis.js +++ b/src/ui/public/agg_response/point_series/__tests__/_init_y_axis.js @@ -19,17 +19,10 @@ import _ from 'lodash'; import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesInitYAxisProvider } from '../_init_y_axis'; +import { initYAxis } from '../_init_y_axis'; describe('initYAxis', function () { - let initYAxis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - initYAxis = Private(PointSeriesInitYAxisProvider); - })); function agg() { return { diff --git a/src/ui/public/agg_response/point_series/__tests__/_main.js b/src/ui/public/agg_response/point_series/__tests__/_main.js index 3652e537d73dfa..e7b03a11966e01 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_main.js +++ b/src/ui/public/agg_response/point_series/__tests__/_main.js @@ -50,7 +50,7 @@ describe('pointSeriesChartDataFromTable', function () { table.columns = [ { aggConfig: agg } ]; table.rows.push([ result ]); - const chartData = pointSeriesChartDataFromTable(vis, table); + const chartData = pointSeriesChartDataFromTable(table); expect(chartData).to.be.an('object'); expect(chartData.series).to.be.an('array'); @@ -92,7 +92,7 @@ describe('pointSeriesChartDataFromTable', function () { table.rows.push([date, new AggConfigResult(y.agg, date, y.at(i))]); }); - const chartData = pointSeriesChartDataFromTable(vis, table); + const chartData = pointSeriesChartDataFromTable(table); expect(chartData).to.be.an('object'); expect(chartData.series).to.be.an('array'); @@ -155,7 +155,7 @@ describe('pointSeriesChartDataFromTable', function () { table.rows.push([dateResult, avgResult, maxResult]); }); - const chartData = pointSeriesChartDataFromTable(vis, table); + const chartData = pointSeriesChartDataFromTable(table); expect(chartData).to.be.an('object'); expect(chartData.series).to.be.an('array'); expect(chartData.series).to.have.length(2); @@ -235,7 +235,7 @@ describe('pointSeriesChartDataFromTable', function () { table.rows.push([dateResult, termResult, avgResult, maxResult]); }); - const chartData = pointSeriesChartDataFromTable(vis, table); + const chartData = pointSeriesChartDataFromTable(table); expect(chartData).to.be.an('object'); expect(chartData.series).to.be.an('array'); // one series for each extension, and then one for each metric inside diff --git a/src/ui/public/agg_response/point_series/__tests__/_ordered_date_axis.js b/src/ui/public/agg_response/point_series/__tests__/_ordered_date_axis.js index 611907733e86b9..f46c2bd37c9bc9 100644 --- a/src/ui/public/agg_response/point_series/__tests__/_ordered_date_axis.js +++ b/src/ui/public/agg_response/point_series/__tests__/_ordered_date_axis.js @@ -21,8 +21,7 @@ import moment from 'moment'; import _ from 'lodash'; import sinon from 'sinon'; import expect from 'expect.js'; -import ngMock from 'ng_mock'; -import { PointSeriesOrderedDateAxisProvider } from '../_ordered_date_axis'; +import { orderedDateAxis } from '../_ordered_date_axis'; describe('orderedDateAxis', function () { @@ -48,17 +47,10 @@ describe('orderedDateAxis', function () { } }; - let orderedDateAxis; - - beforeEach(ngMock.module('kibana')); - beforeEach(ngMock.inject(function (Private) { - orderedDateAxis = Private(PointSeriesOrderedDateAxisProvider); - })); - describe('xAxisFormatter', function () { it('sets the xAxisFormatter', function () { const args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); expect(args.chart).to.have.property('xAxisFormatter'); expect(args.chart.xAxisFormatter).to.be.a('function'); @@ -66,7 +58,7 @@ describe('orderedDateAxis', function () { it('formats values using moment, and returns strings', function () { const args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); const val = '2014-08-06T12:34:01'; expect(args.chart.xAxisFormatter(val)) @@ -77,7 +69,7 @@ describe('orderedDateAxis', function () { describe('ordered object', function () { it('sets date: true', function () { const args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); expect(args.chart) .to.have.property('ordered'); @@ -89,13 +81,13 @@ describe('orderedDateAxis', function () { it('relies on agg.buckets for the interval', function () { const args = _.cloneDeep(baseArgs); const spy = sinon.spy(args.chart.aspects.x.aggConfig.buckets, 'getInterval'); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); expect(spy).to.have.property('callCount', 1); }); it('sets the min/max when the buckets are bounded', function () { const args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); expect(moment.isMoment(args.chart.ordered.min)).to.be(true); expect(moment.isMoment(args.chart.ordered.max)).to.be(true); }); @@ -103,7 +95,7 @@ describe('orderedDateAxis', function () { it('does not set the min/max when the buckets are unbounded', function () { const args = _.cloneDeep(baseArgs); args.chart.aspects.x.aggConfig.buckets.getBounds = _.constant(); - orderedDateAxis(args.vis, args.chart); + orderedDateAxis(args.chart); expect(args.chart.ordered).to.not.have.property('min'); expect(args.chart.ordered).to.not.have.property('max'); }); diff --git a/src/ui/public/agg_response/point_series/_add_to_siri.js b/src/ui/public/agg_response/point_series/_add_to_siri.js index 855c11963014b9..34372819c2413c 100644 --- a/src/ui/public/agg_response/point_series/_add_to_siri.js +++ b/src/ui/public/agg_response/point_series/_add_to_siri.js @@ -17,21 +17,19 @@ * under the License. */ -export function PointSeriesAddToSiriProvider() { - return function addToSiri(series, point, id, label, agg) { - id = id == null ? '' : id + ''; +export function addToSiri(series, point, id, label, agg) { + id = id == null ? '' : id + ''; - if (series.has(id)) { - series.get(id).values.push(point); - return; - } + if (series.has(id)) { + series.get(id).values.push(point); + return; + } - series.set(id, { - label: label == null ? id : label, - aggLabel: agg.type ? agg.type.makeLabel(agg) : label, - aggId: agg.parentId ? agg.parentId : agg.id, - count: 0, - values: [point] - }); - }; + series.set(id, { + label: label == null ? id : label, + aggLabel: agg.type ? agg.type.makeLabel(agg) : label, + aggId: agg.parentId ? agg.parentId : agg.id, + count: 0, + values: [point] + }); } diff --git a/src/ui/public/agg_response/point_series/_fake_x_aspect.js b/src/ui/public/agg_response/point_series/_fake_x_aspect.js index 1fb873dda1f5df..0020778ef434db 100644 --- a/src/ui/public/agg_response/point_series/_fake_x_aspect.js +++ b/src/ui/public/agg_response/point_series/_fake_x_aspect.js @@ -17,28 +17,25 @@ * under the License. */ -import { AggConfig } from '../../vis/agg_config'; import { AggType } from '../../agg_types/agg_type'; -export function PointSeriesFakeXAxisProvider() { +const allAgg = new AggType({ + name: 'all', + title: 'All docs', + ordered: false, + hasNoDsl: true +}); - const allAgg = new AggType({ - name: 'all', - title: 'All docs', - ordered: false, - hasNoDsl: true - }); - - return function makeFakeXAxis(vis) { - const fake = new AggConfig(vis, { - type: allAgg, - schema: vis.type.schemas.all.byName.segment - }); +export function makeFakeXAspect() { + const fake = { + makeLabel: () => 'all', + fieldFormatter: () => '', + type: allAgg + }; - return { - i: -1, - aggConfig: fake, - title: fake.makeLabel(), - }; + return { + i: -1, + aggConfig: fake, + title: fake.makeLabel(), }; } diff --git a/src/ui/public/agg_response/point_series/_get_aspects.js b/src/ui/public/agg_response/point_series/_get_aspects.js index 4bb3ab3c8f1cb7..de5cc4e9f403d3 100644 --- a/src/ui/public/agg_response/point_series/_get_aspects.js +++ b/src/ui/public/agg_response/point_series/_get_aspects.js @@ -18,61 +18,57 @@ */ import _ from 'lodash'; -import { PointSeriesFakeXAxisProvider } from './_fake_x_aspect'; +import { makeFakeXAspect } from './_fake_x_aspect'; -export function PointSeriesGetAspectsProvider(Private) { - const fakeXAspect = Private(PointSeriesFakeXAxisProvider); +const map = { + segment: 'x', + metric: 'y', + radius: 'z', + width: 'width', + group: 'series' +}; - const map = { - segment: 'x', - metric: 'y', - radius: 'z', - width: 'width', - group: 'series' - }; - - function columnToAspect(aspects, col, i) { - const schema = col.aggConfig.schema.name; +function columnToAspect(aspects, col, i) { + const schema = col.aggConfig.schema.name; - const name = map[schema]; - if (!name) throw new TypeError('unknown schema name "' + schema + '"'); + const name = map[schema]; + if (!name) throw new TypeError('unknown schema name "' + schema + '"'); - const aspect = { - i: i, - title: col.title, - aggConfig: col.aggConfig - }; + const aspect = { + i: i, + title: col.title, + aggConfig: col.aggConfig + }; - if (!aspects[name]) aspects[name] = []; - aspects[name].push(aspect); - } + if (!aspects[name]) aspects[name] = []; + aspects[name].push(aspect); +} - /** - * Identify and group the columns based on the aspect of the pointSeries - * they represent. - * - * @param {array} columns - the list of columns - * @return {object} - an object with a key for each aspect (see map). The values - * may be undefined, a single aspect, or an array of aspects. - */ - return function getAspects(vis, table) { - const aspects = _(table.columns) - // write each column into the aspects under it's group - .transform(columnToAspect, {}) - // unwrap groups that only have one value, and validate groups that have more - .transform(function (aspects, group, name) { - if ((name !== 'y' && name !== 'series') && group.length > 1) { - throw new TypeError('Only multiple metrics and series are supported in point series'); - } +/** + * Identify and group the columns based on the aspect of the pointSeries + * they represent. + * + * @param {array} columns - the list of columns + * @return {object} - an object with a key for each aspect (see map). The values + * may be undefined, a single aspect, or an array of aspects. + */ +export function getAspects(table) { + const aspects = _(table.columns) + // write each column into the aspects under it's group + .transform(columnToAspect, {}) + // unwrap groups that only have one value, and validate groups that have more + .transform(function (aspects, group, name) { + if ((name !== 'y' && name !== 'series') && group.length > 1) { + throw new TypeError('Only multiple metrics and series are supported in point series'); + } - aspects[name] = group.length > 1 ? group : group[0]; - }) - .value(); + aspects[name] = group.length > 1 ? group : group[0]; + }) + .value(); - if (!aspects.x) { - aspects.x = fakeXAspect(vis); - } + if (!aspects.x) { + aspects.x = makeFakeXAspect(); + } - return aspects; - }; + return aspects; } diff --git a/src/ui/public/agg_response/point_series/_get_point.js b/src/ui/public/agg_response/point_series/_get_point.js index 2713483e3e342a..cd46cc7e1217d4 100644 --- a/src/ui/public/agg_response/point_series/_get_point.js +++ b/src/ui/public/agg_response/point_series/_get_point.js @@ -19,45 +19,43 @@ import _ from 'lodash'; -export function PointSeriesGetPointProvider() { - function unwrap(aggConfigResult, def) { - return aggConfigResult ? aggConfigResult.value : def; - } +function unwrap(aggConfigResult, def) { + return aggConfigResult ? aggConfigResult.value : def; +} - return function getPoint(x, series, yScale, row, y, z) { - const zRow = z && row[z.i]; - const xRow = row[x.i]; - - const point = { - x: unwrap(xRow, '_all'), - y: unwrap(row[y.i]), - z: zRow && unwrap(zRow), - aggConfigResult: row[y.i], - extraMetrics: _.compact([zRow]), - yScale: yScale - }; - - if (point.y === 'NaN') { - // filter out NaN from stats - // from metrics that are not based at zero - return; - } - - if (series) { - const seriesArray = series.length ? series : [ series ]; - point.aggConfig = seriesArray[0].aggConfig; - point.series = seriesArray.map(s => s.aggConfig.fieldFormatter()(unwrap(row[s.i]))).join(' - '); - } else if (y) { - // If the data is not split up with a series aspect, then - // each point's "series" becomes the y-agg that produced it - point.aggConfig = y.aggConfig; - point.series = y.title; - } - - if (yScale) { - point.y *= yScale; - } - - return point; +export function getPoint(x, series, yScale, row, y, z) { + const zRow = z && row[z.i]; + const xRow = row[x.i]; + + const point = { + x: unwrap(xRow, '_all'), + y: unwrap(row[y.i]), + z: zRow && unwrap(zRow), + aggConfigResult: row[y.i], + extraMetrics: _.compact([zRow]), + yScale: yScale }; + + if (point.y === 'NaN') { + // filter out NaN from stats + // from metrics that are not based at zero + return; + } + + if (series) { + const seriesArray = series.length ? series : [ series ]; + point.aggConfig = seriesArray[0].aggConfig; + point.series = seriesArray.map(s => s.aggConfig.fieldFormatter()(unwrap(row[s.i]))).join(' - '); + } else if (y) { + // If the data is not split up with a series aspect, then + // each point's "series" becomes the y-agg that produced it + point.aggConfig = y.aggConfig; + point.series = y.title; + } + + if (yScale) { + point.y *= yScale; + } + + return point; } diff --git a/src/ui/public/agg_response/point_series/_get_series.js b/src/ui/public/agg_response/point_series/_get_series.js index 31a40ede7e3efe..8b516f6a0a4210 100644 --- a/src/ui/public/agg_response/point_series/_get_series.js +++ b/src/ui/public/agg_response/point_series/_get_series.js @@ -18,66 +18,61 @@ */ import _ from 'lodash'; -import { PointSeriesGetPointProvider } from './_get_point'; -import { PointSeriesAddToSiriProvider } from './_add_to_siri'; +import { getPoint } from './_get_point'; +import { addToSiri } from './_add_to_siri'; -export function PointSeriesGetSeriesProvider(Private) { - const getPoint = Private(PointSeriesGetPointProvider); - const addToSiri = Private(PointSeriesAddToSiriProvider); +export function getSeries(rows, chart) { + const aspects = chart.aspects; + const multiY = Array.isArray(aspects.y); + const yScale = chart.yScale; + const partGetPoint = _.partial(getPoint, aspects.x, aspects.series, yScale); - return function getSeries(rows, chart) { - const aspects = chart.aspects; - const multiY = Array.isArray(aspects.y); - const yScale = chart.yScale; - const partGetPoint = _.partial(getPoint, aspects.x, aspects.series, yScale); + let series = _(rows) + .transform(function (series, row) { + if (!multiY) { + const point = partGetPoint(row, aspects.y, aspects.z); + if (point) addToSiri(series, point, point.series, point.series, aspects.y.aggConfig); + return; + } - let series = _(rows) - .transform(function (series, row) { - if (!multiY) { - const point = partGetPoint(row, aspects.y, aspects.z); - if (point) addToSiri(series, point, point.series, point.series, aspects.y.aggConfig); - return; - } - - aspects.y.forEach(function (y) { - const point = partGetPoint(row, y, aspects.z); - if (!point) return; + aspects.y.forEach(function (y) { + const point = partGetPoint(row, y, aspects.z); + if (!point) return; - // use the point's y-axis as it's series by default, - // but augment that with series aspect if it's actually - // available - let seriesId = y.aggConfig.id; - let seriesLabel = y.title; + // use the point's y-axis as it's series by default, + // but augment that with series aspect if it's actually + // available + let seriesId = y.aggConfig.id; + let seriesLabel = y.title; - if (aspects.series) { - const prefix = point.series ? point.series + ': ' : ''; - seriesId = prefix + seriesId; - seriesLabel = prefix + seriesLabel; - } + if (aspects.series) { + const prefix = point.series ? point.series + ': ' : ''; + seriesId = prefix + seriesId; + seriesLabel = prefix + seriesLabel; + } - addToSiri(series, point, seriesId, seriesLabel, y.aggConfig); - }); + addToSiri(series, point, seriesId, seriesLabel, y.aggConfig); + }); - }, new Map()) - .thru(series => [...series.values()]) - .value(); + }, new Map()) + .thru(series => [...series.values()]) + .value(); - if (multiY) { - series = _.sortBy(series, function (siri) { - const firstVal = siri.values[0]; - let y; + if (multiY) { + series = _.sortBy(series, function (siri) { + const firstVal = siri.values[0]; + let y; - if (firstVal) { - const agg = firstVal.aggConfigResult.aggConfig; - y = _.find(aspects.y, function (y) { - return y.aggConfig === agg; - }); - } + if (firstVal) { + const agg = firstVal.aggConfigResult.aggConfig; + y = _.find(aspects.y, function (y) { + return y.aggConfig === agg; + }); + } - return y ? y.i : series.length; - }); - } + return y ? y.i : series.length; + }); + } - return series; - }; + return series; } diff --git a/src/ui/public/agg_response/point_series/_init_x_axis.js b/src/ui/public/agg_response/point_series/_init_x_axis.js index 74be642a418748..2a37bbd1519871 100644 --- a/src/ui/public/agg_response/point_series/_init_x_axis.js +++ b/src/ui/public/agg_response/point_series/_init_x_axis.js @@ -18,21 +18,19 @@ */ -export function PointSeriesInitXAxisProvider() { - return function initXAxis(chart) { - const x = chart.aspects.x; - chart.xAxisFormatter = x.aggConfig ? x.aggConfig.fieldFormatter() : String; - chart.xAxisLabel = x.title; +export function initXAxis(chart) { + const x = chart.aspects.x; + chart.xAxisFormatter = x.aggConfig ? x.aggConfig.fieldFormatter() : String; + chart.xAxisLabel = x.title; - if (!x.aggConfig || !x.aggConfig.type.ordered) return; + if (!x.aggConfig || !x.aggConfig.type.ordered) return; - chart.indexPattern = x.aggConfig.vis.indexPattern; - chart.xAxisField = x.aggConfig.params.field; + chart.indexPattern = x.aggConfig.getIndexPattern(); + chart.xAxisField = x.aggConfig.params.field; - chart.ordered = {}; - const xAggOutput = x.aggConfig.write(); - if (xAggOutput.params.interval) { - chart.ordered.interval = xAggOutput.params.interval; - } - }; + chart.ordered = {}; + const xAggOutput = x.aggConfig.write(); + if (xAggOutput.params.interval && xAggOutput.params.interval !== '0ms') { + chart.ordered.interval = xAggOutput.params.interval; + } } diff --git a/src/ui/public/agg_response/point_series/_init_y_axis.js b/src/ui/public/agg_response/point_series/_init_y_axis.js index 1f38a100309555..e9033e74a71f28 100644 --- a/src/ui/public/agg_response/point_series/_init_y_axis.js +++ b/src/ui/public/agg_response/point_series/_init_y_axis.js @@ -17,29 +17,26 @@ * under the License. */ -export function PointSeriesInitYAxisProvider() { +export function initYAxis(chart) { + const y = chart.aspects.y; - return function initYAxis(chart) { - const y = chart.aspects.y; + if (Array.isArray(y)) { + // TODO: vis option should allow choosing this format + chart.yAxisFormatter = y[0].aggConfig.fieldFormatter(); + chart.yAxisLabel = ''; // use the legend + } else { + chart.yAxisFormatter = y.aggConfig.fieldFormatter(); + chart.yAxisLabel = y.title; + } - if (Array.isArray(y)) { - // TODO: vis option should allow choosing this format - chart.yAxisFormatter = y[0].aggConfig.fieldFormatter(); - chart.yAxisLabel = ''; // use the legend + const z = chart.aspects.series; + if (z) { + if (Array.isArray(z)) { + chart.zAxisFormatter = z[0].aggConfig.fieldFormatter(); + chart.zAxisLabel = ''; // use the legend } else { - chart.yAxisFormatter = y.aggConfig.fieldFormatter(); - chart.yAxisLabel = y.title; + chart.zAxisFormatter = z.aggConfig.fieldFormatter(); + chart.zAxisLabel = z.title; } - - const z = chart.aspects.series; - if (z) { - if (Array.isArray(z)) { - chart.zAxisFormatter = z[0].aggConfig.fieldFormatter(); - chart.zAxisLabel = ''; // use the legend - } else { - chart.zAxisFormatter = z.aggConfig.fieldFormatter(); - chart.zAxisLabel = z.title; - } - } - }; + } } diff --git a/src/ui/public/agg_response/point_series/_ordered_date_axis.js b/src/ui/public/agg_response/point_series/_ordered_date_axis.js index 0b63e82ebd6430..933e93aca2db81 100644 --- a/src/ui/public/agg_response/point_series/_ordered_date_axis.js +++ b/src/ui/public/agg_response/point_series/_ordered_date_axis.js @@ -19,29 +19,26 @@ import moment from 'moment'; -export function PointSeriesOrderedDateAxisProvider() { +export function orderedDateAxis(chart) { + const xAgg = chart.aspects.x.aggConfig; + const buckets = xAgg.buckets; + const format = buckets.getScaledDateFormat(); - return function orderedDateAxis(vis, chart) { - const xAgg = chart.aspects.x.aggConfig; - const buckets = xAgg.buckets; - const format = buckets.getScaledDateFormat(); - - chart.xAxisFormatter = function (val) { - return moment(val).format(format); - }; - - chart.ordered = { - date: true, - interval: buckets.getInterval(), - }; + chart.xAxisFormatter = function (val) { + return moment(val).format(format); + }; - const axisOnTimeField = xAgg.fieldIsTimeField(); - const bounds = buckets.getBounds(); - if (bounds && axisOnTimeField) { - chart.ordered.min = bounds.min; - chart.ordered.max = bounds.max; - } else { - chart.ordered.endzones = false; - } + chart.ordered = { + date: true, + interval: buckets.getInterval(), }; + + const axisOnTimeField = xAgg.fieldIsTimeField(); + const bounds = buckets.getBounds(); + if (bounds && axisOnTimeField) { + chart.ordered.min = bounds.min; + chart.ordered.max = bounds.max; + } else { + chart.ordered.endzones = false; + } } diff --git a/src/ui/public/agg_response/point_series/point_series.js b/src/ui/public/agg_response/point_series/point_series.js index b6788bbbec00e8..10cbd4726034e1 100644 --- a/src/ui/public/agg_response/point_series/point_series.js +++ b/src/ui/public/agg_response/point_series/point_series.js @@ -17,25 +17,20 @@ * under the License. */ -import { PointSeriesGetSeriesProvider } from './_get_series'; -import { PointSeriesGetAspectsProvider } from './_get_aspects'; -import { PointSeriesInitYAxisProvider } from './_init_y_axis'; -import { PointSeriesInitXAxisProvider } from './_init_x_axis'; -import { PointSeriesOrderedDateAxisProvider } from './_ordered_date_axis'; +import { getSeries } from './_get_series'; +import { getAspects } from './_get_aspects'; +import { initYAxis } from './_init_y_axis'; +import { initXAxis } from './_init_x_axis'; +import { orderedDateAxis } from './_ordered_date_axis'; import { PointSeriesTooltipFormatter } from './_tooltip_formatter'; export function AggResponsePointSeriesProvider(Private) { - const getSeries = Private(PointSeriesGetSeriesProvider); - const getAspects = Private(PointSeriesGetAspectsProvider); - const initYAxis = Private(PointSeriesInitYAxisProvider); - const initXAxis = Private(PointSeriesInitXAxisProvider); - const setupOrderedDateXAxis = Private(PointSeriesOrderedDateAxisProvider); const tooltipFormatter = Private(PointSeriesTooltipFormatter); - return function pointSeriesChartDataFromTable(vis, table) { + return function pointSeriesChartDataFromTable(table) { const chart = {}; - const aspects = chart.aspects = getAspects(vis, table); + const aspects = chart.aspects = getAspects(table); chart.tooltipFormatter = tooltipFormatter; @@ -44,7 +39,7 @@ export function AggResponsePointSeriesProvider(Private) { const datedX = aspects.x.aggConfig.type.ordered && aspects.x.aggConfig.type.ordered.date; if (datedX) { - setupOrderedDateXAxis(vis, chart); + orderedDateAxis(chart); } chart.series = getSeries(table.rows, chart); diff --git a/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js index 8b777d919550f8..a0736ebd2f5264 100644 --- a/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js +++ b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js @@ -30,6 +30,17 @@ describe('Geohash Agg', () => { top_left: { lat: 1.0, lon: -1.0 }, bottom_right: { lat: -1.0, lon: 1.0 } }; + + const BucketAggTypeMock = (aggOptions) => { + return aggOptions; + }; + const AggConfigMock = (parent, aggOptions) => { + return aggOptions; + }; + const createAggregationMock = (aggOptions) => { + return new AggConfigMock(null, aggOptions); + }; + const aggMock = { getField: () => { return { @@ -41,17 +52,11 @@ describe('Geohash Agg', () => { useGeocentroid: true, mapZoom: initialZoom }, - vis: { - aggs: [] - }, + aggConfigs: {}, type: 'geohash_grid', }; - const BucketAggTypeMock = (aggOptions) => { - return aggOptions; - }; - const AggConfigMock = (vis, aggOptions) => { - return aggOptions; - }; + aggMock.aggConfigs.createAggConfig = createAggregationMock; + before(function () { sinon.stub(AggConfigModule, 'AggConfig').callsFake(AggConfigMock); diff --git a/src/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js b/src/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js index 11326c1e8a7366..afacdc522562ed 100644 --- a/src/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js +++ b/src/ui/public/agg_types/__tests__/buckets/date_histogram/_editor.js @@ -58,7 +58,10 @@ describe('editor', function () { ] }); - const $el = $(''); + const $el = $('' + + ''); const $parentScope = $injector.get('$rootScope').$new(); agg = $parentScope.agg = vis.aggs.bySchemaName.segment[0]; diff --git a/src/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js b/src/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js index ba4528110546e7..394ff6e526907c 100644 --- a/src/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js +++ b/src/ui/public/agg_types/__tests__/buckets/date_histogram/_params.js @@ -36,7 +36,7 @@ describe('params', function () { let paramWriter; let writeInterval; - let setTimeBounds; + let getTimeBounds; let timeField; beforeEach(ngMock.module('kibana')); @@ -47,19 +47,17 @@ describe('params', function () { timeField = indexPattern.timeFieldName; paramWriter = new AggParamWriter({ aggType: 'date_histogram' }); - writeInterval = function (interval) { - return paramWriter.write({ interval: interval, field: timeField }); + writeInterval = function (interval, timeRange) { + return paramWriter.write({ interval: interval, field: timeField, timeRange: timeRange }); }; const now = moment(); - setTimeBounds = function (n, units) { + getTimeBounds = function (n, units) { timefilter.enableAutoRefreshSelector(); timefilter.enableTimeRangeSelector(); - paramWriter.vis.filters = { - timeRange: { - from: now.clone().subtract(n, units), - to: now.clone() - } + return { + from: now.clone().subtract(n, units), + to: now.clone() }; }; })); @@ -76,22 +74,22 @@ describe('params', function () { }); it('automatically picks an interval', function () { - setTimeBounds(15, 'm'); - const output = writeInterval('auto'); + const timeBounds = getTimeBounds(15, 'm'); + const output = writeInterval('auto', timeBounds); expect(output.params.interval).to.be('30s'); }); it('scales up the interval if it will make too many buckets', function () { - setTimeBounds(30, 'm'); - const output = writeInterval('s'); + const timeBounds = getTimeBounds(30, 'm'); + const output = writeInterval('s', timeBounds); expect(output.params.interval).to.be('10s'); expect(output.metricScaleText).to.be('second'); expect(output.metricScale).to.be(0.1); }); it('does not scale down the interval', function () { - setTimeBounds(1, 'm'); - const output = writeInterval('h'); + const timeBounds = getTimeBounds(1, 'm'); + const output = writeInterval('h', timeBounds); expect(output.params.interval).to.be('1h'); expect(output.metricScaleText).to.be(undefined); expect(output.metricScale).to.be(undefined); @@ -109,21 +107,21 @@ describe('params', function () { const typeNames = test.slice(); it(typeNames.join(', ') + ' should ' + (should ? '' : 'not') + ' scale', function () { - setTimeBounds(1, 'y'); + const timeBounds = getTimeBounds(1, 'y'); const vis = paramWriter.vis; vis.aggs.splice(0); - const histoConfig = new AggConfig(vis, { + const histoConfig = new AggConfig(vis.aggs, { type: aggTypes.byName.date_histogram, schema: 'segment', - params: { interval: 's', field: timeField } + params: { interval: 's', field: timeField, timeRange: timeBounds } }); vis.aggs.push(histoConfig); typeNames.forEach(function (type) { - vis.aggs.push(new AggConfig(vis, { + vis.aggs.push(new AggConfig(vis.aggs, { type: aggTypes.byName[type], schema: 'metric' })); diff --git a/src/ui/public/agg_types/buckets/_terms_other_bucket_helper.js b/src/ui/public/agg_types/buckets/_terms_other_bucket_helper.js index 338a427674329a..2840da808b7524 100644 --- a/src/ui/public/agg_types/buckets/_terms_other_bucket_helper.js +++ b/src/ui/public/agg_types/buckets/_terms_other_bucket_helper.js @@ -18,7 +18,6 @@ */ import _ from 'lodash'; -import { AggConfig } from '../../vis/agg_config'; import { buildExistsFilter } from '../../filter_manager/lib/exists'; import { buildPhrasesFilter } from '../../filter_manager/lib/phrases'; import { buildQueryFromFilters } from '../../courier'; @@ -110,7 +109,7 @@ const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) => { const indexPattern = aggWithOtherBucket.params.field.indexPattern; // create filters aggregation - const filterAgg = new AggConfig(aggConfigs[index].vis, { + const filterAgg = aggConfigs.createAggConfig({ type: 'filters', id: 'other', }); diff --git a/src/ui/public/agg_types/buckets/create_filter/date_histogram.js b/src/ui/public/agg_types/buckets/create_filter/date_histogram.js index 5431fbf7925b30..0bfefe35381e40 100644 --- a/src/ui/public/agg_types/buckets/create_filter/date_histogram.js +++ b/src/ui/public/agg_types/buckets/create_filter/date_histogram.js @@ -28,5 +28,5 @@ export function createFilterDateHistogram(agg, key) { gte: start.valueOf(), lt: start.add(interval).valueOf(), format: 'epoch_millis' - }, agg._indexPattern); + }, agg.getIndexPattern()); } diff --git a/src/ui/public/agg_types/buckets/create_filter/date_range.js b/src/ui/public/agg_types/buckets/create_filter/date_range.js index 09d027e16bef04..5acace8e53dfcd 100644 --- a/src/ui/public/agg_types/buckets/create_filter/date_range.js +++ b/src/ui/public/agg_types/buckets/create_filter/date_range.js @@ -31,5 +31,5 @@ export function createFilterDateRange(agg, key) { if (range.to) filter.lt = +range.to; if (range.to && range.from) filter.format = 'epoch_millis'; - return buildRangeFilter(agg.params.field, filter, agg._indexPattern); + return buildRangeFilter(agg.params.field, filter, agg.getIndexPattern()); } diff --git a/src/ui/public/agg_types/buckets/create_filter/filters.js b/src/ui/public/agg_types/buckets/create_filter/filters.js index 794b4f773c7ccd..23dfeb109cc1a8 100644 --- a/src/ui/public/agg_types/buckets/create_filter/filters.js +++ b/src/ui/public/agg_types/buckets/create_filter/filters.js @@ -26,6 +26,6 @@ export function createFilterFilters(aggConfig, key) { const filter = dslFilters[key]; if (filter) { - return buildQueryFilter(filter.query, aggConfig._indexPattern.id); + return buildQueryFilter(filter.query, aggConfig.getIndexPattern().id); } } diff --git a/src/ui/public/agg_types/buckets/create_filter/histogram.js b/src/ui/public/agg_types/buckets/create_filter/histogram.js index 343469e207209b..d9ddae8ae30f17 100644 --- a/src/ui/public/agg_types/buckets/create_filter/histogram.js +++ b/src/ui/public/agg_types/buckets/create_filter/histogram.js @@ -25,7 +25,7 @@ export function createFilterHistogram(aggConfig, key) { return buildRangeFilter( aggConfig.params.field, { gte: value, lt: value + aggConfig.params.interval }, - aggConfig._indexPattern, + aggConfig.getIndexPattern(), aggConfig.fieldFormatter()(key) ); } diff --git a/src/ui/public/agg_types/buckets/create_filter/ip_range.js b/src/ui/public/agg_types/buckets/create_filter/ip_range.js index 41e2af1477106d..578607edb903df 100644 --- a/src/ui/public/agg_types/buckets/create_filter/ip_range.js +++ b/src/ui/public/agg_types/buckets/create_filter/ip_range.js @@ -32,5 +32,5 @@ export function createFilterIpRange(aggConfig, key) { }; } - return buildRangeFilter(aggConfig.params.field, { gte: range.from, lte: range.to }, aggConfig._indexPattern); + return buildRangeFilter(aggConfig.params.field, { gte: range.from, lte: range.to }, aggConfig.getIndexPattern()); } diff --git a/src/ui/public/agg_types/buckets/create_filter/range.js b/src/ui/public/agg_types/buckets/create_filter/range.js index f6516f6d06c614..e344aae438d405 100644 --- a/src/ui/public/agg_types/buckets/create_filter/range.js +++ b/src/ui/public/agg_types/buckets/create_filter/range.js @@ -23,7 +23,7 @@ export function createFilterRange(aggConfig, key) { return buildRangeFilter( aggConfig.params.field, key, - aggConfig._indexPattern, + aggConfig.getIndexPattern(), aggConfig.fieldFormatter()(key) ); } diff --git a/src/ui/public/agg_types/buckets/date_histogram.js b/src/ui/public/agg_types/buckets/date_histogram.js index c712a34a95eaf3..35b27f9b4076fe 100644 --- a/src/ui/public/agg_types/buckets/date_histogram.js +++ b/src/ui/public/agg_types/buckets/date_histogram.js @@ -28,6 +28,7 @@ import { TimeBuckets } from '../../time_buckets'; import { createFilterDateHistogram } from './create_filter/date_histogram'; import { intervalOptions } from './_interval_options'; import intervalTemplate from '../controls/time_interval.html'; +import { timefilter } from '../../timefilter'; import dropPartialTemplate from '../controls/drop_partials.html'; const config = chrome.getUiSettingsClient(); @@ -42,16 +43,10 @@ function getInterval(agg) { return interval; } -function getBounds(vis) { - if (vis.filters && vis.filters.timeRange) { - return vis.API.timeFilter.calculateBounds(vis.filters.timeRange); - } -} - function setBounds(agg, force) { if (agg.buckets._alreadySet && !force) return; agg.buckets._alreadySet = true; - const bounds = getBounds(agg.vis); + const bounds = agg.params.timeRange ? timefilter.calculateBounds(agg.params.timeRange) : null; agg.buckets.setBounds(agg.fieldIsTimeField() && bounds); } @@ -93,7 +88,7 @@ export const dateHistogramBucketAgg = new BucketAggType({ name: 'field', filterFieldTypes: 'date', default: function (agg) { - return agg._indexPattern.timeFieldName; + return agg.getIndexPattern().timeFieldName; }, onChange: function (agg) { if (_.get(agg, 'params.interval.val') === 'auto' && !agg.fieldIsTimeField()) { @@ -103,7 +98,11 @@ export const dateHistogramBucketAgg = new BucketAggType({ setBounds(agg, true); } }, - + { + name: 'timeRange', + default: null, + write: _.noop, + }, { name: 'interval', type: 'optioned', diff --git a/src/ui/public/agg_types/buckets/date_range.js b/src/ui/public/agg_types/buckets/date_range.js index e2dd39a74444d4..41085085aafe5e 100644 --- a/src/ui/public/agg_types/buckets/date_range.js +++ b/src/ui/public/agg_types/buckets/date_range.js @@ -43,7 +43,7 @@ export const dateRangeBucketAgg = new BucketAggType({ name: 'field', filterFieldTypes: 'date', default: function (agg) { - return agg._indexPattern.timeFieldName; + return agg.getIndexPattern().timeFieldName; } }, { name: 'ranges', diff --git a/src/ui/public/agg_types/buckets/geo_hash.js b/src/ui/public/agg_types/buckets/geo_hash.js index 3cc5facd3ae396..1f969972536b46 100644 --- a/src/ui/public/agg_types/buckets/geo_hash.js +++ b/src/ui/public/agg_types/buckets/geo_hash.js @@ -20,7 +20,6 @@ import _ from 'lodash'; import chrome from '../../chrome'; import { BucketAggType } from './_bucket_agg_type'; -import { AggConfig } from '../../vis/agg_config'; import precisionTemplate from '../controls/precision.html'; import { geohashColumns } from '../../utils/decode_geo_hash'; import { geoContains, scaleBounds } from '../../utils/geo_utils'; @@ -117,7 +116,7 @@ export const geoHashBucketAgg = new BucketAggType({ ], getRequestAggs: function (agg) { const aggs = []; - const { vis, params } = agg; + const params = agg.params; if (params.isFilteredByCollar && agg.getField()) { const { mapBounds, mapZoom } = params; @@ -137,7 +136,7 @@ export const geoHashBucketAgg = new BucketAggType({ bottom_right: mapCollar.bottom_right } }; - aggs.push(new AggConfig(vis, { + aggs.push(agg.aggConfigs.createAggConfig({ type: 'filter', id: 'filter_agg', enabled: true, @@ -147,20 +146,20 @@ export const geoHashBucketAgg = new BucketAggType({ schema: { group: 'buckets' } - })); + }, { addToAggConfigs: false })); } } aggs.push(agg); if (params.useGeocentroid) { - aggs.push(new AggConfig(vis, { + aggs.push(agg.aggConfigs.createAggConfig({ type: 'geo_centroid', enabled: true, params: { field: agg.getField() } - })); + }, { addToAggConfigs: false })); } return aggs; diff --git a/src/ui/public/agg_types/buckets/terms.js b/src/ui/public/agg_types/buckets/terms.js index 0c1f6b71f4651b..3317707e736bc3 100644 --- a/src/ui/public/agg_types/buckets/terms.js +++ b/src/ui/public/agg_types/buckets/terms.js @@ -147,7 +147,7 @@ export const termsBucketAgg = new BucketAggType({ makeOrderAgg: function (termsAgg, state) { state = state || {}; state.schema = orderAggSchema; - const orderAgg = new AggConfig(termsAgg.vis, state); + const orderAgg = this.aggConfigs.createAggConfig(state); orderAgg.id = termsAgg.id + '-orderAgg'; return orderAgg; }, diff --git a/src/ui/public/agg_types/controls/field.html b/src/ui/public/agg_types/controls/field.html index b6a0edc1d16303..fcac6015af69c9 100644 --- a/src/ui/public/agg_types/controls/field.html +++ b/src/ui/public/agg_types/controls/field.html @@ -33,7 +33,7 @@

- No Compatible Fields: The "{{ agg._indexPattern.title }}" index pattern does not contain any of the following field types: {{ agg.type.params.byName.field.filterFieldTypes | commaList:false }} + No Compatible Fields: The "{{ agg.getIndexPattern().title }}" index pattern does not contain any of the following field types: {{ agg.type.params.byName.field.filterFieldTypes | commaList:false }}

diff --git a/src/ui/public/agg_types/controls/order_agg.html b/src/ui/public/agg_types/controls/order_agg.html index fd3f80158d5263..4d600c75fd5949 100644 --- a/src/ui/public/agg_types/controls/order_agg.html +++ b/src/ui/public/agg_types/controls/order_agg.html @@ -24,7 +24,7 @@
diff --git a/src/ui/public/agg_types/controls/sub_agg.html b/src/ui/public/agg_types/controls/sub_agg.html index 1378bc896ab2c3..9d882b9aa003cf 100644 --- a/src/ui/public/agg_types/controls/sub_agg.html +++ b/src/ui/public/agg_types/controls/sub_agg.html @@ -25,7 +25,7 @@
diff --git a/src/ui/public/agg_types/controls/sub_metric.html b/src/ui/public/agg_types/controls/sub_metric.html index d5d53902378b51..937a25cb8c7f6e 100644 --- a/src/ui/public/agg_types/controls/sub_metric.html +++ b/src/ui/public/agg_types/controls/sub_metric.html @@ -5,7 +5,7 @@ diff --git a/src/ui/public/agg_types/metrics/lib/make_nested_label.js b/src/ui/public/agg_types/metrics/lib/make_nested_label.js index 5ced0753282d79..3d65ae4ad15766 100644 --- a/src/ui/public/agg_types/metrics/lib/make_nested_label.js +++ b/src/ui/public/agg_types/metrics/lib/make_nested_label.js @@ -35,7 +35,7 @@ const makeNestedLabel = function (aggConfig, label) { } return metricLabel; } - const metric = aggConfig._aggs.find(agg => agg.id === aggConfig.params.metricAgg); + const metric = aggConfig.aggConfigs.find(agg => agg.id === aggConfig.params.metricAgg); if (!metric) return ''; return `${uppercaseLabel} of ${metric.makeLabel()}`; }; diff --git a/src/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.js b/src/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.js index c9687b1c4903f1..c54a1b8e4910dd 100644 --- a/src/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.js +++ b/src/ui/public/agg_types/metrics/lib/parent_pipeline_agg_helper.js @@ -54,7 +54,7 @@ const parentPipelineAggHelper = { makeAgg: function (termsAgg, state) { state = state || { type: 'count' }; state.schema = metricAggSchema; - const metricAgg = new AggConfig(termsAgg.vis, state); + const metricAgg = termsAgg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); metricAgg.id = termsAgg.id + '-metric'; return metricAgg; }, @@ -79,7 +79,7 @@ const parentPipelineAggHelper = { if (agg.params.customMetric) { subAgg = agg.params.customMetric; } else { - subAgg = agg._aggs.byId[agg.params.metricAgg]; + subAgg = agg.aggConfigs.byId[agg.params.metricAgg]; } return subAgg.type.getFormat(subAgg); } diff --git a/src/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.js b/src/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.js index befc8c0c95fe26..8dc36b5e10d5ee 100644 --- a/src/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.js +++ b/src/ui/public/agg_types/metrics/lib/sibling_pipeline_agg_helper.js @@ -68,7 +68,7 @@ const siblingPipelineAggHelper = { makeAgg: function (agg, state) { state = state || { type: 'date_histogram' }; state.schema = bucketAggSchema; - const orderAgg = new AggConfig(agg.vis, state); + const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); orderAgg.id = agg.id + '-bucket'; return orderAgg; }, @@ -90,7 +90,7 @@ const siblingPipelineAggHelper = { makeAgg: function (agg, state) { state = state || { type: 'count' }; state.schema = metricAggSchema; - const orderAgg = new AggConfig(agg.vis, state); + const orderAgg = agg.aggConfigs.createAggConfig(state, { addToAggConfigs: false }); orderAgg.id = agg.id + '-metric'; return orderAgg; }, diff --git a/src/ui/public/agg_types/metrics/top_hit.js b/src/ui/public/agg_types/metrics/top_hit.js index 498739373d0787..c5efcb8f7717a7 100644 --- a/src/ui/public/agg_types/metrics/top_hit.js +++ b/src/ui/public/agg_types/metrics/top_hit.js @@ -41,12 +41,7 @@ export const topHitMetricAgg = new MetricAggType({ { name: 'field', onlyAggregatable: false, - filterFieldTypes: function (vis, value) { - if (vis.type.name === 'table' || vis.type.name === 'metric') { - return true; - } - return value === 'number'; - }, + filterFieldTypes: '*', write(agg, output) { const field = agg.params.field; output.params = {}; @@ -137,7 +132,7 @@ export const topHitMetricAgg = new MetricAggType({ editor: null, filterFieldTypes: [ 'number', 'date', 'ip', 'string' ], default: function (agg) { - return agg._indexPattern.timeFieldName; + return agg.getIndexPattern().timeFieldName; }, write: _.noop // prevent default write, it is handled below }, @@ -187,7 +182,7 @@ export const topHitMetricAgg = new MetricAggType({ const path = agg.params.field.name; let values = _(hits).map(hit => { - return path === '_source' ? hit._source : agg._indexPattern.flattenHit(hit, true)[path]; + return path === '_source' ? hit._source : agg.getIndexPattern().flattenHit(hit, true)[path]; }) .flatten() .value(); diff --git a/src/ui/public/vis/__tests__/_agg_config.js b/src/ui/public/vis/__tests__/_agg_config.js index 2e87f1a9bffb81..707ccf2d6f17a2 100644 --- a/src/ui/public/vis/__tests__/_agg_config.js +++ b/src/ui/public/vis/__tests__/_agg_config.js @@ -414,11 +414,10 @@ describe('AggConfig', function () { const label = aggConfig.makeLabel(); expect(label).to.be('Count'); }); - it('default label should be "Percentage of Count" when Vis is in percentage mode', function () { + it('default label should be "Percentage of Count" when percentageMode is set to true', function () { const vis = new Vis(indexPattern, {}); const aggConfig = vis.aggs[0]; - aggConfig.vis.params.mode = 'percentage'; - const label = aggConfig.makeLabel(); + const label = aggConfig.makeLabel(true); expect(label).to.be('Percentage of Count'); }); it('empty label if the Vis type is not defined', function () { diff --git a/src/ui/public/vis/__tests__/_agg_configs.js b/src/ui/public/vis/__tests__/_agg_configs.js index 28f8a1bea68c81..14bf84485898a8 100644 --- a/src/ui/public/vis/__tests__/_agg_configs.js +++ b/src/ui/public/vis/__tests__/_agg_configs.js @@ -52,7 +52,7 @@ describe('AggConfigs', function () { aggs: [] }); - const ac = new AggConfigs(vis); + const ac = new AggConfigs(vis.indexPattern, [], vis.type.schemas.all); expect(ac).to.have.length(1); }); @@ -62,16 +62,16 @@ describe('AggConfigs', function () { aggs: [] }); - const ac = new AggConfigs(vis, [ + const ac = new AggConfigs(vis.indexPattern, [ { type: 'date_histogram', schema: 'segment' }, - new AggConfig(vis, { + new AggConfig(vis.aggs, { type: 'terms', schema: 'split' }) - ]); + ], vis.type.schemas.all); expect(ac).to.have.length(3); }); @@ -94,7 +94,7 @@ describe('AggConfigs', function () { ]; const spy = sinon.spy(AggConfig, 'ensureIds'); - new AggConfigs(vis, states); + new AggConfigs(vis.indexPattern, states, vis.type.schemas.all); expect(spy.callCount).to.be(1); expect(spy.firstCall.args[0]).to.be(states); AggConfig.ensureIds.restore(); @@ -136,17 +136,17 @@ describe('AggConfigs', function () { }); it('should only set the number of defaults defined by the max', function () { - const ac = new AggConfigs(vis); + const ac = new AggConfigs(vis.indexPattern, [], vis.type.schemas.all); expect(ac.bySchemaName.metric).to.have.length(2); }); it('should set the defaults defined in the schema when none exist', function () { - const ac = new AggConfigs(vis); + const ac = new AggConfigs(vis.indexPattern, [], vis.type.schemas.all); expect(ac).to.have.length(3); }); it('should NOT set the defaults defined in the schema when some exist', function () { - const ac = new AggConfigs(vis, [{ schema: 'segment', type: 'date_histogram' }]); + const ac = new AggConfigs(vis.indexPattern, [{ schema: 'segment', type: 'date_histogram' }], vis.type.schemas.all); expect(ac).to.have.length(3); expect(ac.bySchemaName.segment[0].type.name).to.equal('date_histogram'); }); @@ -332,7 +332,7 @@ describe('AggConfigs', function () { }); vis.isHierarchical = _.constant(true); - const topLevelDsl = vis.aggs.toDsl(); + const topLevelDsl = vis.aggs.toDsl(vis.isHierarchical()); const buckets = vis.aggs.bySchemaGroup.buckets; const metrics = vis.aggs.bySchemaGroup.metrics; diff --git a/src/ui/public/vis/__tests__/_vis.js b/src/ui/public/vis/__tests__/_vis.js index 81ca0d31b5a038..a5765b8b8fe947 100644 --- a/src/ui/public/vis/__tests__/_vis.js +++ b/src/ui/public/vis/__tests__/_vis.js @@ -48,6 +48,7 @@ describe('Vis Class', function () { const state = (type) => ({ type: { visConfig: { defaults: {} }, + schemas: {}, ...type, } }); diff --git a/src/ui/public/vis/agg_config.js b/src/ui/public/vis/agg_config.js index 1df28693e1b520..62d24186658ac9 100644 --- a/src/ui/public/vis/agg_config.js +++ b/src/ui/public/vis/agg_config.js @@ -62,11 +62,9 @@ class AggConfig { }, 0); } - constructor(vis, opts = {}, aggs) { - this.id = String(opts.id || AggConfig.nextId(vis.aggs)); - this.vis = vis; - this._indexPattern = vis.indexPattern; - this._aggs = aggs || vis.aggs; + constructor(aggConfigs, opts = {}) { + this.aggConfigs = aggConfigs; + this.id = String(opts.id || AggConfig.nextId(aggConfigs)); this._opts = opts; this.enabled = typeof opts.enabled === 'boolean' ? opts.enabled : true; @@ -261,18 +259,22 @@ class AggConfig { return this.params.field; } - makeLabel() { + makeLabel(percentageMode = false) { if (this.params.customLabel) { return this.params.customLabel; } if (!this.type) return ''; - let pre = (_.get(this.vis, 'params.mode') === 'percentage') ? 'Percentage of ' : ''; + let pre = percentageMode ? 'Percentage of ' : ''; return pre += this.type.makeLabel(this); } getIndexPattern() { - return this.vis.indexPattern; + return _.get(this.aggConfigs, 'indexPattern', null); + } + + getTimeRange() { + return _.get(this.aggConfigs, 'timeRange', null); } getFieldOptions() { @@ -305,7 +307,7 @@ class AggConfig { } fieldIsTimeField() { - const timeFieldName = this.vis.indexPattern.timeFieldName; + const timeFieldName = this.getIndexPattern().timeFieldName; return timeFieldName && this.fieldName() === timeFieldName; } @@ -348,8 +350,8 @@ class AggConfig { } set schema(schema) { - if (_.isString(schema)) { - schema = this.vis.type.schemas.all.byName[schema]; + if (_.isString(schema) && this.aggConfigs.schemas) { + schema = this.aggConfigs.schemas.byName[schema]; } this.__schema = schema; diff --git a/src/ui/public/vis/agg_configs.js b/src/ui/public/vis/agg_configs.js index de7e805158641a..c61fae5ec740bb 100644 --- a/src/ui/public/vis/agg_configs.js +++ b/src/ui/public/vis/agg_configs.js @@ -47,7 +47,7 @@ function parseParentAggs(dslLvlCursor, dsl) { } class AggConfigs extends IndexedArray { - constructor(vis, configStates = []) { + constructor(indexPattern, configStates = [], schemas) { configStates = AggConfig.ensureIds(configStates); super({ @@ -55,36 +55,77 @@ class AggConfigs extends IndexedArray { group: ['schema.group', 'type.name', 'schema.name'], }); - this.push(...configStates.map(aggConfigState => { - if (aggConfigState instanceof AggConfig) { - return aggConfigState; - } - return new AggConfig(vis, aggConfigState, this); - })); + this.indexPattern = indexPattern; + this.schemas = schemas; - this.vis = vis; + configStates.forEach(params => this.createAggConfig(params)); + if (this.schemas) { + this.initializeDefaultsFromSchemas(schemas); + } + } + + initializeDefaultsFromSchemas(schemas) { // Set the defaults for any schema which has them. If the defaults // for some reason has more then the max only set the max number // of defaults (not sure why a someone define more... // but whatever). Also if a schema.name is already set then don't // set anything. - if (vis && vis.type && vis.type.schemas && vis.type.schemas.all) { - _(vis.type.schemas.all) - .filter(schema => { - return Array.isArray(schema.defaults) && schema.defaults.length > 0; - }) - .each(schema => { - if (!this.bySchemaName[schema.name]) { - const defaults = schema.defaults.slice(0, schema.max); - _.each(defaults, defaultState => { - const state = _.defaults({ id: AggConfig.nextId(this) }, defaultState); - this.push(new AggConfig(vis, state, this)); - }); - } - }) - .commit(); + _(schemas) + .filter(schema => { + return Array.isArray(schema.defaults) && schema.defaults.length > 0; + }) + .each(schema => { + if (!this.bySchemaName[schema.name]) { + const defaults = schema.defaults.slice(0, schema.max); + _.each(defaults, defaultState => { + const state = _.defaults({ id: AggConfig.nextId(this) }, defaultState); + this.push(new AggConfig(this, state)); + }); + } + }) + .commit(); + } + + setTimeRange(timeRange) { + this.timeRange = timeRange; + + const updateAggTimeRange = (agg) => { + _.each(agg.params, param => { + if (param instanceof AggConfig) { + updateAggTimeRange(param); + } + }); + if (agg.type.name === 'date_histogram') { + agg.params.timeRange = timeRange; + } + }; + + this.forEach(updateAggTimeRange); + } + + // clone method will reuse existing AggConfig in the list (will not create new instances) + clone({ enabledOnly = true } = {}) { + const filterAggs = (agg) => { + if (!enabledOnly) return true; + return agg.enabled; + }; + const aggConfigs = new AggConfigs(this.indexPattern, this.raw.filter(filterAggs), this.schemas); + return aggConfigs; + } + + createAggConfig(params, { addToAggConfigs = true } = {}) { + let aggConfig; + if (params instanceof AggConfig) { + aggConfig = params; + params.parent = this; + } else { + aggConfig = new AggConfig(this, params); + } + if (addToAggConfigs) { + this.push(aggConfig); } + return aggConfig; } /** @@ -104,14 +145,14 @@ class AggConfigs extends IndexedArray { return true; } - toDsl() { + toDsl(hierarchical = false) { const dslTopLvl = {}; let dslLvlCursor; let nestedMetrics; - if (this.vis.isHierarchical()) { + if (hierarchical) { // collect all metrics, and filter out the ones that we won't be copying - nestedMetrics = _(this.vis.aggs.bySchemaGroup.metrics) + nestedMetrics = _(this.bySchemaGroup.metrics) .filter(function (agg) { return agg.type.name !== 'count'; }) diff --git a/src/ui/public/vis/editors/default/__tests__/agg_params.js b/src/ui/public/vis/editors/default/__tests__/agg_params.js index 70abf6d676f736..9e84cdf4b90ada 100644 --- a/src/ui/public/vis/editors/default/__tests__/agg_params.js +++ b/src/ui/public/vis/editors/default/__tests__/agg_params.js @@ -76,7 +76,7 @@ describe('Vis-Editor-Agg-Params plugin directive', function () { ] }); - $parentScope.agg = new AggConfig(vis, state); + $parentScope.agg = new AggConfig(vis.aggs, state); $parentScope.vis = vis; // make the element diff --git a/src/ui/public/vis/editors/default/agg_add.js b/src/ui/public/vis/editors/default/agg_add.js index 965323ccac4fd6..9c7e2c4fc1e61c 100644 --- a/src/ui/public/vis/editors/default/agg_add.js +++ b/src/ui/public/vis/editors/default/agg_add.js @@ -36,7 +36,7 @@ uiModules self.submit = function (schema) { self.form = false; - const aggConfig = new AggConfig($scope.vis, { + const aggConfig = new AggConfig($scope.state.aggs, { schema: schema, id: AggConfig.nextId($scope.state.aggs), }); diff --git a/src/ui/public/vis/map/convert_to_geojson.js b/src/ui/public/vis/map/convert_to_geojson.js index 0b4193460f1e59..8ce11ede15c724 100644 --- a/src/ui/public/vis/map/convert_to_geojson.js +++ b/src/ui/public/vis/map/convert_to_geojson.js @@ -45,6 +45,7 @@ export function convertToGeoJson(tabifiedResponse) { features = table.rows.map(row => { const geohash = row[geohashColumn.id]; + if (!geohash) return false; const geohashLocation = decodeGeoHash(geohash); let pointCoordinates; @@ -94,7 +95,7 @@ export function convertToGeoJson(tabifiedResponse) { }; - }); + }).filter(row => row); } diff --git a/src/ui/public/vis/request_handlers/courier.js b/src/ui/public/vis/request_handlers/courier.js index f39130280e49c8..fd64c225863a00 100644 --- a/src/ui/public/vis/request_handlers/courier.js +++ b/src/ui/public/vis/request_handlers/courier.js @@ -84,6 +84,8 @@ const CourierRequestHandlerProvider = function () { const timeFilterSearchSource = searchSource.createChild({ callParentStartHandlers: true }); const requestSearchSource = timeFilterSearchSource.createChild({ callParentStartHandlers: true }); + aggs.setTimeRange(timeRange); + // For now we need to mirror the history of the passed search source, since // the spy panel wouldn't work otherwise. Object.defineProperty(requestSearchSource, 'history', { @@ -122,7 +124,7 @@ const CourierRequestHandlerProvider = function () { return requestSearchSource.getSearchRequestBody().then(q => { const queryHash = calculateObjectHash(q); if (shouldQuery(queryHash)) { - const lastAggConfig = vis.getAggConfig(); + const lastAggConfig = aggs; vis.API.inspectorAdapters.requests.reset(); const request = vis.API.inspectorAdapters.requests.start('Data', { description: `This request queries Elasticsearch to fetch the data for the visualization.`, diff --git a/src/ui/public/vis/response_handlers/vislib.js b/src/ui/public/vis/response_handlers/vislib.js index 9cdf11596aaf41..d8359b7bdef6ae 100644 --- a/src/ui/public/vis/response_handlers/vislib.js +++ b/src/ui/public/vis/response_handlers/vislib.js @@ -61,7 +61,7 @@ const VislibResponseHandlerProvider = function (Private) { } function convertTable(vis, table) { - return vis.type.responseConverter ? vis.type.responseConverter(vis, table) : table; + return vis.type.responseConverter ? vis.type.responseConverter(table) : table; } return { diff --git a/src/ui/public/vis/vis.js b/src/ui/public/vis/vis.js index 84606c5b656886..85d59bf4ccd57a 100644 --- a/src/ui/public/vis/vis.js +++ b/src/ui/public/vis/vis.js @@ -192,7 +192,7 @@ export function VisProvider(Private, indexPatterns, getAppState) { updateVisualizationConfig(state.params, this.params); - this.aggs = new AggConfigs(this, state.aggs); + this.aggs = new AggConfigs(this.indexPattern, state.aggs, this.type.schemas.all); } setState(state, updateCurrentState = true) { @@ -237,7 +237,7 @@ export function VisProvider(Private, indexPatterns, getAppState) { copyCurrentState(includeDisabled = false) { const state = this.getCurrentState(includeDisabled); - state.aggs = new AggConfigs(this, state.aggs); + state.aggs = new AggConfigs(this.indexPattern, state.aggs, this.type.schemas.all); return state; } @@ -256,7 +256,7 @@ export function VisProvider(Private, indexPatterns, getAppState) { } getAggConfig() { - return new AggConfigs(this, this.aggs.raw.filter(agg => agg.enabled)); + return this.aggs.clone({ enabledOnly: true }); } getState() {