Skip to content
This repository has been archived by the owner on Dec 10, 2021. It is now read-only.

feat(core): add support for non-temporal series limit #1356

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,20 @@ const limit: SharedControlConfig<'SelectControl'> = {
),
};

const series_limit: SharedControlConfig<'SelectControl'> = {
type: 'SelectControl',
freeForm: true,
label: t('Series limit'),
validators: [legacyValidateInteger],
choices: formatSelectOptions(SERIES_LIMITS),
description: t(
'Limits the number of series that get displayed. A sub query ' +
'(or an extra phase where sub queries are not supported) is applied to limit ' +
'the number of series that get fetched and displayed. This feature is useful ' +
'when grouping by high cardinality dimension(s).',
),
};

const sort_by: SharedControlConfig<'MetricsControl'> = {
type: 'MetricsControl',
label: t('Sort By'),
Expand Down Expand Up @@ -495,6 +509,9 @@ const sharedControls = {
adhoc_filters: enableExploreDnd ? dnd_adhoc_filters : adhoc_filters,
color_scheme,
label_colors,
series_columns: enableExploreDnd ? dndColumnsControl : columnsControl,
series_limit,
series_limit_metric: enableExploreDnd ? dnd_sort_by : sort_by,
};

export default sharedControls;
6 changes: 6 additions & 0 deletions packages/superset-ui-core/src/query/buildQueryObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export default function buildQueryObject<T extends QueryFormData>(
granularity,
url_params = {},
custom_params = {},
series_columns,
series_limit,
series_limit_metric,
...residualFormData
} = formData;
const {
Expand Down Expand Up @@ -76,6 +79,9 @@ export default function buildQueryObject<T extends QueryFormData>(
annotation_layers,
row_limit: row_limit == null || Number.isNaN(numericRowLimit) ? undefined : numericRowLimit,
row_offset: row_offset == null || Number.isNaN(numericRowOffset) ? undefined : numericRowOffset,
series_columns,
series_limit,
series_limit_metric,
timeseries_limit: limit ? Number(limit) : 0,
timeseries_limit_metric: timeseries_limit_metric || undefined,
order_desc: typeof order_desc === 'undefined' ? true : order_desc,
Expand Down
9 changes: 7 additions & 2 deletions packages/superset-ui-core/src/query/types/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { DatasourceType } from './Datasource';
import { BinaryOperator, SetOperator, UnaryOperator } from './Operator';
import { AppliedTimeExtras, TimeRange, TimeRangeEndpoints } from './Time';
import { AnnotationLayer } from './AnnotationLayer';
import { QueryFields, QueryFormMetric } from './QueryFormData';
import { QueryFields, QueryFormColumn, QueryFormMetric } from './QueryFormData';
import { Maybe } from '../../types';
import { PostProcessingRule } from './PostProcessing';
import { JsonObject } from '../../connection';
Expand Down Expand Up @@ -121,7 +121,7 @@ export interface QueryObject extends QueryFields, TimeRange, ResidualQueryObject
/** The size of bucket by which to group timeseries data (forthcoming) */
time_grain?: string;

/** Maximum number of series */
/** Maximum number of timeseries */
timeseries_limit?: number;

/** The metric used to sort the returned result. */
Expand All @@ -136,6 +136,11 @@ export interface QueryObject extends QueryFields, TimeRange, ResidualQueryObject

/** Free-form WHERE SQL: multiple clauses are concatenated by AND */
where?: string;

/** Limit number of series */
series_columns?: QueryFormColumn[];
series_limit?: number;
series_limit_metric?: Maybe<QueryFormMetric>;
}

export interface QueryContext {
Expand Down
4 changes: 4 additions & 0 deletions packages/superset-ui-core/src/query/types/QueryFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ export interface BaseFormData extends TimeRange, FormDataResidual {
annotation_layers?: AnnotationLayer[];
url_params?: Record<string, string>;
custom_params?: Record<string, string>;
/** limit number of series */
series_columns?: QueryFormColumn[];
series_limit?: number;
series_limit_metric?: QueryFormColumn;
}

/**
Expand Down
18 changes: 13 additions & 5 deletions plugins/plugin-chart-echarts/src/BoxPlot/buildQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ import { BoxPlotQueryFormData, BoxPlotQueryObjectWhiskerType } from './types';
const PERCENTILE_REGEX = /(\d+)\/(\d+) percentiles/;

export default function buildQuery(formData: BoxPlotQueryFormData) {
const { whiskerOptions, columns: distributionColumns = [] } = formData;
const { columns = [], granularity_sqla, groupby = [], whiskerOptions } = formData;
return buildQueryContext(formData, baseQueryObject => {
let whiskerType: BoxPlotQueryObjectWhiskerType;
let percentiles: [number, number] | undefined;
const { columns = [], metrics = [] } = baseQueryObject;
const { metrics = [] } = baseQueryObject;
const percentileMatch = PERCENTILE_REGEX.exec(whiskerOptions as string);

if (whiskerOptions === 'Tukey') {
if (whiskerOptions === 'Tukey' || whiskerOptions === undefined) {
villebro marked this conversation as resolved.
Show resolved Hide resolved
whiskerType = 'tukey';
} else if (whiskerOptions === 'Min/max (no outliers)') {
whiskerType = 'min/max';
Expand All @@ -39,17 +39,25 @@ export default function buildQuery(formData: BoxPlotQueryFormData) {
} else {
throw new Error(`Unsupported whisker type: ${whiskerOptions}`);
}
const distributionColumns: string[] = [];

// For now default to using the temporal column as distribution column.
// In the future this control should be made mandatory.
if (!columns.length && granularity_sqla) {
distributionColumns.push(granularity_sqla);
}
return [
{
...baseQueryObject,
is_timeseries: distributionColumns.length === 0,
columns: [...distributionColumns, ...columns, ...groupby],
series_columns: groupby,
post_processing: [
{
operation: 'boxplot',
options: {
whisker_type: whiskerType,
percentiles,
groupby: columns.filter(x => !distributionColumns.includes(x)),
groupby,
metrics: metrics.map(getMetricLabel),
},
},
Expand Down
5 changes: 3 additions & 2 deletions plugins/plugin-chart-echarts/src/BoxPlot/controlPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ export default {
['adhoc_filters'],
emitFilterControl,
['groupby'],
['columns'],
['limit'],
['columns'], // this should be migrated to `series_columns`
villebro marked this conversation as resolved.
Show resolved Hide resolved
['series_limit'],
['series_limit_metric'],
[
{
name: 'whiskerOptions',
Expand Down
17 changes: 9 additions & 8 deletions plugins/plugin-chart-echarts/test/BoxPlot/buildQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,31 @@ import { BoxPlotQueryFormData } from '../../src/BoxPlot/types';

describe('BoxPlot buildQuery', () => {
const formData: BoxPlotQueryFormData = {
emitFilter: false,
columns: [],
datasource: '5__table',
granularity_sqla: 'ds',
time_grain_sqla: 'P1Y',
columns: [],
metrics: ['foo'],
groupby: ['bar'],
metrics: ['foo'],
time_grain_sqla: 'P1Y',
viz_type: 'my_chart',
whiskerOptions: 'Tukey',
yAxisFormat: 'SMART_NUMBER',
viz_type: 'my_chart',
};

it('should build timeseries when columns is empty', () => {
it('should build timeseries when series columns is empty', () => {
const queryContext = buildQuery(formData);
const [query] = queryContext.queries;
expect(query.is_timeseries).toEqual(true);
expect(query.metrics).toEqual(['foo']);
expect(query.columns).toEqual(['bar']);
expect(query.columns).toEqual(['ds', 'bar']);
expect(query.series_columns).toEqual(['bar']);
});

it('should build non-timeseries query object when columns is defined', () => {
const queryContext = buildQuery({ ...formData, columns: ['qwerty'] });
const [query] = queryContext.queries;
expect(query.is_timeseries).toEqual(false);
expect(query.metrics).toEqual(['foo']);
expect(query.columns).toEqual(['qwerty', 'bar']);
expect(query.series_columns).toEqual(['bar']);
});
});