mirror of
https://github.com/docmost/docmost.git
synced 2026-07-14 03:46:51 +10:00
feat(base): add /bases/rows/count with estimate and capped-exact modes
Row-count display on a filtered view shouldn't force a full COUNT(*) on
every list fetch. New endpoint returns either an EXPLAIN-plan estimate
(default, ~1ms, no execution) or a LIMIT-capped exact count that short-
circuits to `{ capped: true }` once the match set passes EXACT_COUNT_CAP.
Clients call it in parallel with the rows query so the grid still paints
at its own pace.
- DTO + repo.countEstimate/countExact reusing the list predicate shape
- service picks the mode; controller mirrors the list Read ability check
- client hook keyed by filter/search/exact so a "show exact" toggle
doesn't clobber the estimate cache
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
InfiniteData,
|
||||
} from "@tanstack/react-query";
|
||||
import {
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
deleteRows,
|
||||
listRows,
|
||||
reorderRow,
|
||||
countRows,
|
||||
} from "@/features/base/services/base-service";
|
||||
import {
|
||||
IBaseRow,
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
FilterNode,
|
||||
SearchSpec,
|
||||
ViewSortConfig,
|
||||
CountRowsResult,
|
||||
} from "@/features/base/types/base.types";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { queryClient } from "@/main";
|
||||
@@ -277,6 +280,35 @@ export function useDeleteRowsMutation() {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Row count for the current view. Fires in parallel with `useBaseRowsQuery`
|
||||
* — doesn't block first paint. Keyed by filter + search so an independent
|
||||
* view with a different filter gets its own cached count; `exact` is part
|
||||
* of the key so a "show exact" toggle doesn't clobber the estimate cache.
|
||||
*/
|
||||
export function useBaseRowsCountQuery(
|
||||
baseId: string | undefined,
|
||||
filter?: FilterNode,
|
||||
search?: SearchSpec,
|
||||
exact = false,
|
||||
) {
|
||||
const activeFilter = normalizeFilter(filter);
|
||||
const activeSearch = search?.query ? search : undefined;
|
||||
|
||||
return useQuery<CountRowsResult>({
|
||||
queryKey: ["base-rows-count", baseId, activeFilter, activeSearch, exact],
|
||||
queryFn: () =>
|
||||
countRows({
|
||||
baseId: baseId!,
|
||||
filter: activeFilter,
|
||||
search: activeSearch,
|
||||
exact,
|
||||
}),
|
||||
enabled: !!baseId,
|
||||
staleTime: 30 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useReorderRowMutation() {
|
||||
const { t } = useTranslation();
|
||||
return useMutation<void, Error, ReorderRowInput, RowCacheContext>({
|
||||
|
||||
@@ -23,6 +23,8 @@ import {
|
||||
FilterNode,
|
||||
SearchSpec,
|
||||
ViewSortConfig,
|
||||
CountRowsInput,
|
||||
CountRowsResult,
|
||||
} from "@/features/base/types/base.types";
|
||||
import { IPagination } from "@/lib/types";
|
||||
|
||||
@@ -151,6 +153,13 @@ export async function reorderRow(data: ReorderRowInput): Promise<void> {
|
||||
await api.post("/bases/rows/reorder", data);
|
||||
}
|
||||
|
||||
export async function countRows(
|
||||
data: CountRowsInput,
|
||||
): Promise<CountRowsResult> {
|
||||
const req = await api.post<CountRowsResult>("/bases/rows/count", data);
|
||||
return req.data;
|
||||
}
|
||||
|
||||
// --- Views ---
|
||||
|
||||
export async function createView(data: CreateViewInput): Promise<IBaseView> {
|
||||
|
||||
@@ -239,6 +239,21 @@ export type ReorderPropertyInput = {
|
||||
requestId?: string;
|
||||
};
|
||||
|
||||
export type CountRowsInput = {
|
||||
baseId: string;
|
||||
filter?: FilterNode;
|
||||
search?: SearchSpec;
|
||||
// When true the server returns an exact (but capped) count. Otherwise
|
||||
// a cheap EXPLAIN-plan estimate. Paired with `capped` on the result.
|
||||
exact?: boolean;
|
||||
};
|
||||
|
||||
export type CountRowsResult = {
|
||||
value: number;
|
||||
exact: boolean;
|
||||
capped: boolean;
|
||||
};
|
||||
|
||||
export type CreateRowInput = {
|
||||
baseId: string;
|
||||
cells?: Record<string, unknown>;
|
||||
|
||||
Reference in New Issue
Block a user