This is an automated email from the ASF dual-hosted git repository. vogievetsky pushed a commit to branch explore_source in repository https://gitbox.apache.org/repos/asf/druid.git
commit 5ce2abafb787a3c6bf8d748a877585bf7d3db2e3 Author: Vadim Ogievetsky <[email protected]> AuthorDate: Wed Aug 14 14:19:09 2024 -0700 add others --- .../_save/modules/pie-chart-echarts-module.ts | 203 --------------------- web-console/src/modules/pie-chart-module.tsx | 50 +++-- web-console/src/utils/general.tsx | 6 + 3 files changed, 41 insertions(+), 218 deletions(-) diff --git a/web-console/_save/modules/pie-chart-echarts-module.ts b/web-console/_save/modules/pie-chart-echarts-module.ts deleted file mode 100644 index d9aedb16082..00000000000 --- a/web-console/_save/modules/pie-chart-echarts-module.ts +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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 { C, SqlExpression } from '@druid-toolkit/query'; -import { typedVisualModule } from '@druid-toolkit/visuals-core'; -import * as echarts from 'echarts'; - -import { highlightStore } from '../highlight-store/highlight-store'; -import { getInitQuery } from '../utils'; - -/** - * Returns the cartesian coordinates of a pie slice external centroid - */ -function getCentroid(chart: echarts.ECharts, dataIndex: number) { - // see these underscores everywhere? that's because those are private properties - // I have no real choice but to use them, because there is no public API for this (on pie charts) - // #no_ragrets - const layout = (chart as any)._chartsViews?.[0]?._data?._itemLayouts?.[dataIndex]; - - if (!layout) return; - - const { cx, cy, startAngle, endAngle, r } = layout; - const angle = (startAngle + endAngle) / 2; - - const x = cx + Math.cos(angle) * r; - const y = cy + Math.sin(angle) * r; - - return { x, y }; -} - -export default typedVisualModule({ - parameters: { - splitColumn: { - type: 'column', - control: { - label: 'Slice column', - // transferGroup: 'show', - required: true, - }, - }, - metric: { - type: 'aggregate', - default: { expression: SqlExpression.parse('COUNT(*)'), name: 'Count', sqlType: 'BIGINT' }, - control: { - // transferGroup: 'show', - required: true, - }, - }, - limit: { - type: 'number', - default: 5, - control: { - label: 'Max slices to show', - required: true, - }, - }, - showOthers: { - type: 'boolean', - default: true, - control: { label: 'Show others' }, - }, - }, - module: ({ container, host, updateWhere }) => { - const myChart = echarts.init(container, 'dark'); - - myChart.setOption({ - tooltip: { - trigger: 'item', - }, - legend: { - orient: 'vertical', - left: 'left', - }, - series: [ - { - type: 'pie', - id: 'hello', - radius: '50%', - data: [], - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)', - }, - }, - }, - ], - }); - - return { - async update({ table, where, parameterValues }) { - const { splitColumn, metric, limit } = parameterValues; - - if (!splitColumn) return; - - myChart.off('click'); - - const result = await host.sqlQuery( - getInitQuery(table, where) - .addSelect(splitColumn.expression.as('name'), { addToGroupBy: 'end' }) - .addSelect(metric.expression.as('value'), { - addToOrderBy: 'end', - direction: 'DESC', - }) - .changeLimitValue(limit), - ); - - const data = result.toObjectArray(); - - if (parameterValues.showOthers) { - const others = await host.sqlQuery( - getInitQuery( - table, - where.changeClauseInWhere( - C(splitColumn.name).notIn(result.getColumnByIndex(0)!), - ) as SqlExpression, - ).addSelect(metric.expression.as('value')), - ); - - data.push({ name: 'Others', value: others.rows[0][0], __isOthers: true }); - } - - myChart.setOption({ - series: [ - { - id: 'hello', - data, - }, - ], - }); - - myChart.on('click', 'series', p => { - if (highlightStore.getState().highlight?.data.name === p.name) { - highlightStore.getState().dropHighlight(); - return; - } - - const centroid = getCentroid(myChart, p.dataIndex); - - if (!centroid) return; - - const { name, value, __isOthers } = p.data as any; - - highlightStore.getState().setHighlight({ - label: name + ': ' + value, - x: centroid.x, - y: centroid.y - 20, - data: { name, value, dataIndex: p.dataIndex }, - onDrop: () => { - highlightStore.getState().dropHighlight(); - }, - onSave: __isOthers - ? undefined - : () => { - updateWhere(where.toggleClauseInWhere(C(splitColumn.name).equal(name))); - highlightStore.getState().dropHighlight(); - }, - }); - }); - }, - - resize() { - myChart.resize(); - - // if there is a highlight, update its x position - // by calculating new pixel position from the highlight's data - const highlight = highlightStore.getState().highlight; - if (highlight) { - const { dataIndex } = highlight.data; - - const centroid = getCentroid(myChart, dataIndex); - - if (!centroid) return; - - highlightStore.getState().updateHighlight({ - x: centroid.x, - y: centroid.y - 20, - }); - } - }, - - destroy() { - myChart.dispose(); - }, - }; - }, -}); diff --git a/web-console/src/modules/pie-chart-module.tsx b/web-console/src/modules/pie-chart-module.tsx index 7daa2742ec4..fdd32f2ee99 100644 --- a/web-console/src/modules/pie-chart-module.tsx +++ b/web-console/src/modules/pie-chart-module.tsx @@ -22,6 +22,7 @@ import * as echarts from 'echarts'; import React, { useEffect, useMemo, useRef } from 'react'; import { useQueryManager } from '../hooks'; +import { formatEmpty, formatNumber } from '../utils'; import { highlightStore } from '../views/explore-view/highlight-store/highlight-store'; import type { ExpressionMeta } from './models'; @@ -97,26 +98,42 @@ ModuleRepository.registerModule<PieChartParameterValues>({ const { querySource, where, setWhere, parameterValues, stage, runSqlQuery } = props; const chartRef = useRef<ECharts>(); - const { splitColumn, measure, limit } = parameterValues; + const { splitColumn, measure, limit, showOthers } = parameterValues; - const dataQuery = useMemo(() => { + const dataQueries = useMemo(() => { const source = querySource.query; const splitExpression = splitColumn ? splitColumn.expression : L(OVERALL_LABEL); - return SqlQuery.from(source) - .addWhere(where) - .addSelect(splitExpression.as('name'), { addToGroupBy: 'end' }) - .addSelect(measure.expression.as('value'), { - addToOrderBy: 'end', - direction: 'DESC', - }) - .changeLimitValue(limit); - }, [querySource, where, splitColumn, measure, limit]); + return { + mainQuery: SqlQuery.from(source) + .addWhere(where) + .addSelect(splitExpression.as('name'), { addToGroupBy: 'end' }) + .addSelect(measure.expression.as('value'), { + addToOrderBy: 'end', + direction: 'DESC', + }) + .changeLimitValue(limit), + splitExpression: splitColumn?.expression, + othersPartialQuery: showOthers + ? SqlQuery.from(source).addWhere(where).addSelect(measure.expression.as('value')) + : undefined, + }; + }, [querySource, where, splitColumn, measure, limit, showOthers]); const [dataState] = useQueryManager({ - query: dataQuery, - processQuery: async (query: SqlQuery) => { - return (await runSqlQuery(query)).toObjectArray(); + query: dataQueries, + processQuery: async ({ mainQuery, splitExpression, othersPartialQuery }) => { + const result = await runSqlQuery(mainQuery); + const data = result.toObjectArray(); + + if (splitExpression && othersPartialQuery) { + const othersResult = await runSqlQuery( + othersPartialQuery.addWhere(splitExpression.notIn(result.getColumnByIndex(0)!)), + ); + data.push({ name: 'Others', value: othersResult.rows[0][0], __isOthers: true }); + } + + return data; }, }); @@ -144,6 +161,9 @@ ModuleRepository.registerModule<PieChartParameterValues>({ shadowColor: 'rgba(0, 0, 0, 0.5)', }, }, + label: { + formatter: (params: any) => formatEmpty(params.name), + }, }, ], }); @@ -188,7 +208,7 @@ ModuleRepository.registerModule<PieChartParameterValues>({ const { name, value, __isOthers } = p.data as any; highlightStore.getState().setHighlight({ - label: name + ': ' + value, + label: formatEmpty(name) + ': ' + formatNumber(value), x: centroid.x, y: centroid.y - 20, data: { name, value, dataIndex: p.dataIndex }, diff --git a/web-console/src/utils/general.tsx b/web-console/src/utils/general.tsx index 6ccab1077b4..016cee28d34 100644 --- a/web-console/src/utils/general.tsx +++ b/web-console/src/utils/general.tsx @@ -259,6 +259,12 @@ export function uniq(array: readonly string[]): string[] { // ---------------------------- +export function formatEmpty(str: string): string { + return str === '' ? 'empty' : str; +} + +// ---------------------------- + export function formatInteger(n: NumberLike): string { return numeral(n).format('0,0'); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
