feat(mongodb): show and sort collection sizes in objects view

This commit is contained in:
zipg authored and GitHub committed 2026-09-21 13:36:44 +08:00
1 parent 0668bdc406
commit 4dd62b78ec
8 files changed
+180 -11

No files matched your search

@@ -290,10 +290,15 @@ const effectiveDatabaseType = computed(() => effectiveDatabaseTypeForConnection(
const isGaussdbM = computed(() => effectiveDatabaseType.value === "gaussdb" && props.connection.driver_profile?.toLowerCase() === "gaussdb-m");
const isVictoriaMetrics = computed(() => effectiveDatabaseType.value === "victoriametrics");
const isMongodb = computed(() => props.connection.db_type === "mongodb");
const supportsObjectRowStats = computed(() => !isMongodb.value);
const supportsObjectSizeStats = computed(() => !isVictoriaMetrics.value && !isMongodb.value);
// Victoria Metrics reports series instead of rows and has no byte size to show;
// every other engine (MongoDB collections included, via `collStats`) fills both
// the row and size columns.
const supportsObjectSizeStats = computed(() => !isVictoriaMetrics.value);
// The batch table toolbar (export/copy/truncate/empty/drop selected) is SQL-only:
// MongoDB collections are not dropped or truncated through it.
const supportsBatchTableActions = computed(() => !isVictoriaMetrics.value && !isMongodb.value);
const showTableStatistics = computed(() => objectFilter.value === "all" || objectFilter.value === "tables");
const showObjectRowStats = computed(() => supportsObjectRowStats.value && showTableStatistics.value);
const showObjectRowStats = computed(() => showTableStatistics.value);
const showObjectSizeStats = computed(() => supportsObjectSizeStats.value && showTableStatistics.value);
const objectRowsLabel = computed(() => t(isVictoriaMetrics.value ? "objects.series" : "objects.rows"));
@@ -2940,7 +2945,7 @@ async function loadObjects(options?: { allowCached?: boolean; preserveExistingRo
if (!objectBrowserRowsLoadGuard.isCurrent(request)) return;
applyObjectBrowserRows(nextRows);
const cachedAt = cacheObjectBrowserRows(cacheWriteToken, nextRows);
if (props.connection.db_type !== "mongodb") void loadObjectStatistics(request, cacheWriteToken, cachedAt);
void loadObjectStatistics(request, cacheWriteToken, cachedAt);
} catch (e: any) {
if (!objectBrowserRowsLoadGuard.isCurrent(request)) return;
// Keep visible rows on a background revalidate failure — surface a lightweight
@@ -3503,23 +3508,23 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
<div class="min-w-0 flex-1 truncate text-muted-foreground">
{{ t("objects.selectedTables", { count: selectedTableCount }) }}
</div>
<Button v-if="supportsObjectSizeStats" variant="ghost" size="sm" class="h-7 px-2 text-xs" @click="openBatchDatabaseExport">
<Button v-if="supportsBatchTableActions" variant="ghost" size="sm" class="h-7 px-2 text-xs" @click="openBatchDatabaseExport">
<Upload class="mr-1.5 h-3.5 w-3.5" />
{{ t("objects.exportSelected") }}
</Button>
<Button v-if="supportsObjectSizeStats" variant="ghost" size="sm" class="h-7 px-2 text-xs" @click="copySelectedTablesToClipboard">
<Button v-if="supportsBatchTableActions" variant="ghost" size="sm" class="h-7 px-2 text-xs" @click="copySelectedTablesToClipboard">
<Clipboard class="mr-1.5 h-3.5 w-3.5" />
{{ t("objects.copyTableSelected") }}
</Button>
<Button v-if="supportsObjectSizeStats && supportsTruncateTable" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchTruncateTables">
<Button v-if="supportsBatchTableActions && supportsTruncateTable" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchTruncateTables">
<Scissors class="mr-1.5 h-3.5 w-3.5" />
{{ t("objects.truncateSelected") }}
</Button>
<Button v-if="supportsObjectSizeStats" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchEmptyTables">
<Button v-if="supportsBatchTableActions" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchEmptyTables">
<Eraser class="mr-1.5 h-3.5 w-3.5" />
{{ t("contextMenu.batchEmpty", { count: selectedTableCount }) }}
</Button>
<Button v-if="supportsObjectSizeStats" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchDropTables">
<Button v-if="supportsBatchTableActions" variant="ghost" size="sm" class="h-7 px-2 text-xs text-destructive" @click="requestBatchDropTables">
<Trash2 class="mr-1.5 h-3.5 w-3.5" />
{{ t("objects.dropSelected") }}
</Button>
@@ -0,0 +1,28 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const objectBrowserSource = readFileSync(new URL("../ObjectBrowser.vue", import.meta.url), "utf8");
describe("ObjectBrowser MongoDB collection statistics (#9705)", () => {
it("requests table statistics for every engine, MongoDB included", () => {
// The loader used to skip MongoDB wholesale, so the columns below stayed
// empty even though the backend can now answer `collStats` per collection.
expect(objectBrowserSource).not.toContain('if (props.connection.db_type !== "mongodb") void loadObjectStatistics');
expect(objectBrowserSource).toContain("void loadObjectStatistics(request, cacheWriteToken, cachedAt);");
});
it("renders the row/size columns and their sort keys for MongoDB", () => {
expect(objectBrowserSource).toContain("const showObjectRowStats = computed(() => showTableStatistics.value);");
expect(objectBrowserSource).toContain("const supportsObjectSizeStats = computed(() => !isVictoriaMetrics.value);");
});
it("keeps the batch table toolbar SQL-only", () => {
expect(objectBrowserSource).toContain("const supportsBatchTableActions = computed(() => !isVictoriaMetrics.value && !isMongodb.value);");
for (const handler of ["openBatchDatabaseExport", "copySelectedTablesToClipboard", "requestBatchTruncateTables", "requestBatchEmptyTables", "requestBatchDropTables"]) {
const button = new RegExp(`v-if="supportsBatchTableActions[^"]*"[^>]*@click="${handler}"`);
expect(objectBrowserSource).toMatch(button);
}
// No toolbar action may key off the stats-column flag any more.
expect(objectBrowserSource).not.toContain('v-if="supportsObjectSizeStats');
});
});