superset buildQuery 源码
superset buildQuery 代码
文件路径:/superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
AdhocColumn,
buildQueryContext,
ensureIsArray,
getMetricLabel,
hasGenericChartAxes,
isPhysicalColumn,
QueryMode,
QueryObject,
removeDuplicates,
} from '@superset-ui/core';
import { PostProcessingRule } from '@superset-ui/core/src/query/types/PostProcessing';
import { BuildQuery } from '@superset-ui/core/src/chart/registries/ChartBuildQueryRegistrySingleton';
import { TableChartFormData } from './types';
import { updateExternalFormData } from './DataTable/utils/externalAPIs';
/**
* Infer query mode from form data. If `all_columns` is set, then raw records mode,
* otherwise defaults to aggregation mode.
*
* The same logic is used in `controlPanel` with control values as well.
*/
export function getQueryMode(formData: TableChartFormData) {
const { query_mode: mode } = formData;
if (mode === QueryMode.aggregate || mode === QueryMode.raw) {
return mode;
}
const rawColumns = formData?.all_columns;
const hasRawColumns = rawColumns && rawColumns.length > 0;
return hasRawColumns ? QueryMode.raw : QueryMode.aggregate;
}
const buildQuery: BuildQuery<TableChartFormData> = (
formData: TableChartFormData,
options,
) => {
const { percent_metrics: percentMetrics, order_desc: orderDesc = false } =
formData;
const queryMode = getQueryMode(formData);
const sortByMetric = ensureIsArray(formData.timeseries_limit_metric)[0];
let formDataCopy = formData;
// never include time in raw records mode
if (queryMode === QueryMode.raw) {
formDataCopy = {
...formData,
include_time: false,
};
}
return buildQueryContext(formDataCopy, baseQueryObject => {
let { metrics, orderby = [], columns = [] } = baseQueryObject;
let postProcessing: PostProcessingRule[] = [];
if (queryMode === QueryMode.aggregate) {
metrics = metrics || [];
// orverride orderby with timeseries metric when in aggregation mode
if (sortByMetric) {
orderby = [[sortByMetric, !orderDesc]];
} else if (metrics?.length > 0) {
// default to ordering by first metric in descending order
// when no "sort by" metric is set (regargless if "SORT DESC" is set to true)
orderby = [[metrics[0], false]];
}
// add postprocessing for percent metrics only when in aggregation mode
if (percentMetrics && percentMetrics.length > 0) {
const percentMetricLabels = removeDuplicates(
percentMetrics.map(getMetricLabel),
);
metrics = removeDuplicates(
metrics.concat(percentMetrics),
getMetricLabel,
);
postProcessing = [
{
operation: 'contribution',
options: {
columns: percentMetricLabels,
rename_columns: percentMetricLabels.map(x => `%${x}`),
},
},
];
}
columns = columns.map(col => {
if (
isPhysicalColumn(col) &&
formData.time_grain_sqla &&
hasGenericChartAxes &&
formData?.datetime_columns_lookup?.[col]
) {
return {
timeGrain: formData.time_grain_sqla,
columnType: 'BASE_AXIS',
sqlExpression: col,
label: col,
expressionType: 'SQL',
} as AdhocColumn;
}
return col;
});
}
const moreProps: Partial<QueryObject> = {};
const ownState = options?.ownState ?? {};
if (formDataCopy.server_pagination) {
moreProps.row_limit =
ownState.pageSize ?? formDataCopy.server_page_length;
moreProps.row_offset =
(ownState.currentPage ?? 0) * (ownState.pageSize ?? 0);
}
let queryObject = {
...baseQueryObject,
columns,
orderby,
metrics,
post_processing: postProcessing,
...moreProps,
};
if (
formData.server_pagination &&
options?.extras?.cachedChanges?.[formData.slice_id] &&
JSON.stringify(options?.extras?.cachedChanges?.[formData.slice_id]) !==
JSON.stringify(queryObject.filters)
) {
queryObject = { ...queryObject, row_offset: 0 };
updateExternalFormData(
options?.hooks?.setDataMask,
0,
queryObject.row_limit ?? 0,
);
}
// Because we use same buildQuery for all table on the page we need split them by id
options?.hooks?.setCachedChanges({
[formData.slice_id]: queryObject.filters,
});
const extraQueries: QueryObject[] = [];
if (
metrics?.length &&
formData.show_totals &&
queryMode === QueryMode.aggregate
) {
extraQueries.push({
...queryObject,
columns: [],
row_limit: 0,
row_offset: 0,
post_processing: [],
order_desc: undefined, // we don't need orderby stuff here,
orderby: undefined, // because this query will be used for get total aggregation.
});
}
const interactiveGroupBy = formData.extra_form_data?.interactive_groupby;
if (interactiveGroupBy && queryObject.columns) {
queryObject.columns = [
...new Set([...queryObject.columns, ...interactiveGroupBy]),
];
}
if (formData.server_pagination) {
return [
{ ...queryObject },
{
...queryObject,
row_limit: 0,
row_offset: 0,
post_processing: [],
is_rowcount: true,
},
...extraQueries,
];
}
return [queryObject, ...extraQueries];
});
};
// Use this closure to cache changing of external filters, if we have server pagination we need reset page to 0, after
// external filter changed
export const cachedBuildQuery = (): BuildQuery<TableChartFormData> => {
let cachedChanges: any = {};
const setCachedChanges = (newChanges: any) => {
cachedChanges = { ...cachedChanges, ...newChanges };
};
return (formData, options) =>
buildQuery(
{ ...formData },
{
extras: { cachedChanges },
ownState: options?.ownState ?? {},
hooks: {
...options?.hooks,
setDataMask: () => {},
setCachedChanges,
},
},
);
};
export default cachedBuildQuery();
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
7、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦