chore(superset-ui-chart-controls): refactor pivot and rename operator (#22963)

This commit is contained in:
Ville Brofeldt 2023-02-02 17:16:07 +02:00 committed by GitHub
parent ed7b3533bc
commit c53c3aa23d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 18 deletions

View File

@ -31,13 +31,14 @@ export const pivotOperator: PostProcessingFactory<PostProcessingPivot> = (
) => { ) => {
const metricLabels = ensureIsArray(queryObject.metrics).map(getMetricLabel); const metricLabels = ensureIsArray(queryObject.metrics).map(getMetricLabel);
const xAxisLabel = getXAxisLabel(formData); const xAxisLabel = getXAxisLabel(formData);
const columns = queryObject.series_columns || queryObject.columns;
if (xAxisLabel && metricLabels.length) { if (xAxisLabel && metricLabels.length) {
return { return {
operation: 'pivot', operation: 'pivot',
options: { options: {
index: [xAxisLabel], index: [xAxisLabel],
columns: ensureIsArray(queryObject.columns).map(getColumnLabel), columns: ensureIsArray(columns).map(getColumnLabel),
// Create 'dummy' mean aggregates to assign cell values in pivot table // Create 'dummy' mean aggregates to assign cell values in pivot table
// use the 'mean' aggregates to avoid drop NaN. PR: https://github.com/apache-superset/superset-ui/pull/1231 // use the 'mean' aggregates to avoid drop NaN. PR: https://github.com/apache-superset/superset-ui/pull/1231
aggregates: Object.fromEntries( aggregates: Object.fromEntries(

View File

@ -32,14 +32,16 @@ export const renameOperator: PostProcessingFactory<PostProcessingRename> = (
queryObject, queryObject,
) => { ) => {
const metrics = ensureIsArray(queryObject.metrics); const metrics = ensureIsArray(queryObject.metrics);
const columns = ensureIsArray(queryObject.columns); const columns = ensureIsArray(
queryObject.series_columns || queryObject.columns,
);
const { truncate_metric } = formData; const { truncate_metric } = formData;
const xAxisLabel = getXAxisLabel(formData); const xAxisLabel = getXAxisLabel(formData);
// remove or rename top level of column name(metric name) in the MultiIndex when // remove or rename top level of column name(metric name) in the MultiIndex when
// 1) only 1 metric // 1) only 1 metric
// 2) exist dimentsion // 2) dimension exist
// 3) exist xAxis // 3) xAxis exist
// 4) exist time comparison, and comparison type is "actual values" // 4) time comparison exist, and comparison type is "actual values"
// 5) truncate_metric in form_data and truncate_metric is true // 5) truncate_metric in form_data and truncate_metric is true
if ( if (
metrics.length === 1 && metrics.length === 1 &&

View File

@ -31,6 +31,7 @@ export const timeComparePivotOperator: PostProcessingFactory<PostProcessingPivot
(formData, queryObject) => { (formData, queryObject) => {
const metricOffsetMap = getMetricOffsetsMap(formData, queryObject); const metricOffsetMap = getMetricOffsetsMap(formData, queryObject);
const xAxisLabel = getXAxisLabel(formData); const xAxisLabel = getXAxisLabel(formData);
const columns = queryObject.series_columns || queryObject.columns;
if (isTimeComparison(formData, queryObject) && xAxisLabel) { if (isTimeComparison(formData, queryObject) && xAxisLabel) {
const aggregates = Object.fromEntries( const aggregates = Object.fromEntries(
@ -45,7 +46,7 @@ export const timeComparePivotOperator: PostProcessingFactory<PostProcessingPivot
operation: 'pivot', operation: 'pivot',
options: { options: {
index: [xAxisLabel], index: [xAxisLabel],
columns: ensureIsArray(queryObject.columns).map(getColumnLabel), columns: ensureIsArray(columns).map(getColumnLabel),
drop_missing_columns: !formData?.show_empty_columns, drop_missing_columns: !formData?.show_empty_columns,
aggregates, aggregates,
}, },

View File

@ -64,7 +64,7 @@ test('skip pivot', () => {
).toEqual(undefined); ).toEqual(undefined);
}); });
test('pivot by __timestamp without groupby', () => { test('pivot by __timestamp without columns', () => {
expect( expect(
pivotOperator( pivotOperator(
{ ...formData, granularity_sqla: 'time_column' }, { ...formData, granularity_sqla: 'time_column' },
@ -84,7 +84,7 @@ test('pivot by __timestamp without groupby', () => {
}); });
}); });
test('pivot by __timestamp with groupby', () => { test('pivot by __timestamp with columns', () => {
expect( expect(
pivotOperator( pivotOperator(
{ ...formData, granularity_sqla: 'time_column' }, { ...formData, granularity_sqla: 'time_column' },
@ -107,6 +107,29 @@ test('pivot by __timestamp with groupby', () => {
}); });
}); });
test('pivot by __timestamp with series_columns', () => {
expect(
pivotOperator(
{ ...formData, granularity_sqla: 'time_column' },
{
...queryObject,
series_columns: ['foo', 'bar'],
},
),
).toEqual({
operation: 'pivot',
options: {
index: ['__timestamp'],
columns: ['foo', 'bar'],
aggregates: {
'count(*)': { operator: 'mean' },
'sum(val)': { operator: 'mean' },
},
drop_missing_columns: false,
},
});
});
test('pivot by x_axis with groupby', () => { test('pivot by x_axis with groupby', () => {
expect( expect(
pivotOperator( pivotOperator(
@ -116,7 +139,7 @@ test('pivot by x_axis with groupby', () => {
}, },
{ {
...queryObject, ...queryObject,
columns: ['foo', 'bar'], series_columns: ['foo', 'bar'],
}, },
), ),
).toEqual({ ).toEqual({
@ -146,7 +169,7 @@ test('pivot by adhoc x_axis', () => {
}, },
{ {
...queryObject, ...queryObject,
columns: ['foo', 'bar'], series_columns: ['foo', 'bar'],
}, },
), ),
).toEqual({ ).toEqual({

View File

@ -49,7 +49,7 @@ test('should skip renameOperator if exists multiple metrics', () => {
).toEqual(undefined); ).toEqual(undefined);
}); });
test('should skip renameOperator if does not exist series', () => { test('should skip renameOperator if series does not exist', () => {
expect( expect(
renameOperator(formData, { renameOperator(formData, {
...queryObject, ...queryObject,
@ -105,7 +105,7 @@ test('should add renameOperator', () => {
}); });
}); });
test('should add renameOperator if does not exist x_axis', () => { test('should add renameOperator if x_axis does not exist', () => {
expect( expect(
renameOperator( renameOperator(
{ {
@ -120,6 +120,25 @@ test('should add renameOperator if does not exist x_axis', () => {
}); });
}); });
test('should add renameOperator if based on series_columns', () => {
expect(
renameOperator(
{
...formData,
...{ x_axis: null, granularity_sqla: 'time column' },
},
{
...queryObject,
columns: [],
series_columns: ['gender', 'dttm'],
},
),
).toEqual({
operation: 'rename',
options: { columns: { 'count(*)': null }, inplace: true, level: 0 },
});
});
test('should add renameOperator if exist "actual value" time comparison', () => { test('should add renameOperator if exist "actual value" time comparison', () => {
expect( expect(
renameOperator( renameOperator(

View File

@ -136,6 +136,45 @@ test('should pivot on x-axis', () => {
}); });
}); });
test('should pivot on x-axis with series_columns', () => {
expect(
timeComparePivotOperator(
{
...formData,
comparison_type: 'values',
time_compare: ['1 year ago', '1 year later'],
x_axis: 'ds',
},
{
...queryObject,
columns: ['ds', 'foo', 'bar'],
series_columns: ['foo', 'bar'],
},
),
).toEqual({
operation: 'pivot',
options: {
aggregates: {
'count(*)': { operator: 'mean' },
'count(*)__1 year ago': { operator: 'mean' },
'count(*)__1 year later': { operator: 'mean' },
'sum(val)': {
operator: 'mean',
},
'sum(val)__1 year ago': {
operator: 'mean',
},
'sum(val)__1 year later': {
operator: 'mean',
},
},
drop_missing_columns: false,
columns: ['foo', 'bar'],
index: ['ds'],
},
});
});
test('should pivot on adhoc x-axis', () => { test('should pivot on adhoc x-axis', () => {
expect( expect(
timeComparePivotOperator( timeComparePivotOperator(

View File

@ -67,8 +67,8 @@ export default function buildQuery(formData: QueryFormData) {
fd, fd,
queryObject, queryObject,
) )
? timeComparePivotOperator(fd, { ...queryObject, columns: fd.groupby }) ? timeComparePivotOperator(fd, queryObject)
: pivotOperator(fd, { ...queryObject, columns: fd.groupby }); : pivotOperator(fd, queryObject);
const tmpQueryObject = { const tmpQueryObject = {
...queryObject, ...queryObject,
@ -78,10 +78,7 @@ export default function buildQuery(formData: QueryFormData) {
rollingWindowOperator(fd, queryObject), rollingWindowOperator(fd, queryObject),
timeCompareOperator(fd, queryObject), timeCompareOperator(fd, queryObject),
resampleOperator(fd, queryObject), resampleOperator(fd, queryObject),
renameOperator(fd, { renameOperator(fd, queryObject),
...queryObject,
columns: fd.groupby,
}),
flattenOperator(fd, queryObject), flattenOperator(fd, queryObject),
], ],
} as QueryObject; } as QueryObject;