Skip to content

Commit 8b59c96

Browse files
committed
feat: initial filter preset impl
1 parent 7c3ee9e commit 8b59c96

File tree

8 files changed

+206
-49
lines changed

8 files changed

+206
-49
lines changed

app/[query]/more/settings.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,22 @@ export const settingsConfig: QueryConfig = {
77
SELECT *
88
FROM system.settings
99
ORDER BY name
10-
`,
10+
`,
1111
columns: ['name', 'value', 'changed', 'description', 'default'],
1212
columnFormats: {
1313
name: ColumnFormat.Code,
1414
changed: ColumnFormat.Boolean,
1515
value: ColumnFormat.Code,
1616
default: ColumnFormat.Code,
1717
},
18+
defaultParams: {
19+
changed: '0',
20+
},
21+
filterParamPresets: [
22+
{
23+
name: 'Changed only',
24+
key: 'changed',
25+
sql: 'changed = 1',
26+
},
27+
],
1828
}

app/[query]/page.tsx

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,52 @@ export default async function Page({
3131
return notFound()
3232
}
3333

34+
// Get valid query params from URL (existing on QueryConfig['defaultParams'])
35+
const validQueryParams = Object.entries(searchParams).filter(([key, _]) => {
36+
return config.defaultParams && config.defaultParams[key] !== undefined
37+
})
38+
const validQueryParamsObj = Object.fromEntries(validQueryParams)
39+
40+
// Filter presets
41+
let condition = []
42+
const searchParamsKeys = ('' + searchParams['__presets'])
43+
.split(',')
44+
.map((key) => key.trim())
45+
.filter((key) => key !== '')
46+
for (const key of searchParamsKeys) {
47+
const preset = config.filterParamPresets?.find(
48+
(preset) => preset.key === key
49+
)
50+
if (preset) {
51+
condition.push(preset.sql)
52+
}
53+
}
54+
55+
let sql = config.sql
56+
if (condition.length > 0) {
57+
// Adding condition to the query after WHERE (if WHERE exists)
58+
// or after FROM (if WHERE doesn't exist)
59+
const whereIndex = sql.indexOf('WHERE')
60+
const fromIndex = sql.indexOf('FROM')
61+
const index = whereIndex !== -1 ? whereIndex : fromIndex
62+
sql =
63+
sql.slice(0, index) +
64+
' WHERE ' +
65+
condition.join(' AND ') +
66+
' AND ' +
67+
sql.slice(index)
68+
}
69+
70+
console.log('========', sql)
71+
3472
// Fetch the data from ClickHouse
3573
try {
3674
const queryParams = {
37-
...searchParams,
3875
...config.defaultParams,
76+
...validQueryParamsObj,
3977
}
4078
const data = await fetchData<RowData[]>({
41-
query: config.sql,
79+
query: sql,
4280
format: 'JSONEachRow',
4381
query_params: queryParams,
4482
})
@@ -47,13 +85,11 @@ export default async function Page({
4785
<div className="flex flex-col">
4886
<RelatedCharts relatedCharts={config.relatedCharts} />
4987

50-
<div>
51-
<DataTable
52-
title={query.replaceAll('-', ' ')}
53-
config={config}
54-
data={data}
55-
/>
56-
</div>
88+
<DataTable
89+
title={query.replaceAll('-', ' ')}
90+
config={config}
91+
data={data}
92+
/>
5793
</div>
5894
)
5995
} catch (error) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { ColumnFormat } from '@/components/data-table/column-defs'
2+
import { type QueryConfig } from '@/lib/types/query-config'
3+
4+
export type Row = {
5+
host: string
6+
readable_total_rows: string
7+
readable_total_bytes: string
8+
readable_primary_key_size: string
9+
readable_marks_bytes: string
10+
part_count: number
11+
last_modification_time: string
12+
}
13+
14+
export const config: QueryConfig = {
15+
name: 'count-in-replicas',
16+
description: 'Count across replicas for all tables in the cluster',
17+
sql: `
18+
SELECT
19+
hostName() AS host,
20+
21+
sum(rows) AS total_rows,
22+
formatReadableQuantity(total_rows) AS readable_total_rows,
23+
(100 * total_rows / max(total_rows) OVER ()) AS pct_total_rows,
24+
25+
sum(bytes) AS total_bytes,
26+
formatReadableSize(sum(bytes)) AS readable_total_bytes,
27+
(100 * total_bytes / max(total_bytes) OVER ()) AS pct_total_bytes,
28+
29+
countDistinct(database) AS database_count,
30+
countDistinct(database || table) AS table_count,
31+
32+
countIf(active) as active_part_count,
33+
(100 * active_part_count / max(active_part_count) OVER ()) AS pct_active_part_count,
34+
35+
count() as all_part_count,
36+
(100 * all_part_count / max(all_part_count) OVER ()) AS pct_all_part_count,
37+
38+
sum(primary_key_size) AS primary_key_size,
39+
formatReadableSize(primary_key_size) AS readable_primary_key_size,
40+
(100 * primary_key_size / max(primary_key_size) OVER ()) AS pct_primary_key_size,
41+
42+
sum(marks_bytes) AS marks_bytes,
43+
formatReadableSize(marks_bytes) AS readable_marks_bytes,
44+
(100 * marks_bytes / max(marks_bytes) OVER ()) AS pct_marks_bytes,
45+
46+
max(modification_time) AS last_modification_time
47+
48+
FROM clusterAllReplicas({cluster: String}, system.parts)
49+
WHERE active
50+
GROUP BY 1
51+
ORDER BY
52+
1 ASC,
53+
2 DESC
54+
`,
55+
columns: [
56+
'host',
57+
'readable_total_rows',
58+
'readable_total_bytes',
59+
'readable_primary_key_size',
60+
'readable_marks_bytes',
61+
'active_part_count',
62+
'all_part_count',
63+
'last_modification_time',
64+
'database_count',
65+
'table_count',
66+
],
67+
columnFormats: {
68+
readable_total_rows: ColumnFormat.BackgroundBar,
69+
readable_total_bytes: ColumnFormat.BackgroundBar,
70+
readable_primary_key_size: ColumnFormat.BackgroundBar,
71+
readable_marks_bytes: ColumnFormat.BackgroundBar,
72+
active_part_count: ColumnFormat.BackgroundBar,
73+
all_part_count: ColumnFormat.BackgroundBar,
74+
database_count: ColumnFormat.BackgroundBar,
75+
table_count: ColumnFormat.BackgroundBar,
76+
},
77+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { DataTable } from '@/components/data-table/data-table'
2+
import { fetchData } from '@/lib/clickhouse'
3+
4+
import { config, type Row } from './config'
5+
6+
interface PageProps {
7+
params: {
8+
replica: string
9+
}
10+
}
11+
12+
export default async function ClustersPage({ params: { replica } }: PageProps) {
13+
const tables = await fetchData<Row[]>({
14+
query: config.sql,
15+
query_params: { replica },
16+
})
17+
18+
return (
19+
<DataTable
20+
title={`Tables in replica - ${replica}`}
21+
config={config}
22+
data={tables}
23+
/>
24+
)
25+
}

components/menu/types.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import type { CountBadgeProps } from '@/components/menu/count-badge'
2-
import type { IconProps } from '@radix-ui/react-icons/dist/types'
3-
import type { LucideProps } from 'lucide-react'
4-
5-
declare const RadixIcon:
6-
| React.ForwardRefExoticComponent<
7-
IconProps & React.RefAttributes<SVGSVGElement>
8-
>
9-
| React.ForwardRefExoticComponent<LucideProps>
2+
import type { Icon } from '@/lib/types/icon'
103

114
export interface MenuItem {
125
title: string
@@ -15,5 +8,5 @@ export interface MenuItem {
158
countSql?: string
169
countVariant?: CountBadgeProps['variant']
1710
items?: MenuItem[]
18-
icon?: typeof RadixIcon
11+
icon?: Icon
1912
}

components/ui/separator.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use client'
2+
3+
import * as SeparatorPrimitive from '@radix-ui/react-separator'
4+
import * as React from 'react'
5+
6+
import { cn } from '@/lib/utils'
7+
8+
const Separator = React.forwardRef<
9+
React.ElementRef<typeof SeparatorPrimitive.Root>,
10+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
11+
>(
12+
(
13+
{ className, orientation = 'horizontal', decorative = true, ...props },
14+
ref
15+
) => (
16+
<SeparatorPrimitive.Root
17+
ref={ref}
18+
decorative={decorative}
19+
orientation={orientation}
20+
className={cn(
21+
'shrink-0 bg-border',
22+
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
23+
className
24+
)}
25+
{...props}
26+
/>
27+
)
28+
)
29+
Separator.displayName = SeparatorPrimitive.Root.displayName
30+
31+
export { Separator }

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@radix-ui/react-navigation-menu": "^1.1.4",
3030
"@radix-ui/react-popover": "^1.0.7",
3131
"@radix-ui/react-select": "^2.0.0",
32+
"@radix-ui/react-separator": "^1.0.3",
3233
"@radix-ui/react-slot": "^1.0.2",
3334
"@radix-ui/react-tabs": "^1.0.4",
3435
"@radix-ui/react-toast": "^1.1.5",
@@ -73,7 +74,7 @@
7374
"prettier-plugin-organize-imports": "^3.2.4",
7475
"prettier-plugin-tailwindcss": "^0.5.12",
7576
"start-server-and-test": "^2.0.3",
76-
"tailwindcss": "^3.4.1",
77+
"tailwindcss": "^3.4.3",
7778
"typescript": "^5"
7879
}
7980
}

yarn.lock

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,14 @@
911911
aria-hidden "^1.1.1"
912912
react-remove-scroll "2.5.5"
913913

914+
"@radix-ui/react-separator@^1.0.3":
915+
version "1.0.3"
916+
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
917+
integrity sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==
918+
dependencies:
919+
"@babel/runtime" "^7.13.10"
920+
"@radix-ui/react-primitive" "1.0.3"
921+
914922
"@radix-ui/react-slot@1.0.2", "@radix-ui/react-slot@^1.0.2":
915923
version "1.0.2"
916924
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab"
@@ -5024,16 +5032,7 @@ streamsearch@^1.1.0:
50245032
resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
50255033
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
50265034

5027-
"string-width-cjs@npm:string-width@^4.2.0":
5028-
version "4.2.3"
5029-
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
5030-
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
5031-
dependencies:
5032-
emoji-regex "^8.0.0"
5033-
is-fullwidth-code-point "^3.0.0"
5034-
strip-ansi "^6.0.1"
5035-
5036-
string-width@^4.1.0, string-width@^4.2.0:
5035+
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0:
50375036
version "4.2.3"
50385037
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
50395038
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -5093,14 +5092,7 @@ string.prototype.trimstart@^1.0.7:
50935092
define-properties "^1.2.0"
50945093
es-abstract "^1.22.1"
50955094

5096-
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
5097-
version "6.0.1"
5098-
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
5099-
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
5100-
dependencies:
5101-
ansi-regex "^5.0.1"
5102-
5103-
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
5095+
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
51045096
version "6.0.1"
51055097
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
51065098
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -5202,7 +5194,7 @@ tailwindcss-animate@^1.0.7:
52025194
resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4"
52035195
integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
52045196

5205-
tailwindcss@^3.4.1:
5197+
tailwindcss@^3.4.3:
52065198
version "3.4.3"
52075199
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519"
52085200
integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==
@@ -5607,7 +5599,8 @@ which@^2.0.1:
56075599
dependencies:
56085600
isexe "^2.0.0"
56095601

5610-
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
5602+
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
5603+
name wrap-ansi-cjs
56115604
version "7.0.0"
56125605
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
56135606
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -5625,15 +5618,6 @@ wrap-ansi@^6.2.0:
56255618
string-width "^4.1.0"
56265619
strip-ansi "^6.0.0"
56275620

5628-
wrap-ansi@^7.0.0:
5629-
version "7.0.0"
5630-
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
5631-
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
5632-
dependencies:
5633-
ansi-styles "^4.0.0"
5634-
string-width "^4.1.0"
5635-
strip-ansi "^6.0.0"
5636-
56375621
wrap-ansi@^8.1.0:
56385622
version "8.1.0"
56395623
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"

0 commit comments

Comments
 (0)