fix: Support JSON keys in dashboard filters (#1271)

This commit is contained in:
Drew Davis 2025-10-16 08:14:57 -04:00 committed by GitHub
parent 2dc0079b08
commit 2d27fe27c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 36 additions and 6 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
fix: Support JSON keys in dashboard filters

View file

@ -1083,7 +1083,6 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
<Tooltip withArrow label="Edit Filters" fz="xs" color="gray">
<Button
variant="outline"
type="submit"
color="gray"
px="xs"
mr={6}

View file

@ -52,6 +52,19 @@ describe('searchFilters', () => {
{ type: 'sql', condition: "a NOT IN ('c')" },
]);
});
it('should wrap keys with toString() when specified', () => {
const filters = {
'json.key': {
included: new Set<string>(['value']),
excluded: new Set<string>(['other value']),
},
};
expect(filtersToQuery(filters, { stringifyKeys: true })).toEqual([
{ type: 'sql', condition: "toString(json.key) IN ('value')" },
{ type: 'sql', condition: "toString(json.key) NOT IN ('other value')" },
]);
});
});
describe('parseQuery', () => {

View file

@ -23,7 +23,10 @@ const useDashboardFilters = (filters: DashboardFilter[]) => {
};
}
return filtersToQuery(filterValues);
return filtersToQuery(
filterValues,
{ stringifyKeys: false }, // Don't wrap keys with toString(), to preserve exact key names in URL query parameters
);
});
},
[setFilterQueries],
@ -42,7 +45,12 @@ const useDashboardFilters = (filters: DashboardFilter[]) => {
return {
valuesForExistingFilters,
queriesForExistingFilters: filtersToQuery(valuesForExistingFilters),
queriesForExistingFilters: filtersToQuery(
valuesForExistingFilters,
// Wrap keys in `toString()` to support JSON/Dynamic-type columns.
// All keys can be stringified, since filter select values are stringified as well.
{ stringifyKeys: true },
),
};
}, [filterQueries, filters]);

View file

@ -11,17 +11,22 @@ export type FilterState = {
};
};
export const filtersToQuery = (filters: FilterState): Filter[] => {
export const filtersToQuery = (
filters: FilterState,
{ stringifyKeys = false }: { stringifyKeys?: boolean } = {},
): Filter[] => {
return Object.entries(filters)
.filter(
([_, values]) => values.included.size > 0 || values.excluded.size > 0,
)
.flatMap(([key, values]) => {
const conditions = [];
const actualKey = stringifyKeys ? `toString(${key})` : key;
if (values.included.size > 0) {
conditions.push({
type: 'sql' as const,
condition: `${key} IN (${Array.from(values.included)
condition: `${actualKey} IN (${Array.from(values.included)
.map(v => `'${v}'`)
.join(', ')})`,
});
@ -29,7 +34,7 @@ export const filtersToQuery = (filters: FilterState): Filter[] => {
if (values.excluded.size > 0) {
conditions.push({
type: 'sql' as const,
condition: `${key} NOT IN (${Array.from(values.excluded)
condition: `${actualKey} NOT IN (${Array.from(values.excluded)
.map(v => `'${v}'`)
.join(', ')})`,
});