diff --git a/ui/lib/apps/SlowQuery/pages/Detail/index.tsx b/ui/lib/apps/SlowQuery/pages/Detail/index.tsx index 5ffe5a5650..1f47b2e777 100644 --- a/ui/lib/apps/SlowQuery/pages/Detail/index.tsx +++ b/ui/lib/apps/SlowQuery/pages/Detail/index.tsx @@ -1,25 +1,25 @@ import React from 'react' import { Space } from 'antd' import { useTranslation } from 'react-i18next' -import { useLocation, Link } from 'react-router-dom' +import { Link, useLocation } from 'react-router-dom' import { ArrowLeftOutlined } from '@ant-design/icons' -import { useToggle } from '@umijs/hooks' +import { useLocalStorageState } from '@umijs/hooks' import client from '@lib/client' import { useClientRequest } from '@lib/utils/useClientRequest' -import { parseQueryFn, buildQueryFn } from '@lib/utils/query' +import { buildQueryFn, parseQueryFn } from '@lib/utils/query' import formatSql from '@lib/utils/formatSql' import { - Head, - Descriptions, - TextWithInfo, - Pre, - HighlightSQL, - Expand, - CopyLink, - CardTabs, AnimatedSkeleton, + CardTabs, + CopyLink, + Descriptions, ErrorBar, + Expand, + Head, + HighlightSQL, + Pre, + TextWithInfo, } from '@lib/components' import TabBasic from './DetailTabBasic' import TabTime from './DetailTabTime' @@ -32,6 +32,8 @@ export interface IPageQuery { timestamp?: number } +const SLOW_QUERY_DETAIL_EXPAND = 'slow_query.detail_expand' + function DetailPage() { const query = DetailPage.parseQuery(useLocation().search) @@ -48,11 +50,21 @@ function DetailPage() { ) ) - const { state: sqlExpanded, toggle: toggleSqlExpanded } = useToggle(false) - const { state: prevSqlExpanded, toggle: togglePrevSqlExpanded } = useToggle( - false + const [detailExpand, setDetailExpand] = useLocalStorageState( + SLOW_QUERY_DETAIL_EXPAND, + { + prev_query: false, + query: false, + plan: false, + } ) - const { state: planExpanded, toggle: togglePlanExpanded } = useToggle(false) + + const togglePrevQuery = () => + setDetailExpand((prev) => ({ ...prev, prev_query: !prev.prev_query })) + const toggleQuery = () => + setDetailExpand((prev) => ({ ...prev, query: !prev.query })) + const togglePlan = () => + setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) return (
@@ -71,20 +83,20 @@ function DetailPage() { toggleSqlExpanded()} + expanded={detailExpand.query} + onClick={toggleQuery} /> } > } @@ -97,20 +109,20 @@ function DetailPage() { return ( togglePrevSqlExpanded()} + expanded={detailExpand.prev_query} + onClick={togglePrevQuery} /> } > } @@ -122,19 +134,19 @@ function DetailPage() { })()} togglePlanExpanded()} + expanded={detailExpand.plan} + onClick={togglePlan} /> } > - +
{data.plan}
diff --git a/ui/lib/apps/SlowQuery/utils/tableColumns.tsx b/ui/lib/apps/SlowQuery/utils/tableColumns.tsx index d3ca0e48bc..72e11399b3 100644 --- a/ui/lib/apps/SlowQuery/utils/tableColumns.tsx +++ b/ui/lib/apps/SlowQuery/utils/tableColumns.tsx @@ -18,6 +18,21 @@ function ResultStatusBadge({ status }: { status: 'success' | 'error' }) { ////////////////////////////////////////// const TRANS_KEY_PREFIX = 'slow_query.fields' +export const derivedFields = { + cop_proc_avg: [ + { tooltipPrefix: 'mean', fieldName: 'cop_proc_avg' }, + { tooltipPrefix: 'max', fieldName: 'cop_proc_max' }, + { tooltipPrefix: 'p90', fieldName: 'cop_proc_p90' }, + ], + cop_wait_avg: [ + { tooltipPrefix: 'mean', fieldName: 'cop_wait_avg' }, + { tooltipPrefix: 'max', fieldName: 'cop_wait_max' }, + { tooltipPrefix: 'p90', fieldName: 'cop_wait_p90' }, + ], +} + +////////////////////////////////////////// + export function slowQueryColumns( rows: SlowquerySlowQuery[], showFullSQL?: boolean @@ -73,28 +88,8 @@ export function slowQueryColumns( tcf.bar.single('commit_backoff_time', 'ns', rows), tcf.bar.single('resolve_lock_time', 'ns', rows), // cop - tcf.bar.multiple( - { - bars: [ - { mean: 'cop_proc_avg' }, - { max: 'cop_proc_max' }, - { p90: 'cop_proc_p90' }, - ], - }, - 'ns', - rows - ), - tcf.bar.multiple( - { - bars: [ - { mean: 'cop_wait_avg' }, - { max: 'cop_wait_avg' }, - { p90: 'cop_wait_avg' }, - ], - }, - 'ns', - rows - ), + tcf.bar.multiple({ sources: derivedFields.cop_proc_avg }, 'ns', rows), + tcf.bar.multiple({ sources: derivedFields.cop_wait_avg }, 'ns', rows), // transaction tcf.bar.single('write_keys', 'short', rows), tcf.bar.single('write_size', 'bytes', rows), diff --git a/ui/lib/apps/SlowQuery/utils/useSlowQueryTableController.ts b/ui/lib/apps/SlowQuery/utils/useSlowQueryTableController.ts index 9e35fdc9f2..dcb4370bc7 100644 --- a/ui/lib/apps/SlowQuery/utils/useSlowQueryTableController.ts +++ b/ui/lib/apps/SlowQuery/utils/useSlowQueryTableController.ts @@ -6,7 +6,7 @@ import client, { ErrorStrategy, SlowquerySlowQuery } from '@lib/client' import { calcTimeRange, TimeRange, IColumnKeys } from '@lib/components' import useOrderState, { IOrderOptions } from '@lib/utils/useOrderState' -import { slowQueryColumns } from './tableColumns' +import { derivedFields, slowQueryColumns } from './tableColumns' import { getSelectedFields } from '@lib/utils/tableColumnFactory' export const DEF_SLOW_QUERY_COLUMN_KEYS: IColumnKeys = { @@ -123,18 +123,16 @@ export default function useSlowQueryTableController( querySchemas() }, []) - // Notice: slowQueries, tableColumns, selectedFields make loop dependencies + const selectedFields = useMemo( + () => getSelectedFields(visibleColumnKeys, derivedFields).join(','), + [visibleColumnKeys] + ) + const tableColumns = useMemo( () => slowQueryColumns(slowQueries, showFullSQL), [slowQueries, showFullSQL] ) - // make selectedFields as a string instead of an array to avoid infinite loop - // I have verified that it will cause infinite loop if we return selectedFields as an array - // so it is better to use the basic type (string, number...) instead of object as the dependency - const selectedFields = useMemo( - () => getSelectedFields(visibleColumnKeys, tableColumns).join(','), - [visibleColumnKeys, tableColumns] - ) + useEffect(() => { async function getSlowQueryList() { setLoadingSlowQueries(true) diff --git a/ui/lib/apps/Statement/pages/Detail/PlanDetail.tsx b/ui/lib/apps/Statement/pages/Detail/PlanDetail.tsx index ef16819a97..b686c63345 100644 --- a/ui/lib/apps/Statement/pages/Detail/PlanDetail.tsx +++ b/ui/lib/apps/Statement/pages/Detail/PlanDetail.tsx @@ -1,18 +1,18 @@ import React from 'react' import { Space } from 'antd' -import { useToggle } from '@umijs/hooks' +import { useLocalStorageState } from '@umijs/hooks' import { useTranslation } from 'react-i18next' import { + AnimatedSkeleton, Card, - Descriptions, - HighlightSQL, - TextWithInfo, - Pre, CardTabs, - Expand, CopyLink, - AnimatedSkeleton, + Descriptions, ErrorBar, + Expand, + HighlightSQL, + Pre, + TextWithInfo, } from '@lib/components' import { useClientRequest } from '@lib/utils/useClientRequest' import client from '@lib/client' @@ -34,6 +34,8 @@ export interface IPlanDetailProps { query: IQuery } +const STMT_DETAIL_PLAN_EXPAND = 'statement.detail_plan_expand' + function PlanDetail({ query }: IPlanDetailProps) { const { t } = useTranslation() const { data, isLoading, error } = useClientRequest((reqConfig) => @@ -48,11 +50,22 @@ function PlanDetail({ query }: IPlanDetailProps) { reqConfig ) ) - const { state: sqlExpanded, toggle: toggleSqlExpanded } = useToggle(false) - const { state: prevSqlExpanded, toggle: togglePrevSqlExpanded } = useToggle( - false + + const [detailExpand, setDetailExpand] = useLocalStorageState( + STMT_DETAIL_PLAN_EXPAND, + { + prev_query: false, + query: false, + plan: false, + } ) - const { state: planExpanded, toggle: togglePlanExpanded } = useToggle(false) + + const togglePrevQuery = () => + setDetailExpand((prev) => ({ ...prev, prev_query: !prev.prev_query })) + const toggleQuery = () => + setDetailExpand((prev) => ({ ...prev, query: !prev.query })) + const togglePlan = () => + setDetailExpand((prev) => ({ ...prev, plan: !prev.plan })) let title_key if (query.allPlans === 1) { @@ -62,7 +75,6 @@ function PlanDetail({ query }: IPlanDetailProps) { } else { title_key = 'some' } - return ( toggleSqlExpanded()} + expanded={detailExpand.query} + onClick={toggleQuery} /> } > } @@ -100,20 +112,20 @@ function PlanDetail({ query }: IPlanDetailProps) { {data.prev_sample_text ? ( togglePrevSqlExpanded()} + expanded={detailExpand.prev_query} + onClick={togglePrevQuery} /> } > } @@ -124,19 +136,19 @@ function PlanDetail({ query }: IPlanDetailProps) { ) : null} togglePlanExpanded()} + expanded={detailExpand.plan} + onClick={togglePlan} /> } > - +
{data.plan}
diff --git a/ui/lib/apps/Statement/pages/Detail/index.tsx b/ui/lib/apps/Statement/pages/Detail/index.tsx index 858747e8b0..8f97175764 100644 --- a/ui/lib/apps/Statement/pages/Detail/index.tsx +++ b/ui/lib/apps/Statement/pages/Detail/index.tsx @@ -5,7 +5,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Link, useLocation } from 'react-router-dom' import { ArrowLeftOutlined } from '@ant-design/icons' -import { useToggle } from '@umijs/hooks' +import { useLocalStorageState } from '@umijs/hooks' import client, { StatementModel } from '@lib/client' import { @@ -13,11 +13,11 @@ import { CardTable, DateTime, Descriptions, + ErrorBar, Expand, Head, HighlightSQL, TextWithInfo, - ErrorBar, } from '@lib/components' import CopyLink from '@lib/components/CopyLink' import formatSql from '@lib/utils/formatSql' @@ -34,6 +34,8 @@ export interface IPageQuery { endTime?: number } +const STMT_DETAIL_EXPAND = 'statement.detail_expand' + function DetailPage() { const query = DetailPage.parseQuery(useLocation().search) const { data: plans, isLoading, error } = useClientRequest((reqConfig) => @@ -60,7 +62,11 @@ function DetailPage() { }) ) - const { state: sqlExpanded, toggle: toggleSqlExpanded } = useToggle(false) + const [sqlExpanded, setSqlExpanded] = useLocalStorageState( + STMT_DETAIL_EXPAND, + false + ) + const toggleSqlExpanded = () => setSqlExpanded((prev) => !prev) useEffect(() => { if (plans && plans.length > 0) { @@ -91,7 +97,7 @@ function DetailPage() { toggleSqlExpanded()} + onClick={toggleSqlExpanded} /> diff --git a/ui/lib/apps/Statement/utils/tableColumns.tsx b/ui/lib/apps/Statement/utils/tableColumns.tsx index ea901ead38..94bdab4875 100644 --- a/ui/lib/apps/Statement/utils/tableColumns.tsx +++ b/ui/lib/apps/Statement/utils/tableColumns.tsx @@ -10,9 +10,9 @@ import { orange, red } from '@ant-design/colors' import { StatementModel } from '@lib/client' import { Bar, Pre } from '@lib/components' import { - TableColumnFactory, formatVal, - IColumnWithSourceFields, + genDerivedBarSources, + TableColumnFactory, } from '@lib/utils/tableColumnFactory' /////////////////////////////////////// @@ -20,27 +20,76 @@ import { // slow query order list in backend by key of IColumn const TRANS_KEY_PREFIX = 'statement.fields' +export const derivedFields = { + avg_latency: genDerivedBarSources( + 'avg_latency', + 'max_latency', + 'min_latency' + ), + parse_latency: genDerivedBarSources('avg_parse_latency', 'max_parse_latency'), + compile_latency: genDerivedBarSources( + 'avg_compile_latency', + 'max_compile_latency' + ), + process_time: genDerivedBarSources( + 'avg_cop_process_time', + 'max_cop_process_time' + ), + wait_time: genDerivedBarSources('avg_cop_wait_time', 'max_cop_wait_time'), + total_process_time: genDerivedBarSources( + 'avg_process_time', + 'max_process_time' + ), + total_wait_time: genDerivedBarSources('avg_wait_time', 'max_wait_time'), + backoff_time: genDerivedBarSources('avg_backoff_time', 'max_backoff_time'), + avg_write_keys: genDerivedBarSources('avg_write_keys', 'max_write_keys'), + avg_processed_keys: genDerivedBarSources( + 'avg_processed_keys', + 'max_processed_keys' + ), + avg_total_keys: genDerivedBarSources('avg_total_keys', 'max_total_keys'), + prewrite_time: genDerivedBarSources('avg_prewrite_time', 'max_prewrite_time'), + commit_time: genDerivedBarSources('avg_commit_time', 'max_commit_time'), + get_commit_ts_time: genDerivedBarSources( + 'avg_get_commit_ts_time', + 'max_get_commit_ts_time' + ), + commit_backoff_time: genDerivedBarSources( + 'avg_commit_backoff_time', + 'max_commit_backoff_time' + ), + resolve_lock_time: genDerivedBarSources( + 'avg_resolve_lock_time', + 'max_resolve_lock_time' + ), + local_latch_wait_time: genDerivedBarSources( + 'avg_local_latch_wait_time', + 'max_local_latch_wait_time' + ), + avg_write_size: genDerivedBarSources('avg_write_size', 'max_write_size'), + avg_prewrite_regions: genDerivedBarSources( + 'avg_prewrite_regions', + 'max_prewrite_regions' + ), + avg_txn_retry: genDerivedBarSources('avg_txn_retry', 'max_txn_retry'), + avg_mem: genDerivedBarSources('avg_mem', 'max_mem'), + sum_errors: ['sum_errors', 'sum_warnings'], + related_schemas: ['table_names'], +} + +////////////////////////////////////////// + function avgMinMaxLatencyColumn( tcf: TableColumnFactory, rows?: { max_latency?: number; min_latency?: number; avg_latency?: number }[] ): IColumn { - return tcf.bar.multiple( - { - bars: [ - { mean: 'avg_latency' }, - { max: 'max_latency' }, - { min: 'min_latency' }, - ], - }, - 'ns', - rows - ) + return tcf.bar.multiple({ sources: derivedFields.avg_latency }, 'ns', rows) } function errorsWarningsColumn( tcf: TableColumnFactory, rows?: { sum_errors?: number; sum_warnings?: number }[] -): IColumnWithSourceFields { +): IColumn { const capacity = rows ? max(rows.map((v) => v.sum_errors! + v.sum_warnings!)) ?? 0 : 0 @@ -49,7 +98,6 @@ function errorsWarningsColumn( name: tcf.columnName('errors_warnings'), key, fieldName: key, - sourceFields: ['sum_errors', 'sum_warnings'], minWidth: 140, maxWidth: 200, columnActionsMode: ColumnActionsMode.clickable, @@ -80,8 +128,6 @@ Warnings: ${warningsFmtVal}` function avgMaxColumn( tcf: TableColumnFactory, - avgKey: keyof T, - maxKey: keyof T, displayTransKey: string, unit: string, rows?: T[] @@ -89,7 +135,7 @@ function avgMaxColumn( return tcf.bar.multiple( { displayTransKey, - bars: [{ mean: avgKey }, { max: maxKey }], + sources: derivedFields[displayTransKey], }, unit, rows @@ -101,7 +147,7 @@ function avgMaxColumn( export function statementColumns( rows: StatementModel[], showFullSQL?: boolean -): IColumnWithSourceFields[] { +): IColumn[] { const tcf = new TableColumnFactory(TRANS_KEY_PREFIX) return [ @@ -116,161 +162,28 @@ export function statementColumns( maxWidth: 300, columnActionsMode: ColumnActionsMode.clickable, }, - avgMaxColumn(tcf, 'avg_mem', 'max_mem', 'avg_mem', 'bytes', rows), + avgMaxColumn(tcf, 'avg_mem', 'bytes', rows), errorsWarningsColumn(tcf, rows), - avgMaxColumn( - tcf, - 'avg_parse_latency', - 'max_parse_latency', - 'parse_latency', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_compile_latency', - 'max_compile_latency', - 'compile_latency', - 'ns', - rows - ), + avgMaxColumn(tcf, 'parse_latency', 'ns', rows), + avgMaxColumn(tcf, 'compile_latency', 'ns', rows), tcf.bar.single('sum_cop_task_num', 'short', rows), - avgMaxColumn( - tcf, - 'avg_cop_process_time', - 'max_cop_process_time', - 'process_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_cop_wait_time', - 'max_cop_wait_time', - 'wait_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_process_time', - 'max_process_time', - 'total_process_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_wait_time', - 'max_wait_time', - 'total_wait_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_backoff_time', - 'max_backoff_time', - 'backoff_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_write_keys', - 'max_write_keys', - 'avg_write_keys', - 'short', - rows - ), - avgMaxColumn( - tcf, - 'avg_processed_keys', - 'max_processed_keys', - 'avg_processed_keys', - 'short', - rows - ), - avgMaxColumn( - tcf, - 'avg_total_keys', - 'max_total_keys', - 'avg_total_keys', - 'short', - rows - ), - avgMaxColumn( - tcf, - 'avg_prewrite_time', - 'max_prewrite_time', - 'prewrite_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_commit_time', - 'max_commit_time', - 'commit_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_get_commit_ts_time', - 'max_get_commit_ts_time', - 'get_commit_ts_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_commit_backoff_time', - 'max_commit_backoff_time', - 'commit_backoff_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_resolve_lock_time', - 'max_resolve_lock_time', - 'resolve_lock_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_local_latch_wait_time', - 'max_local_latch_wait_time', - 'local_latch_wait_time', - 'ns', - rows - ), - avgMaxColumn( - tcf, - 'avg_write_size', - 'max_write_size', - 'avg_write_size', - 'bytes', - rows - ), - avgMaxColumn( - tcf, - 'avg_prewrite_regions', - 'max_prewrite_regions', - 'avg_prewrite_regions', - 'short', - rows - ), - avgMaxColumn( - tcf, - 'avg_txn_retry', - 'max_txn_retry', - 'avg_txn_retry', - 'short', - rows - ), + avgMaxColumn(tcf, 'process_time', 'ns', rows), + avgMaxColumn(tcf, 'wait_time', 'ns', rows), + avgMaxColumn(tcf, 'total_process_time', 'ns', rows), + avgMaxColumn(tcf, 'total_wait_time', 'ns', rows), + avgMaxColumn(tcf, 'backoff_time', 'ns', rows), + avgMaxColumn(tcf, 'avg_write_keys', 'short', rows), + avgMaxColumn(tcf, 'avg_processed_keys', 'short', rows), + avgMaxColumn(tcf, 'avg_total_keys', 'short', rows), + avgMaxColumn(tcf, 'prewrite_time', 'ns', rows), + avgMaxColumn(tcf, 'commit_time', 'ns', rows), + avgMaxColumn(tcf, 'get_commit_ts_time', 'ns', rows), + avgMaxColumn(tcf, 'commit_backoff_time', 'ns', rows), + avgMaxColumn(tcf, 'resolve_lock_time', 'ns', rows), + avgMaxColumn(tcf, 'local_latch_wait_time', 'ns', rows), + avgMaxColumn(tcf, 'avg_write_size', 'bytes', rows), + avgMaxColumn(tcf, 'avg_prewrite_regions', 'short', rows), + avgMaxColumn(tcf, 'avg_txn_retry', 'short', rows), tcf.bar.single('sum_backoff_times', 'short', rows), tcf.bar.single('avg_affected_rows', 'short', rows), @@ -292,7 +205,6 @@ export function statementColumns( ...tcf.textWithTooltip('related_schemas', rows), minWidth: 160, maxWidth: 240, - sourceFields: ['table_names'], }, ] } @@ -309,6 +221,6 @@ export function planColumns(rows: StatementModel[]): IColumn[] { tcf.bar.single('sum_latency', 'ns', rows), avgMinMaxLatencyColumn(tcf, rows), tcf.bar.single('exec_count', 'short', rows), - avgMaxColumn(tcf, 'avg_mem', 'max_mem', 'avg_mem', 'bytes', rows), + avgMaxColumn(tcf, 'avg_mem', 'bytes', rows), ] } diff --git a/ui/lib/apps/Statement/utils/useStatementTableController.ts b/ui/lib/apps/Statement/utils/useStatementTableController.ts index 876549dc02..ddfbf41b0d 100644 --- a/ui/lib/apps/Statement/utils/useStatementTableController.ts +++ b/ui/lib/apps/Statement/utils/useStatementTableController.ts @@ -15,7 +15,7 @@ import { DEFAULT_TIME_RANGE, TimeRange, } from '../pages/List/TimeRangeSelector' -import { statementColumns } from './tableColumns' +import { derivedFields, statementColumns } from './tableColumns' import { getSelectedFields } from '@lib/utils/tableColumnFactory' export const DEF_STMT_COLUMN_KEYS: IColumnKeys = { @@ -174,18 +174,16 @@ export default function useStatementTableController( queryStmtTypes() }, [refreshTimes]) - // Notice: statements, tableColumns, selectedFields make loop dependencies + const selectedFields = useMemo( + () => getSelectedFields(visibleColumnKeys, derivedFields).join(','), + [visibleColumnKeys] + ) + const tableColumns = useMemo( () => statementColumns(statements, showFullSQL), [statements, showFullSQL] ) - // make selectedFields as a string instead of an array to avoid infinite loop - // I have verified that it will cause infinite loop if we return selectedFields as an array - // so it is better to use the basic type (string, number...) instead of object as the dependency - const selectedFields = useMemo( - () => getSelectedFields(visibleColumnKeys, tableColumns).join(','), - [visibleColumnKeys, tableColumns] - ) + useEffect(() => { async function queryStatementList() { if (allTimeRanges.length === 0) { diff --git a/ui/lib/utils/tableColumnFactory.tsx b/ui/lib/utils/tableColumnFactory.tsx index ba43d41ba7..9abb88f56a 100644 --- a/ui/lib/utils/tableColumnFactory.tsx +++ b/ui/lib/utils/tableColumnFactory.tsx @@ -17,15 +17,17 @@ import { IColumnKeys, } from '@lib/components' -type Bar = { [key: string]: keyof T } -type BarsConfig = { +export type DerivedField = { displayTransKey?: string // it is same as avg field name default - bars: [Bar, Bar, Bar?] // [avg, max, min?] + sources: T[] } -export type IColumnWithSourceFields = IColumn & { - sourceFields?: string[] -} +export type DerivedBar = DerivedField<{ + tooltipPrefix: string + fieldName: string +}> + +export type DerivedCol = DerivedField export function formatVal(val: number, unit: string, decimals: number = 1) { const formatFn = getValueFormat(unit) @@ -61,7 +63,7 @@ export class TableColumnFactory { textWithTooltip( fieldName: T, _rows?: U[] - ): IColumnWithSourceFields { + ): IColumn { return { ...this.columnFromField(fieldName), minWidth: 100, @@ -78,7 +80,7 @@ export class TableColumnFactory { fieldName: T, unit: string, rows?: U[] - ): IColumnWithSourceFields { + ): IColumn { const capacity = rows ? _max(rows.map((v) => v[fieldName])) ?? 0 : 0 return { ...this.columnFromField(fieldName), @@ -96,48 +98,27 @@ export class TableColumnFactory { } } - multipleBar( - barsConfig: BarsConfig, - unit: string, - rows?: T[] - ): IColumnWithSourceFields { + multipleBar(barsConfig: DerivedBar, unit: string, rows?: T[]): IColumn { const { displayTransKey, - bars: [avg_, max_, min_], + sources: [avg, max, min], } = barsConfig - const tooltioPrefixLens: number[] = [] - const avg = { - fieldName: Object.values(avg_)[0], - tooltipPrefix: Object.keys(avg_)[0], - } - tooltioPrefixLens.push(avg.tooltipPrefix.length) - const max = { - fieldName: Object.values(max_)[0], - tooltipPrefix: Object.keys(max_)[0], - } - tooltioPrefixLens.push(max.tooltipPrefix.length) - let min - if (min_) { - min = { - fieldName: Object.values(min_)[0], - tooltipPrefix: Object.keys(min_)[0], - } - tooltioPrefixLens.push(min.tooltipPrefix.length) - } else { - min = undefined - } - const maxTooltipPrefixLen = _max(tooltioPrefixLens) || 0 + const tooltipPrefixLens: number[] = [] - const capacity = rows ? _max(rows.map((v) => v[max.fieldName])) ?? 0 : 0 - let sourceFields = [avg.fieldName, max.fieldName] as string[] + tooltipPrefixLens.push(avg.tooltipPrefix.length) + tooltipPrefixLens.push(max.tooltipPrefix.length) if (min) { - sourceFields.push(min.fieldName) + tooltipPrefixLens.push(min.tooltipPrefix.length) } + + const maxTooltipPrefixLen = _max(tooltipPrefixLens) || 0 + + const capacity = rows ? _max(rows.map((v) => v[max.fieldName])) ?? 0 : 0 + return { - ...this.columnFromField(avg.fieldName as string), - name: this.columnName((displayTransKey || avg.fieldName) as string), - sourceFields, + ...this.columnFromField(avg.fieldName), + name: this.columnName(displayTransKey || avg.fieldName), minWidth: 140, maxWidth: 200, columnActionsMode: ColumnActionsMode.clickable, @@ -175,7 +156,7 @@ export class TableColumnFactory { timestamp( fieldName: T, _rows?: U[] - ): IColumnWithSourceFields { + ): IColumn { return { ...this.columnFromField(fieldName), minWidth: 100, @@ -193,7 +174,7 @@ export class TableColumnFactory { fieldName: T, showFullSQL?: boolean, _rows?: U[] - ): IColumnWithSourceFields { + ): IColumn { return { ...this.columnFromField(fieldName), minWidth: 100, @@ -229,26 +210,64 @@ export class BarColumn { return this.factory.singleBar(fieldName, unit, rows) } - multiple(bars: BarsConfig, unit: string, rows?: T[]) { + multiple(bars: DerivedBar, unit: string, rows?: T[]) { return this.factory.multipleBar(bars, unit, rows) } } //////////////////////////////////////////// +export type DerivedFields = Record< + string, + DerivedBar['sources'] | DerivedCol['sources'] +> + +export function genDerivedBarSources( + avg: string, + max: string, + min?: string +): DerivedBar['sources'] { + const res = [ + { + tooltipPrefix: 'mean', + fieldName: avg, + }, + { + tooltipPrefix: 'max', + fieldName: max, + }, + ] + if (min) { + res.push({ + tooltipPrefix: 'min', + fieldName: min, + }) + } + return res +} + +function isDerivedBarSources(v: any): v is DerivedBar['sources'] { + return !!v[0].fieldName +} + export function getSelectedFields( visibleColumnKeys: IColumnKeys, - columns: IColumnWithSourceFields[] + derivedFields: DerivedFields ) { let fields: string[] = [] - columns.forEach((c) => { - if (visibleColumnKeys[c.key] === true) { - if (c.sourceFields !== undefined) { - fields = fields.concat(c.sourceFields) + let sources: DerivedFields[keyof DerivedFields] + for (const columnKey in visibleColumnKeys) { + if (visibleColumnKeys[columnKey]) { + if ((sources = derivedFields[columnKey])) { + if (isDerivedBarSources(sources)) { + fields.push(...sources.map((b) => b.fieldName)) + } else { + fields.push(...sources) + } } else { - fields.push(c.key) + fields.push(columnKey) } } - }) + } return fields }