feat(mongodb): improve sidebar index management
This commit is contained in:
1 parent
22055e081f
commit
8e7b74d0ee
56 files changed
+3558
-246
No files matched your search
@@ -221,6 +221,10 @@ import { supportsTableStructureEditing } from "@/lib/database/databaseCapabiliti
|
||||
import { rememberDataGridConditionHistory } from "@/lib/dataGrid/dataGridConditionHistory";
|
||||
import { restoreDataGridLocalColumnFilters, serializeDataGridLocalColumnFilters } from "@/lib/dataGrid/dataGridLocalColumnFilterState";
|
||||
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { mongoCollectionSupportsIndexes, supportsMongoIndexMutations } from "@/lib/mongo/mongoCapabilities";
|
||||
import { refreshLoadedMongoIndexes } from "@/lib/mongo/mongoIndexMetadata";
|
||||
import { isProtectedMongoIndex, mongoDropAllIndexesPreview, mongoDropIndexFailureCount, mongoDropIndexPreview } from "@/lib/sidebar/mongoCollectionMutation";
|
||||
import { runMongoMutation } from "@/lib/sidebar/runMongoSidebarMutation";
|
||||
import { dataGridConditionColumnOptions, dataGridConditionIdentifierQuote } from "@/lib/dataGrid/dataGridConditionCompletion";
|
||||
import { isMacOS } from "@/lib/backend/platform";
|
||||
import { appendDebugLog, isDebugLoggingEnabled } from "@/lib/backend/debugLog";
|
||||
@@ -7336,7 +7340,8 @@ function toggleCellDetailPanelLayout() {
|
||||
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(props.databaseType));
|
||||
const canOpenTableStructureEditor = computed(() => !!props.connectionId && !!props.database && !!props.tableMeta?.tableName && supportsTableStructureEditing(resolvedDatabaseType.value));
|
||||
const mongoConnectionConfig = resolvedConnectionConfig;
|
||||
const canManageMongoIndexes = computed(() => resolvedDatabaseType.value === "mongodb" && !!props.connectionId && !!props.database && !!props.tableMeta?.tableName && mongoConnectionConfig.value?.db_type === "mongodb" && mongoConnectionConfig.value?.driver_profile !== "mongodb-legacy");
|
||||
const canManageMongoIndexes = computed(() => resolvedDatabaseType.value === "mongodb" && !!props.connectionId && !!props.database && !!props.tableMeta?.tableName && supportsMongoIndexMutations(mongoConnectionConfig.value, props.tableMeta?.tableType));
|
||||
const canShowTableIndexes = computed(() => tableMetadataCapabilities.value.indexes && (resolvedDatabaseType.value !== "mongodb" || mongoCollectionSupportsIndexes(props.tableMeta?.tableType)));
|
||||
const tableInfoTabs = computed(() => {
|
||||
const tabs: TableInfoTabItem[] = [];
|
||||
if (tableMetadataCapabilities.value.ddl) {
|
||||
@@ -7350,7 +7355,7 @@ const tableInfoTabs = computed(() => {
|
||||
count: props.tableMeta?.columns.length,
|
||||
});
|
||||
}
|
||||
if (tableMetadataCapabilities.value.indexes) {
|
||||
if (canShowTableIndexes.value) {
|
||||
tabs.push({ id: "indexes", label: t("grid.tableInfoIndexes"), icon: KeyRound, count: indexes.value.length });
|
||||
}
|
||||
if (tableMetadataCapabilities.value.foreignKeys) {
|
||||
@@ -7435,6 +7440,19 @@ async function reloadIndexes() {
|
||||
await fetchIndexes();
|
||||
}
|
||||
|
||||
async function refreshMongoIndexMetadataAfterMutation() {
|
||||
await reloadIndexes();
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const collection = props.tableMeta?.tableName;
|
||||
if (!connectionId || !database || !collection) return;
|
||||
try {
|
||||
await refreshLoadedMongoIndexes(connectionStore, { connectionId, database, collection });
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.mongoIndexRefreshFailed", { message: String(e?.message || e) }), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchForeignKeys() {
|
||||
const requestIdentity = currentForeignKeyTableIdentity.value;
|
||||
if (!props.connectionId || !props.tableMeta || !requestIdentity || foreignKeysLoaded.value || foreignKeysLoading.value) return;
|
||||
@@ -7775,7 +7793,8 @@ const filteredIndexes = computed(() => {
|
||||
return indexes.value.filter((i) => i.name.toLowerCase().includes(q) || i.columns.some((c) => c.toLowerCase().includes(q)));
|
||||
});
|
||||
|
||||
const droppableMongoIndexes = computed(() => indexes.value.filter((index) => !index.is_primary));
|
||||
const droppableMongoIndexes = computed(() => indexes.value.filter((index) => !isProtectedMongoIndex(index)));
|
||||
|
||||
const dropMongoIndexConfirmMessage = computed(() =>
|
||||
pendingDropMongoIndex.value
|
||||
? t("contextMenu.confirmDropMongoIndexMessage", {
|
||||
@@ -7784,51 +7803,86 @@ const dropMongoIndexConfirmMessage = computed(() =>
|
||||
})
|
||||
: "",
|
||||
);
|
||||
const dropMongoIndexPreview = computed(() => (pendingDropMongoIndex.value ? mongoDropIndexPreview(props.database || "", props.tableMeta?.tableName || "", pendingDropMongoIndex.value.name) : ""));
|
||||
const dropAllMongoIndexesConfirmMessage = computed(() => t("contextMenu.confirmDropMongoAllIndexesMessage", { name: props.tableMeta?.tableName || "" }));
|
||||
const dropAllMongoIndexesConfirmDetails = computed(() => t("contextMenu.confirmDropMongoAllIndexesDetails"));
|
||||
const dropMongoIndexPreview = computed(() => (pendingDropMongoIndex.value ? `db.getCollection(${JSON.stringify(props.tableMeta?.tableName || "")}).dropIndex(${JSON.stringify(pendingDropMongoIndex.value.name)})` : ""));
|
||||
const dropAllMongoIndexesPreview = computed(() => `db.getCollection(${JSON.stringify(props.tableMeta?.tableName || "")}).dropIndexes()`);
|
||||
const dropAllMongoIndexesPreview = computed(() => mongoDropAllIndexesPreview(props.database || "", props.tableMeta?.tableName || ""));
|
||||
|
||||
function requestDropMongoIndex(index: IndexInfo) {
|
||||
if (!canManageMongoIndexes.value || isProtectedMongoIndex(index)) return;
|
||||
pendingDropMongoIndex.value = index;
|
||||
showDropMongoIndexConfirm.value = true;
|
||||
}
|
||||
|
||||
function requestDropAllMongoIndexes() {
|
||||
if (!canManageMongoIndexes.value || droppableMongoIndexes.value.length === 0) return;
|
||||
showDropAllMongoIndexesConfirm.value = true;
|
||||
}
|
||||
|
||||
async function confirmDropMongoIndex() {
|
||||
if (!props.connectionId || !props.database || !props.tableMeta?.tableName || !pendingDropMongoIndex.value || dropMongoIndexLoading.value) return;
|
||||
dropMongoIndexLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(props.connectionId);
|
||||
await api.mongoDropIndexes(props.connectionId, props.database, props.tableMeta.tableName, JSON.stringify(pendingDropMongoIndex.value.name), true);
|
||||
toast(t("contextMenu.dropTableChildObjectSuccess", { name: pendingDropMongoIndex.value.name }), 3000);
|
||||
showDropMongoIndexConfirm.value = false;
|
||||
pendingDropMongoIndex.value = null;
|
||||
await reloadIndexes();
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropMongoIndexLoading.value = false;
|
||||
}
|
||||
const index = pendingDropMongoIndex.value;
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const tableName = props.tableMeta?.tableName;
|
||||
if (!connectionId || !database || !tableName || !index || !canManageMongoIndexes.value || isProtectedMongoIndex(index) || dropMongoIndexLoading.value) return;
|
||||
await runMongoMutation({
|
||||
connection: connectionStore.getConfig(connectionId),
|
||||
database,
|
||||
reviewText: dropMongoIndexPreview.value,
|
||||
source: t("production.sourceDataGrid"),
|
||||
loading: dropMongoIndexLoading,
|
||||
beforeExecute: () => connectionStore.ensureConnected(connectionId),
|
||||
execute: async () => {
|
||||
try {
|
||||
return await api.mongoDropIndexes(connectionId, database, tableName, JSON.stringify(index.name), true);
|
||||
} finally {
|
||||
await refreshMongoIndexMetadataAfterMutation();
|
||||
}
|
||||
},
|
||||
onSuccess: (result) => {
|
||||
const failed = mongoDropIndexFailureCount(result);
|
||||
if (failed > 0) {
|
||||
toast(t("contextMenu.dropIndexesPartialFailure", { success: result.dropped_names.length, failed }), 5000);
|
||||
} else {
|
||||
toast(t("contextMenu.dropTableChildObjectSuccess", { name: index.name }), 3000);
|
||||
}
|
||||
showDropMongoIndexConfirm.value = false;
|
||||
pendingDropMongoIndex.value = null;
|
||||
},
|
||||
onError: (e: any) => toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000),
|
||||
});
|
||||
}
|
||||
|
||||
async function confirmDropAllMongoIndexes() {
|
||||
if (!props.connectionId || !props.database || !props.tableMeta?.tableName || dropAllMongoIndexesLoading.value) return;
|
||||
dropAllMongoIndexesLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(props.connectionId);
|
||||
const result = await api.mongoDropIndexes(props.connectionId, props.database, props.tableMeta.tableName, undefined, false);
|
||||
toast(t("contextMenu.dropAllIndexesSuccess", { count: result.dropped_names.length, name: props.tableMeta.tableName }), 3000);
|
||||
showDropAllMongoIndexesConfirm.value = false;
|
||||
await reloadIndexes();
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropAllMongoIndexesLoading.value = false;
|
||||
}
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const tableName = props.tableMeta?.tableName;
|
||||
if (!connectionId || !database || !tableName || !canManageMongoIndexes.value || dropAllMongoIndexesLoading.value) return;
|
||||
await runMongoMutation({
|
||||
connection: connectionStore.getConfig(connectionId),
|
||||
database,
|
||||
reviewText: dropAllMongoIndexesPreview.value,
|
||||
source: t("production.sourceDataGrid"),
|
||||
loading: dropAllMongoIndexesLoading,
|
||||
beforeExecute: () => connectionStore.ensureConnected(connectionId),
|
||||
execute: async () => {
|
||||
try {
|
||||
return await api.mongoDropIndexes(connectionId, database, tableName, undefined, false);
|
||||
} finally {
|
||||
await refreshMongoIndexMetadataAfterMutation();
|
||||
}
|
||||
},
|
||||
onSuccess: (result) => {
|
||||
const failed = mongoDropIndexFailureCount(result);
|
||||
if (failed > 0) {
|
||||
toast(t("contextMenu.dropIndexesPartialFailure", { success: result.dropped_names.length, failed }), 5000);
|
||||
} else {
|
||||
toast(t("contextMenu.dropAllIndexesSuccess", { count: result.dropped_names.length, name: tableName }), 3000);
|
||||
}
|
||||
showDropAllMongoIndexesConfirm.value = false;
|
||||
},
|
||||
onError: (e: any) => toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000),
|
||||
});
|
||||
}
|
||||
|
||||
const filteredForeignKeys = computed(() => {
|
||||
@@ -9373,7 +9427,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
{{ index.columns.join(", ") }}
|
||||
</div>
|
||||
</div>
|
||||
<Button v-if="canManageMongoIndexes && !index.is_primary" variant="ghost" size="sm" class="h-7 shrink-0 px-2 text-[11px] text-destructive hover:text-destructive" @click="requestDropMongoIndex(index)">
|
||||
<Button v-if="canManageMongoIndexes && !isProtectedMongoIndex(index)" variant="ghost" size="sm" class="h-7 shrink-0 px-2 text-[11px] text-destructive hover:text-destructive" @click="requestDropMongoIndex(index)">
|
||||
<Trash2 class="mr-1 h-3 w-3" />
|
||||
{{ t("contextMenu.dropIndex") }}
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user