Skip to content

Commit 6f24567

Browse files
committed
initial : server side filters implementation
1 parent 06e18fe commit 6f24567

File tree

5 files changed

+437
-0
lines changed

5 files changed

+437
-0
lines changed

superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTable/index.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
CellClickedEvent,
4040
IMenuActionParams,
4141
} from '@superset-ui/core/components/ThemedAgGridReact';
42+
import type { FilterChangedEvent } from 'ag-grid-community';
4243
import { type FunctionComponent } from 'react';
4344
import { JsonObject, DataRecordValue, DataRecord, t } from '@superset-ui/core';
4445
import { SearchOutlined } from '@ant-design/icons';
@@ -48,6 +49,11 @@ import SearchSelectDropdown from './components/SearchSelectDropdown';
4849
import { SearchOption, SortByItem } from '../types';
4950
import getInitialSortState, { shouldSort } from '../utils/getInitialSortState';
5051
import { PAGE_SIZE_OPTIONS } from '../consts';
52+
import {
53+
convertAgGridFiltersToSQL,
54+
logFilterConversion,
55+
type AgGridFilterModel,
56+
} from '../utils/agGridFilterConverter';
5157

5258
export interface AgGridTableProps {
5359
gridTheme?: string;
@@ -70,6 +76,7 @@ export interface AgGridTableProps {
7076
onSearchColChange: (searchCol: string) => void;
7177
onSearchChange: (searchText: string) => void;
7278
onSortChange: (sortBy: SortByItem[]) => void;
79+
onAgGridColumnFiltersChange?: (filterModel: AgGridFilterModel) => void;
7380
id: number;
7481
percentMetrics: string[];
7582
serverPageLength: number;
@@ -104,6 +111,7 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
104111
onSearchColChange,
105112
onSearchChange,
106113
onSortChange,
114+
onAgGridColumnFiltersChange,
107115
id,
108116
percentMetrics,
109117
serverPageLength,
@@ -153,6 +161,11 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
153161
serverPaginationData?.searchText || '',
154162
);
155163

164+
// State to store column-level filters - initialize from ownState if available
165+
const [columnFilters, setColumnFilters] = useState<any>(
166+
serverPaginationData?.agGridFilterModel || {},
167+
);
168+
156169
const debouncedSearch = useMemo(
157170
() =>
158171
debounce((value: string) => {
@@ -254,11 +267,75 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
254267
}
255268
}, [width]);
256269

270+
// Restore AG Grid filter state from ownState (similar to search box pattern)
271+
useEffect(() => {
272+
if (gridRef.current?.api && serverPagination) {
273+
const storedFilterModel = serverPaginationData?.agGridFilterModel;
274+
275+
if (storedFilterModel && Object.keys(storedFilterModel).length > 0) {
276+
// Only restore if the current filter model is different
277+
const currentFilterModel = gridRef.current.api.getFilterModel();
278+
279+
if (!isEqual(currentFilterModel, storedFilterModel)) {
280+
console.log('Restoring AG Grid filters from ownState:', storedFilterModel);
281+
gridRef.current.api.setFilterModel(storedFilterModel);
282+
}
283+
} else if (Object.keys(columnFilters).length > 0) {
284+
// Clear filters if ownState has no filters but local state does
285+
console.log('Clearing AG Grid filters (ownState is empty)');
286+
gridRef.current.api.setFilterModel(null);
287+
setColumnFilters({});
288+
}
289+
}
290+
}, [serverPaginationData?.agGridFilterModel, serverPagination]);
291+
257292
const onGridReady = (params: GridReadyEvent) => {
258293
// This will make columns fill the grid width
259294
params.api.sizeColumnsToFit();
295+
296+
// Restore filter state on grid ready if server pagination is enabled
297+
if (serverPagination && serverPaginationData?.agGridFilterModel) {
298+
const storedFilterModel = serverPaginationData.agGridFilterModel;
299+
if (Object.keys(storedFilterModel).length > 0) {
300+
console.log('Restoring AG Grid filters on grid ready:', storedFilterModel);
301+
params.api.setFilterModel(storedFilterModel);
302+
}
303+
}
260304
};
261305

306+
// Handler for column filter changes
307+
const onFilterChanged = useCallback((event: FilterChangedEvent) => {
308+
const filterModel = event.api.getFilterModel();
309+
console.log('Column Filters Changed:', filterModel);
310+
311+
// Only trigger API call if filters actually changed (deep comparison)
312+
// This prevents infinite loops when restoring filters from ownState
313+
if (isEqual(filterModel, serverPaginationData?.agGridFilterModel)) {
314+
console.log('Filter model unchanged - skipping API call');
315+
return;
316+
}
317+
318+
// Convert AG Grid filters to SQLAlchemy format
319+
const convertedFilters = convertAgGridFiltersToSQL(
320+
filterModel as AgGridFilterModel,
321+
);
322+
323+
// Log the conversion for debugging
324+
logFilterConversion(filterModel as AgGridFilterModel, convertedFilters);
325+
326+
setColumnFilters(filterModel);
327+
328+
// Call the handler to update ownState if server pagination is enabled
329+
if (onAgGridColumnFiltersChange && serverPagination) {
330+
onAgGridColumnFiltersChange(filterModel as AgGridFilterModel);
331+
}
332+
}, [onAgGridColumnFiltersChange, serverPagination, serverPaginationData?.agGridFilterModel]);
333+
334+
// Log filter state whenever it changes
335+
useEffect(() => {
336+
console.log('Current Filter State:', columnFilters);
337+
}, [columnFilters]);
338+
262339
return (
263340
<div style={containerStyles} ref={containerRef}>
264341
<div className="dropdown-controls-container">
@@ -303,6 +380,7 @@ const AgGridDataTable: FunctionComponent<AgGridTableProps> = memo(
303380
<ThemedAgGridReact
304381
ref={gridRef}
305382
onGridReady={onGridReady}
383+
onFilterChanged={onFilterChanged}
306384
className="ag-container"
307385
rowData={rowData}
308386
headerHeight={36}

superset-frontend/plugins/plugin-chart-ag-grid-table/src/AgGridTableChart.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import TimeComparisonVisibility from './AgGridTable/components/TimeComparisonVis
4242
import { useColDefs } from './utils/useColDefs';
4343
import { getCrossFilterDataMask } from './utils/getCrossFilterDataMask';
4444
import { StyledChartContainer } from './styles';
45+
import { convertAgGridFiltersToSQL, type AgGridFilterModel } from './utils/agGridFilterConverter';
4546

4647
const getGridHeight = (height: number, includeSearch: boolean | undefined) => {
4748
let calculatedGridHeight = height;
@@ -250,6 +251,26 @@ export default function TableChart<D extends DataRecord = DataRecord>(
250251
[setDataMask, serverPagination],
251252
);
252253

254+
const handleAgGridColumnFiltersChange = useCallback(
255+
(filterModel: AgGridFilterModel) => {
256+
if (!serverPagination) return;
257+
258+
// Convert AG Grid filters to SQLAlchemy format
259+
const converted = convertAgGridFiltersToSQL(filterModel);
260+
261+
const modifiedOwnState = {
262+
...serverPaginationData,
263+
agGridFilterModel: filterModel, // Store raw filter model for state restoration
264+
agGridSimpleFilters: converted.simpleFilters,
265+
agGridComplexWhere: converted.complexWhere,
266+
currentPage: 0, // Reset to first page when filtering
267+
};
268+
269+
updateTableOwnState(setDataMask, modifiedOwnState);
270+
},
271+
[setDataMask, serverPagination, serverPaginationData],
272+
);
273+
253274
const renderTimeComparisonVisibility = (): JSX.Element => (
254275
<TimeComparisonVisibility
255276
comparisonColumns={comparisonColumns}
@@ -277,6 +298,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
277298
onSearchColChange={handleChangeSearchCol}
278299
onSearchChange={handleSearch}
279300
onSortChange={handleSortByChange}
301+
onAgGridColumnFiltersChange={handleAgGridColumnFiltersChange}
280302
id={slice_id}
281303
handleCrossFilter={toggleFilter}
282304
percentMetrics={percentMetrics}

superset-frontend/plugins/plugin-chart-ag-grid-table/src/buildQuery.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,16 @@ const buildQuery: BuildQuery<TableChartFormData> = (
257257
formData.show_totals &&
258258
queryMode === QueryMode.Aggregate
259259
) {
260+
// Create a copy of extras without the WHERE clause
261+
// AG Grid filters in extras.where can reference calculated columns
262+
// which aren't available in the totals subquery
263+
const totalsExtras = { ...extras };
264+
delete totalsExtras.where; // Remove AG Grid WHERE clause from totals query
265+
260266
extraQueries.push({
261267
...queryObject,
262268
columns: [],
269+
extras: totalsExtras, // Use extras without WHERE clause
263270
row_limit: 0,
264271
row_offset: 0,
265272
post_processing: [],
@@ -290,6 +297,28 @@ const buildQuery: BuildQuery<TableChartFormData> = (
290297
],
291298
};
292299
}
300+
301+
// Add AG Grid column filters from ownState
302+
if (ownState.agGridSimpleFilters && ownState.agGridSimpleFilters.length > 0) {
303+
queryObject = {
304+
...queryObject,
305+
filters: [
306+
...(queryObject.filters || []),
307+
...ownState.agGridSimpleFilters,
308+
],
309+
};
310+
}
311+
312+
// Add AG Grid complex WHERE clause from ownState
313+
if (ownState.agGridComplexWhere) {
314+
queryObject = {
315+
...queryObject,
316+
extras: {
317+
...(queryObject.extras || {}),
318+
where: ownState.agGridComplexWhere,
319+
},
320+
};
321+
}
293322
}
294323

295324
// Now since row limit control is always visible even

superset-frontend/plugins/plugin-chart-ag-grid-table/src/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,21 @@ export type SearchOption = {
132132
label: string;
133133
};
134134

135+
export interface AgGridColumnFilter {
136+
col: string;
137+
op: string;
138+
val: any;
139+
}
140+
135141
export interface ServerPaginationData {
136142
pageSize?: number;
137143
currentPage?: number;
138144
sortBy?: SortByItem[];
139145
searchText?: string;
140146
searchColumn?: string;
147+
agGridFilterModel?: Record<string, any>; // Raw AG Grid filter model for state restoration
148+
agGridSimpleFilters?: AgGridColumnFilter[];
149+
agGridComplexWhere?: string;
141150
}
142151

143152
export interface AgGridTableChartTransformedProps<

0 commit comments

Comments
 (0)