Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fifty-garlics-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---

fix: Support JSON keys in dashboard filters
1 change: 0 additions & 1 deletion packages/app/src/DBDashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,6 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
<Tooltip withArrow label="Edit Filters" fz="xs" color="gray">
<Button
variant="outline"
type="submit"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes another issue I noticed - hitting enter anywhere in the dashboard form would open the filters modal.

color="gray"
px="xs"
mr={6}
Expand Down
13 changes: 13 additions & 0 deletions packages/app/src/__tests__/searchFilters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
12 changes: 10 additions & 2 deletions packages/app/src/hooks/useDashboardFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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]);

Expand Down
11 changes: 8 additions & 3 deletions packages/app/src/searchFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,30 @@ 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(', ')})`,
});
}
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(', ')})`,
});
Expand Down
Loading