fix(mongodb): avoid exact count for unfiltered queries

This commit is contained in:
Guoyu Su authored and GitHub committed 2026-07-25 19:40:09 +08:00
1 parent 7e0f77eded
commit 3da4dd5ff9
18 files changed
+304 -36

No files matched your search

@@ -17,6 +17,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
import { clampSearchSplitWidth } from "@/lib/dataGrid/dataGridSearchSplit";
import { documentViewerFontStyle } from "@/lib/document/documentViewerFontStyle";
import { clampDocumentPage, documentPageRequestLimit, resetElasticsearchDocumentTotals, resolveElasticsearchDocumentTotals } from "@/lib/document/elasticsearchDocumentTotals";
import { canGoNextDocumentPage, resolveDocumentQueryTotals } from "@/lib/document/documentQueryTotals";
import {
arrayObjectAncestorPathForDocumentField,
buildDocumentFilterCondition,
@@ -135,7 +136,16 @@ let elasticsearchCountExecutionId = "";
let elasticsearchCountGeneration = 0;
const documentStoreProvider = computed(() => documentStoreProviderFor(props.databaseType));
const pageTotal = computed(() => paginationTotal.value ?? total.value ?? 0);
const pageTotal = computed(() => paginationTotal.value);
const documentPageCount = computed(() => (pageTotal.value === undefined ? undefined : Math.max(1, Math.ceil(pageTotal.value / pageSize.value))));
const canGoNextPage = computed(() =>
canGoNextDocumentPage({
page: page.value,
pageSize: pageSize.value,
rowCount: documents.value.length,
paginationTotal: pageTotal.value,
}),
);
const documentRequestLimit = computed(() => {
if (documentStoreProvider.value.kind !== "elasticsearch" || paginationTotal.value === undefined) return pageSize.value;
return documentPageRequestLimit(page.value, pageSize.value, paginationTotal.value);
@@ -147,7 +157,7 @@ const tableFindPaneStyle = computed(() => {
});
const documentFontStyle = computed(() => documentViewerFontStyle(settingsStore.editorSettings));
const documentStoreLabels = computed(() => ({
documentsLabel: documentStoreProvider.value.documentsLabel({ total: total.value ?? 0, t }),
documentsLabel: documentStoreProvider.value.documentsLabel({ total: total.value ?? 0, totalIsExact: totalIsExact.value, t }),
queryPreview: documentQueryPreview.value,
}));
@@ -843,9 +853,10 @@ async function load() {
applyElasticsearchSearchTotal(result.total, result.total_is_exact !== false, filter);
} else {
cancelElasticsearchCount();
total.value = result.total;
totalIsExact.value = true;
paginationTotal.value = result.total;
const totals = resolveDocumentQueryTotals(result.total, result.total_is_exact !== false);
total.value = totals.total;
totalIsExact.value = totals.totalIsExact;
paginationTotal.value = totals.paginationTotal;
}
syncSelectedDocumentAfterLoad(previousSelectedIdx, previousSelectedId);
} catch (e: unknown) {
@@ -1313,7 +1324,7 @@ function prevPage() {
}
function nextPage() {
if ((page.value + 1) * pageSize.value >= pageTotal.value) return;
if (!canGoNextPage.value) return;
page.value++;
void load();
}
@@ -1479,8 +1490,9 @@ defineExpose({ focusSearch });
<Button variant="ghost" size="icon" class="h-5 w-5" :disabled="page <= 0" @click="prevPage">
<ChevronLeft class="h-3 w-3" />
</Button>
<span>{{ page + 1 }} / {{ Math.max(1, Math.ceil(pageTotal / pageSize)) }}</span>
<Button variant="ghost" size="icon" class="h-5 w-5" :disabled="(page + 1) * pageSize >= pageTotal" @click="nextPage">
<span v-if="documentPageCount !== undefined">{{ page + 1 }} / {{ documentPageCount }}</span>
<span v-else>{{ page + 1 }}</span>
<Button variant="ghost" size="icon" class="h-5 w-5" :disabled="!canGoNextPage" @click="nextPage">
<ChevronRight class="h-3 w-3" />
</Button>
</div>
@@ -133,7 +133,7 @@ import { applyColumnFormatter, buildColumnFormatterKey, getSupportedTimeZoneOpti
import { temporalCellEditorConfig, type TemporalCellEditorConfig } from "@/lib/dataGrid/dataGridTemporalEditor";
import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, isFocusSearchShortcut, isModRShortcut, isSaveShortcut, isToggleTransposeShortcut } from "@/lib/editor/keyboardShortcuts";
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGrid/dataGridScrollGutter";
import { canGoNextDataGridPage, hasCompleteLocalDataGridResult } from "@/lib/dataGrid/dataGridPagination";
import { canGoNextDataGridPage, hasCompleteLocalDataGridResult, resolveDataGridPaginationTotal } from "@/lib/dataGrid/dataGridPagination";
import { dataGridCountQueryOptions } from "@/lib/dataGrid/dataGridQueryOptions";
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
import { CANVAS_DATA_GRID_ROW_HEIGHT, dataGridSearchMatchKey, drawCanvasDataGrid } from "@/lib/dataGrid/canvasDataGridRenderer";
@@ -2398,7 +2398,13 @@ const displayedTotalRowCount = computed(() => serverKnownTotalRowCount.value ??
const totalRowCountIsExact = computed(() => props.totalRowCountIsExact !== false);
// A backend can expose an exact display total while deliberately restricting
// offset pagination to a smaller safe range.
const paginationTotalRowCount = computed(() => props.paginationTotalRowCount ?? serverKnownTotalRowCount.value);
const paginationTotalRowCount = computed(() =>
resolveDataGridPaginationTotal({
paginationTotalRowCount: props.paginationTotalRowCount,
serverKnownTotalRowCount: serverKnownTotalRowCount.value,
totalRowCountIsExact: totalRowCountIsExact.value,
}),
);
// Only a server-confirmed total drives pagination — an inferred total means
// rows exist that we never fetched, so navigation must stay inside rows.length.
const hasKnownPaginationTotalRowCount = computed(() => typeof paginationTotalRowCount.value === "number" && paginationTotalRowCount.value >= 0);
@@ -1190,6 +1190,7 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
:page-limit="activeTab.resultPageLimit"
:count-sql="activeTab.resultCountSql"
:total-row-count="activeTab.resultTotalRowCount"
:total-row-count-is-exact="activeTab.resultTotalRowCount !== undefined || activeTab.result.total_is_exact !== false"
:total-row-count-loading="activeTab.resultTotalRowCountLoading"
:on-execute-sql="async (sql: string) => emit('executeSql', sql)"
:full-export-result="(onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void) => queryStore.fetchTabResultForExport(activeTab.id, onProgress)"
@@ -1517,6 +1518,7 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
:page-offset="activeTab.resultPageOffset"
:page-limit="activeTab.resultPageLimit"
:total-row-count="activeTab.resultTotalRowCount"
:total-row-count-is-exact="activeTab.resultTotalRowCount !== undefined || activeTab.result.total_is_exact !== false"
:total-row-count-loading="activeTab.resultTotalRowCountLoading"
:on-execute-sql="async (sql: string) => emit('executeSql', sql)"
:full-export-result="(onProgress?: (info: { rowsExported: number; totalRows: number | null }) => void) => queryStore.fetchTabResultForExport(activeTab.id, onProgress)"