feat(ag-grid): Server Side Filtering for Column Level Filters (#35683)

This commit is contained in:
amaannawab923
2026-01-12 19:25:07 +05:30
committed by GitHub
parent 459b4cb23d
commit 4f444ae1d2
20 changed files with 4142 additions and 95 deletions

View File

@@ -22,6 +22,7 @@ import {
ensureIsArray,
getColumnLabel,
getMetricLabel,
isDefined,
isPhysicalColumn,
QueryFormColumn,
QueryFormMetric,
@@ -253,13 +254,23 @@ const buildQuery: BuildQuery<TableChartFormData> = (
};
sortByFromOwnState = sortSource
.map((sortItem: any) => {
const colId = sortItem?.colId || sortItem?.key;
const sortKey = mapColIdToIdentifier(colId);
if (!sortKey) return null;
const isDesc = sortItem?.sort === 'desc' || sortItem?.desc;
return [sortKey, !isDesc] as QueryFormOrderBy;
})
.map(
(sortItem: {
colId?: string | number;
key?: string | number;
sort?: string;
desc?: boolean;
}) => {
const colId = isDefined(sortItem?.colId)
? sortItem.colId
: sortItem?.key;
if (!isDefined(colId)) return null;
const sortKey = mapColIdToIdentifier(String(colId));
if (!sortKey) return null;
const isDesc = sortItem?.sort === 'desc' || sortItem?.desc;
return [sortKey, !isDesc] as QueryFormOrderBy;
},
)
.filter((item): item is QueryFormOrderBy => item !== null);
// Add secondary sort for stable ordering (matches AG Grid's stable sort behavior)
@@ -361,6 +372,8 @@ const buildQuery: BuildQuery<TableChartFormData> = (
...options?.ownState,
currentPage: 0,
pageSize: queryObject.row_limit ?? 0,
lastFilteredColumn: undefined,
lastFilteredInputPosition: undefined,
};
updateTableOwnState(options?.hooks?.setDataMask, modifiedOwnState);
}
@@ -370,21 +383,6 @@ const buildQuery: BuildQuery<TableChartFormData> = (
});
const extraQueries: QueryObject[] = [];
if (
metrics?.length &&
formData.show_totals &&
queryMode === QueryMode.Aggregate
) {
extraQueries.push({
...queryObject,
columns: [],
row_limit: 0,
row_offset: 0,
post_processing: [],
order_desc: undefined, // we don't need orderby stuff here,
orderby: undefined, // because this query will be used for get total aggregation.
});
}
const interactiveGroupBy = formData.extra_form_data?.interactive_groupby;
if (interactiveGroupBy && queryObject.columns) {
@@ -445,6 +443,70 @@ const buildQuery: BuildQuery<TableChartFormData> = (
],
};
}
// Add AG Grid column filters from ownState (non-metric filters only)
if (
ownState.agGridSimpleFilters &&
ownState.agGridSimpleFilters.length > 0
) {
// Get columns that have AG Grid filters
const agGridFilterColumns = new Set(
ownState.agGridSimpleFilters.map(
(filter: { col: string }) => filter.col,
),
);
// Remove existing TEMPORAL_RANGE filters for columns that have new AG Grid filters
// This prevents duplicate filters like "No filter" and actual date ranges
const existingFilters = (queryObject.filters || []).filter(filter => {
// Keep filter if it doesn't have the expected structure
if (!filter || typeof filter !== 'object' || !filter.col) {
return true;
}
// Keep filter if it's not a temporal range filter
if (filter.op !== 'TEMPORAL_RANGE') {
return true;
}
// Remove if this column has an AG Grid filter
return !agGridFilterColumns.has(filter.col);
});
queryObject = {
...queryObject,
filters: [...existingFilters, ...ownState.agGridSimpleFilters],
};
}
// Add AG Grid complex WHERE clause from ownState (non-metric filters)
if (ownState.agGridComplexWhere && ownState.agGridComplexWhere.trim()) {
const existingWhere = queryObject.extras?.where;
const combinedWhere = existingWhere
? `${existingWhere} AND ${ownState.agGridComplexWhere}`
: ownState.agGridComplexWhere;
queryObject = {
...queryObject,
extras: {
...queryObject.extras,
where: combinedWhere,
},
};
}
// Add AG Grid HAVING clause from ownState (metric filters only)
if (ownState.agGridHavingClause && ownState.agGridHavingClause.trim()) {
const existingHaving = queryObject.extras?.having;
const combinedHaving = existingHaving
? `${existingHaving} AND ${ownState.agGridHavingClause}`
: ownState.agGridHavingClause;
queryObject = {
...queryObject,
extras: {
...queryObject.extras,
having: combinedHaving,
},
};
}
}
if (isDownloadQuery) {
@@ -481,6 +543,54 @@ const buildQuery: BuildQuery<TableChartFormData> = (
}
}
// Create totals query AFTER all filters (including AG Grid filters) are applied
// This ensures we can properly exclude AG Grid WHERE filters from the totals
if (
metrics?.length &&
formData.show_totals &&
queryMode === QueryMode.Aggregate
) {
// Create a copy of extras without the AG Grid WHERE clause
// AG Grid filters in extras.where can reference calculated columns
// which aren't available in the totals subquery
const totalsExtras = { ...queryObject.extras };
if (ownState.agGridComplexWhere) {
// Remove AG Grid WHERE clause from totals query
const whereClause = totalsExtras.where;
if (whereClause) {
// Remove the AG Grid filter part from the WHERE clause using string methods
const agGridWhere = ownState.agGridComplexWhere;
let newWhereClause = whereClause;
// Try to remove with " AND " before
newWhereClause = newWhereClause.replace(` AND ${agGridWhere}`, '');
// Try to remove with " AND " after
newWhereClause = newWhereClause.replace(`${agGridWhere} AND `, '');
// If it's the only clause, remove it entirely
if (newWhereClause === agGridWhere) {
newWhereClause = '';
}
if (newWhereClause.trim()) {
totalsExtras.where = newWhereClause;
} else {
delete totalsExtras.where;
}
}
}
extraQueries.push({
...queryObject,
columns: [],
extras: totalsExtras, // Use extras with AG Grid WHERE removed
row_limit: 0,
row_offset: 0,
post_processing: [],
order_desc: undefined, // we don't need orderby stuff here,
orderby: undefined, // because this query will be used for get total aggregation.
});
}
// Now since row limit control is always visible even
// in case of server pagination
// we must use row limit from form data
@@ -506,8 +616,8 @@ const buildQuery: BuildQuery<TableChartFormData> = (
// Use this closure to cache changing of external filters, if we have server pagination we need reset page to 0, after
// external filter changed
export const cachedBuildQuery = (): BuildQuery<TableChartFormData> => {
let cachedChanges: any = {};
const setCachedChanges = (newChanges: any) => {
let cachedChanges: Record<string, unknown> = {};
const setCachedChanges = (newChanges: Record<string, unknown>) => {
cachedChanges = { ...cachedChanges, ...newChanges };
};