Ensure extra columns are not shown in table vis when showPartialRows:true (#25690)
This commit is contained in:
parent
46538d22e4
commit
e6f30a2ea0
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ng-model="editorState.params.showPartialRows">
|
<input type="checkbox" ng-model="editorState.params.showPartialRows" data-test-subj="showPartialRows">
|
||||||
<span
|
<span
|
||||||
i18n-id="tableVis.params.showPartialRowsLabel"
|
i18n-id="tableVis.params.showPartialRowsLabel"
|
||||||
i18n-default-message="Show partial rows"
|
i18n-default-message="Show partial rows"
|
||||||
|
|
|
@ -42,7 +42,7 @@ describe('buildHierarchicalData', function () {
|
||||||
const buildHierarchicalData = async (aggs, response) => {
|
const buildHierarchicalData = async (aggs, response) => {
|
||||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: aggs });
|
const vis = new Vis(indexPattern, { type: 'histogram', aggs: aggs });
|
||||||
vis.isHierarchical = () => true;
|
vis.isHierarchical = () => true;
|
||||||
const data = tabifyAggResponse(vis.aggs, response, { metricsAtAllLevels: true });
|
const data = tabifyAggResponse(vis.aggs, response, { minimalColumns: false });
|
||||||
return await responseHandler(data);
|
return await responseHandler(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ describe('tabifyAggResponse Integration', function () {
|
||||||
normalizeIds(vis);
|
normalizeIds(vis);
|
||||||
|
|
||||||
const resp = tabifyAggResponse(vis.getAggConfig(), fixtures.metricOnly, {
|
const resp = tabifyAggResponse(vis.getAggConfig(), fixtures.metricOnly, {
|
||||||
metricsAtAllLevels: vis.isHierarchical()
|
minimalColumns: !vis.isHierarchical(),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(resp).to.have.property('rows').and.property('columns');
|
expect(resp).to.have.property('rows').and.property('columns');
|
||||||
|
@ -138,7 +138,9 @@ describe('tabifyAggResponse Integration', function () {
|
||||||
// the default for a non-hierarchical vis is to display
|
// the default for a non-hierarchical vis is to display
|
||||||
// only complete rows, and only put the metrics at the end.
|
// only complete rows, and only put the metrics at the end.
|
||||||
|
|
||||||
const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, { minimalColumns: true });
|
const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, {
|
||||||
|
minimalColumns: true,
|
||||||
|
});
|
||||||
|
|
||||||
expectColumns(tabbed, [ext, src, os, avg]);
|
expectColumns(tabbed, [ext, src, os, avg]);
|
||||||
|
|
||||||
|
@ -159,7 +161,9 @@ describe('tabifyAggResponse Integration', function () {
|
||||||
// the existing bucket and it's metric
|
// the existing bucket and it's metric
|
||||||
|
|
||||||
vis.isHierarchical = _.constant(true);
|
vis.isHierarchical = _.constant(true);
|
||||||
const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, { metricsAtAllLevels: true });
|
const tabbed = tabifyAggResponse(vis.getAggConfig(), esResp, {
|
||||||
|
minimalColumns: false,
|
||||||
|
});
|
||||||
|
|
||||||
expectColumns(tabbed, [ext, avg, src, avg, os, avg]);
|
expectColumns(tabbed, [ext, avg, src, avg, os, avg]);
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,8 @@ describe('TabbedAggResponseWriter class', function () {
|
||||||
expect(responseWriter.columns.length).to.eql(3);
|
expect(responseWriter.columns.length).to.eql(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly generates columns with metricsAtAllLevels set to true', () => {
|
it('correctly generates columns with minimalColumns set to false', () => {
|
||||||
const minimalColumnsResponseWriter = createResponseWritter(twoSplitsAggConfig, { metricsAtAllLevels: true });
|
const minimalColumnsResponseWriter = createResponseWritter(twoSplitsAggConfig, { minimalColumns: false });
|
||||||
expect(minimalColumnsResponseWriter.columns.length).to.eql(4);
|
expect(minimalColumnsResponseWriter.columns.length).to.eql(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,16 @@ const getColumn = (agg, i) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function tabifyGetColumns(aggs, minimal) {
|
/**
|
||||||
|
* Builds tabify columns.
|
||||||
|
*
|
||||||
|
* @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
|
||||||
|
* @param {boolean} minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level
|
||||||
|
*/
|
||||||
|
export function tabifyGetColumns(aggs, minimalColumns) {
|
||||||
|
|
||||||
// pick the columns
|
// pick the columns
|
||||||
if (minimal) {
|
if (minimalColumns) {
|
||||||
return aggs.map((agg, i) => getColumn(agg, i));
|
return aggs.map((agg, i) => getColumn(agg, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,22 +25,27 @@ import { tabifyGetColumns } from './_get_columns';
|
||||||
* produces a table, or a series of tables.
|
* produces a table, or a series of tables.
|
||||||
*
|
*
|
||||||
* @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
|
* @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
|
||||||
* @param {boolean} metricsAtAllLevels - setting to true will produce metrics for every bucket
|
* @param {boolean} minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level
|
||||||
* @param {boolean} partialRows - setting to true will not remove rows with missing values
|
* @param {boolean} partialRows - vis.params.showPartialRows: determines whether to return rows with incomplete data
|
||||||
|
* @param {Object} timeRange - time range object, if provided
|
||||||
*/
|
*/
|
||||||
function TabbedAggResponseWriter(aggs, { metricsAtAllLevels = false, partialRows = false, timeRange } = {}) {
|
function TabbedAggResponseWriter(aggs, {
|
||||||
|
minimalColumns = true,
|
||||||
|
partialRows = false,
|
||||||
|
timeRange
|
||||||
|
} = {}) {
|
||||||
|
// Private
|
||||||
|
this._removePartialRows = !partialRows;
|
||||||
|
|
||||||
|
// Public
|
||||||
this.rowBuffer = {};
|
this.rowBuffer = {};
|
||||||
this.bucketBuffer = [];
|
this.bucketBuffer = [];
|
||||||
this.metricBuffer = [];
|
this.metricBuffer = [];
|
||||||
|
|
||||||
this.metricsForAllBuckets = metricsAtAllLevels;
|
|
||||||
this.partialRows = partialRows;
|
|
||||||
this.aggs = aggs;
|
this.aggs = aggs;
|
||||||
this.columns = tabifyGetColumns(aggs.getResponseAggs(), !metricsAtAllLevels);
|
this.partialRows = partialRows;
|
||||||
|
this.columns = tabifyGetColumns(aggs.getResponseAggs(), minimalColumns);
|
||||||
this.aggStack = [...this.columns];
|
this.aggStack = [...this.columns];
|
||||||
|
|
||||||
this.rows = [];
|
this.rows = [];
|
||||||
|
|
||||||
// Extract the time range object if provided
|
// Extract the time range object if provided
|
||||||
if (timeRange) {
|
if (timeRange) {
|
||||||
const timeRangeKey = Object.keys(timeRange)[0];
|
const timeRangeKey = Object.keys(timeRange)[0];
|
||||||
|
@ -67,7 +72,7 @@ TabbedAggResponseWriter.prototype.row = function () {
|
||||||
this.rowBuffer[metric.id] = metric.value;
|
this.rowBuffer[metric.id] = metric.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!toArray(this.rowBuffer).length || (!this.partialRows && this.isPartialRow(this.rowBuffer))) {
|
if (!toArray(this.rowBuffer).length || (this._removePartialRows && this.isPartialRow(this.rowBuffer))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,16 @@ import _ from 'lodash';
|
||||||
import { TabbedAggResponseWriter } from './_response_writer';
|
import { TabbedAggResponseWriter } from './_response_writer';
|
||||||
import { TabifyBuckets } from './_buckets';
|
import { TabifyBuckets } from './_buckets';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the ResponseWriter and kicks off bucket collection.
|
||||||
|
*
|
||||||
|
* @param {AggConfigs} aggs - the agg configs object to which the aggregation response correlates
|
||||||
|
* @param {Object} esResponse - response that came back from Elasticsearch
|
||||||
|
* @param {Object} respOpts - options object for the ResponseWriter with params set by Courier
|
||||||
|
* @param {boolean} respOpts.minimalColumns - setting to true will only return a column for the last bucket/metric instead of one for each level
|
||||||
|
* @param {boolean} respOpts.partialRows - vis.params.showPartialRows: determines whether to return rows with incomplete data
|
||||||
|
* @param {Object} respOpts.timeRange - time range object, if provided
|
||||||
|
*/
|
||||||
export function tabifyAggResponse(aggs, esResponse, respOpts = {}) {
|
export function tabifyAggResponse(aggs, esResponse, respOpts = {}) {
|
||||||
const write = new TabbedAggResponseWriter(aggs, respOpts);
|
const write = new TabbedAggResponseWriter(aggs, respOpts);
|
||||||
|
|
||||||
|
@ -34,7 +44,7 @@ export function tabifyAggResponse(aggs, esResponse, respOpts = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read an aggregation from a bucket, which is *might* be found at key (if
|
* read an aggregation from a bucket, which *might* be found at key (if
|
||||||
* the response came in object form), and will recurse down the aggregation
|
* the response came in object form), and will recurse down the aggregation
|
||||||
* tree and will pass the read values to the ResponseWriter.
|
* tree and will pass the read values to the ResponseWriter.
|
||||||
*
|
*
|
||||||
|
|
|
@ -57,7 +57,9 @@ describe('AggTable Directive', function () {
|
||||||
vis2.aggs.forEach(function (agg, i) {
|
vis2.aggs.forEach(function (agg, i) {
|
||||||
agg.id = 'agg_' + (i + 1);
|
agg.id = 'agg_' + (i + 1);
|
||||||
});
|
});
|
||||||
tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets, { metricsAtAllLevels: true });
|
tabifiedData.threeTermBuckets = tabifyAggResponse(vis2.aggs, fixtures.threeTermBuckets, {
|
||||||
|
minimalColumns: false,
|
||||||
|
});
|
||||||
|
|
||||||
const vis3 = new Vis(indexPattern, {
|
const vis3 = new Vis(indexPattern, {
|
||||||
type: 'table',
|
type: 'table',
|
||||||
|
|
|
@ -38,8 +38,9 @@ const CourierRequestHandlerProvider = function () {
|
||||||
filters,
|
filters,
|
||||||
forceFetch,
|
forceFetch,
|
||||||
partialRows,
|
partialRows,
|
||||||
isHierarchical,
|
metricsAtAllLevels,
|
||||||
inspectorAdapters,
|
inspectorAdapters,
|
||||||
|
minimalColumns,
|
||||||
queryFilter
|
queryFilter
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ const CourierRequestHandlerProvider = function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
requestSearchSource.setField('aggs', function () {
|
requestSearchSource.setField('aggs', function () {
|
||||||
return aggs.toDsl(isHierarchical);
|
return aggs.toDsl(metricsAtAllLevels);
|
||||||
});
|
});
|
||||||
|
|
||||||
requestSearchSource.onRequestStart((searchSource, searchRequest) => {
|
requestSearchSource.onRequestStart((searchSource, searchRequest) => {
|
||||||
|
@ -133,7 +134,7 @@ const CourierRequestHandlerProvider = function () {
|
||||||
|
|
||||||
const parsedTimeRange = timeRange ? getTime(aggs.indexPattern, timeRange) : null;
|
const parsedTimeRange = timeRange ? getTime(aggs.indexPattern, timeRange) : null;
|
||||||
const tabifyParams = {
|
const tabifyParams = {
|
||||||
metricsAtAllLevels: isHierarchical,
|
minimalColumns,
|
||||||
partialRows,
|
partialRows,
|
||||||
timeRange: parsedTimeRange ? parsedTimeRange.range : undefined,
|
timeRange: parsedTimeRange ? parsedTimeRange.range : undefined,
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,7 +36,8 @@ export interface RequestHandlerParams {
|
||||||
uiState?: PersistedState;
|
uiState?: PersistedState;
|
||||||
partialRows?: boolean;
|
partialRows?: boolean;
|
||||||
inspectorAdapters?: Adapters;
|
inspectorAdapters?: Adapters;
|
||||||
isHierarchical?: boolean;
|
metricsAtAllLevels?: boolean;
|
||||||
|
minimalColumns?: boolean;
|
||||||
visParams?: any;
|
visParams?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -128,8 +128,12 @@ describe('No global chart settings', function () {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const table1 = tabifyAggResponse(stubVis1.aggs, fixtures.threeTermBuckets, { metricsAtAllLevels: true });
|
const table1 = tabifyAggResponse(stubVis1.aggs, fixtures.threeTermBuckets, {
|
||||||
const table2 = tabifyAggResponse(stubVis2.aggs, fixtures.threeTermBuckets, { metricsAtAllLevels: true });
|
minimalColumns: false,
|
||||||
|
});
|
||||||
|
const table2 = tabifyAggResponse(stubVis2.aggs, fixtures.threeTermBuckets, {
|
||||||
|
minimalColumns: false,
|
||||||
|
});
|
||||||
data1 = await responseHandler(table1);
|
data1 = await responseHandler(table1);
|
||||||
data2 = await responseHandler(table2);
|
data2 = await responseHandler(table2);
|
||||||
|
|
||||||
|
@ -219,7 +223,9 @@ describe('Vislib PieChart Class Test Suite', function () {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const table = tabifyAggResponse(stubVis.aggs, fixtures.threeTermBuckets, { metricsAtAllLevels: true });
|
const table = tabifyAggResponse(stubVis.aggs, fixtures.threeTermBuckets, {
|
||||||
|
minimalColumns: false,
|
||||||
|
});
|
||||||
data = await responseHandler(table);
|
data = await responseHandler(table);
|
||||||
vis.render(data, persistedState);
|
vis.render(data, persistedState);
|
||||||
});
|
});
|
||||||
|
|
|
@ -73,10 +73,18 @@ export class VisualizeDataLoader {
|
||||||
this.vis.showRequestError = false;
|
this.vis.showRequestError = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Vis types that have a `showMetricsAtAllLevels` param (e.g. data table) should tell
|
||||||
|
// tabify whether to return columns for each bucket based on the param value. Vis types
|
||||||
|
// without this param should default to returning all columns if they are hierarchical.
|
||||||
|
const minimalColumns =
|
||||||
|
typeof this.vis.params.showMetricsAtAllLevels !== 'undefined'
|
||||||
|
? !this.vis.params.showMetricsAtAllLevels
|
||||||
|
: !this.vis.isHierarchical();
|
||||||
// searchSource is only there for courier request handler
|
// searchSource is only there for courier request handler
|
||||||
const requestHandlerResponse = await this.requestHandler({
|
const requestHandlerResponse = await this.requestHandler({
|
||||||
partialRows: this.vis.params.partialRows || this.vis.type.requiresPartialRows,
|
partialRows: this.vis.type.requiresPartialRows || this.vis.params.showPartialRows,
|
||||||
isHierarchical: this.vis.isHierarchical(),
|
minimalColumns,
|
||||||
|
metricsAtAllLevels: this.vis.isHierarchical(),
|
||||||
visParams: this.vis.params,
|
visParams: this.vis.params,
|
||||||
...params,
|
...params,
|
||||||
filters: params.filters
|
filters: params.filters
|
||||||
|
|
|
@ -267,6 +267,25 @@ export default function ({ getService, getPageObjects }) {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show correct data without showMetricsAtAllLevels even if showPartialRows is selected', async () => {
|
||||||
|
await PageObjects.visualize.clickOptionsTab();
|
||||||
|
await PageObjects.visualize.checkCheckbox('showPartialRows');
|
||||||
|
await PageObjects.visualize.clickGo();
|
||||||
|
const data = await PageObjects.visualize.getTableVisContent();
|
||||||
|
expect(data).to.be.eql([
|
||||||
|
[ 'jpg', 'CN', '1,718' ],
|
||||||
|
[ 'jpg', 'IN', '1,511' ],
|
||||||
|
[ 'jpg', 'US', '770' ],
|
||||||
|
[ 'jpg', 'ID', '314' ],
|
||||||
|
[ 'jpg', 'PK', '244' ],
|
||||||
|
[ 'css', 'CN', '422' ],
|
||||||
|
[ 'css', 'IN', '346' ],
|
||||||
|
[ 'css', 'US', '189' ],
|
||||||
|
[ 'css', 'ID', '68' ],
|
||||||
|
[ 'css', 'BR', '58' ],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should show metrics on each level', async () => {
|
it('should show metrics on each level', async () => {
|
||||||
await PageObjects.visualize.clickOptionsTab();
|
await PageObjects.visualize.clickOptionsTab();
|
||||||
await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels');
|
await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels');
|
||||||
|
@ -364,7 +383,7 @@ export default function ({ getService, getPageObjects }) {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show metrics for split bucket when using showMetricsAtAllLevels', async () => {
|
it('should show metrics for split bucket when using showMetricsAtAllLevels', async () => {
|
||||||
await PageObjects.visualize.clickOptionsTab();
|
await PageObjects.visualize.clickOptionsTab();
|
||||||
await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels');
|
await PageObjects.visualize.checkCheckbox('showMetricsAtAllLevels');
|
||||||
await PageObjects.visualize.clickGo();
|
await PageObjects.visualize.clickGo();
|
||||||
|
|
Loading…
Reference in a new issue