From 45ad47cc6669af5a00259bd0c32119e3bd3f6829 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Fri, 26 Jan 2024 02:59:23 -0800 Subject: [PATCH] allow segment table to sort on start and end when grouped (#15720) --- .../src/react-table/react-table-extra.scss | 4 + .../__snapshots__/segments-view.spec.tsx.snap | 1 + .../src/views/segments-view/segments-view.tsx | 84 ++++++++++--------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/web-console/src/react-table/react-table-extra.scss b/web-console/src/react-table/react-table-extra.scss index bdeecf5e964..e1c65d5143f 100644 --- a/web-console/src/react-table/react-table-extra.scss +++ b/web-console/src/react-table/react-table-extra.scss @@ -26,6 +26,10 @@ .rt-td.padded, .rt-expandable { padding: $table-cell-v-padding $table-cell-h-padding; + + .default-aggregated { + padding: 0; + } } } diff --git a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap index aa62a576f54..66884a5d651 100755 --- a/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap +++ b/web-console/src/views/segments-view/__snapshots__/segments-view.spec.tsx.snap @@ -218,6 +218,7 @@ exports[`SegmentsView matches snapshot 1`] = ` "width": 100, }, Object { + "Aggregated": [Function], "Cell": [Function], "Header": "Shard spec", "accessor": "shard_spec", diff --git a/web-console/src/views/segments-view/segments-view.tsx b/web-console/src/views/segments-view/segments-view.tsx index 36dd94e4263..ae40a8d641e 100644 --- a/web-console/src/views/segments-view/segments-view.tsx +++ b/web-console/src/views/segments-view/segments-view.tsx @@ -56,6 +56,7 @@ import { Api } from '../../singletons'; import type { NumberLike } from '../../utils'; import { compact, + countBy, deepGet, filterMap, formatBytes, @@ -64,6 +65,7 @@ import { isNumberLikeNaN, LocalStorageBackedVisibility, LocalStorageKeys, + oneOf, queryDruidSql, QueryManager, QueryState, @@ -137,6 +139,11 @@ interface Sorted { desc: boolean; } +function sortedToOrderByClause(sorted: Sorted[]): string | undefined { + if (!sorted.length) return; + return 'ORDER BY ' + sorted.map(sort => `${C(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`).join(', '); +} + interface TableState { page: number; pageSize: number; @@ -315,9 +322,9 @@ END AS "time_span"`, let queryParts: string[]; - let whereClause = ''; + let filterClause = ''; if (whereParts.length) { - whereClause = SqlExpression.and(...whereParts).toString(); + filterClause = SqlExpression.and(...whereParts).toString(); } let effectiveSorted = sorted; @@ -331,61 +338,45 @@ END AS "time_span"`, ]); } + const base = SegmentsView.baseQuery(visibleColumns); + const orderByClause = sortedToOrderByClause(effectiveSorted); + if (groupByInterval) { const innerQuery = compact([ - `SELECT "start" || '/' || "end" AS "interval"`, + `SELECT "start", "end"`, `FROM sys.segments`, - whereClause ? `WHERE ${whereClause}` : undefined, - `GROUP BY 1`, - `ORDER BY 1 DESC`, + filterClause ? `WHERE ${filterClause}` : undefined, + `GROUP BY 1, 2`, + sortedToOrderByClause(sorted.filter(sort => oneOf(sort.id, 'start', 'end'))) || + `ORDER BY 1 DESC`, `LIMIT ${pageSize}`, page ? `OFFSET ${page * pageSize}` : undefined, ]).join('\n'); const intervals: string = (await queryDruidSql({ query: innerQuery })) - .map(row => `'${row.interval}'`) + .map(({ start, end }) => `'${start}/${end}'`) .join(', '); queryParts = compact([ - SegmentsView.baseQuery(visibleColumns), + base, `SELECT "start" || '/' || "end" AS "interval", *`, `FROM s`, `WHERE`, intervals ? ` ("start" || '/' || "end") IN (${intervals})` : 'FALSE', - whereClause ? ` AND ${whereClause}` : '', + filterClause ? ` AND ${filterClause}` : '', + orderByClause, + `LIMIT ${pageSize * 1000}`, ]); - - if (effectiveSorted.length) { - queryParts.push( - 'ORDER BY ' + - effectiveSorted - .map(sort => `${C(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`) - .join(', '), - ); - } - - queryParts.push(`LIMIT ${pageSize * 1000}`); } else { - queryParts = [SegmentsView.baseQuery(visibleColumns), `SELECT *`, `FROM s`]; - - if (whereClause) { - queryParts.push(`WHERE ${whereClause}`); - } - - if (effectiveSorted.length) { - queryParts.push( - 'ORDER BY ' + - effectiveSorted - .map(sort => `${C(sort.id)} ${sort.desc ? 'DESC' : 'ASC'}`) - .join(', '), - ); - } - - queryParts.push(`LIMIT ${pageSize}`); - - if (page) { - queryParts.push(`OFFSET ${page * pageSize}`); - } + queryParts = compact([ + base, + `SELECT *`, + `FROM s`, + filterClause ? `WHERE ${filterClause}` : undefined, + orderByClause, + `LIMIT ${pageSize}`, + page ? `OFFSET ${page * pageSize}` : undefined, + ]); } const sqlQuery = queryParts.join('\n'); setIntermediateQuery(sqlQuery); @@ -762,6 +753,19 @@ END AS "time_span"`, ); } }, + Aggregated: opt => { + const { subRows } = opt; + const previewValues = filterMap(subRows, row => row['shard_spec'].type); + const previewCount = countBy(previewValues); + return ( +
+ {Object.keys(previewCount) + .sort() + .map(v => `${v} (${previewCount[v]})`) + .join(', ')} +
+ ); + }, }, { Header: 'Partition',