feat(mongodb): support copying MongoDB documents
This commit is contained in:
19 files changed
+580
-43
No files matched your search
@@ -84,6 +84,7 @@ type ViewMode = "document" | "table";
|
||||
|
||||
const documents = ref<JsonRecord[]>([]);
|
||||
const copyDocuments = ref<JsonRecord[]>([]);
|
||||
const mongoCopyDocumentsAvailable = ref(false);
|
||||
const lastGridColumns = ref<string[]>([]);
|
||||
const total = ref<number | undefined>(undefined);
|
||||
const totalIsExact = ref(true);
|
||||
@@ -112,6 +113,7 @@ const sortInput = ref("");
|
||||
const filterInputRef = ref<HTMLTextAreaElement>();
|
||||
const sortInputRef = ref<HTMLTextAreaElement>();
|
||||
const dataGridRef = ref<InstanceType<typeof DataGrid>>();
|
||||
const mongoUpdateTarget = computed(() => (props.databaseType === "mongodb" && mongoCopyDocumentsAvailable.value ? { collection: props.collection, idColumn: "_id" as const } : undefined));
|
||||
const documentViewerRef = ref<HTMLElement>();
|
||||
const documentSearchInputRef = ref<HTMLInputElement>();
|
||||
const documentSearchOpen = ref(false);
|
||||
@@ -822,9 +824,11 @@ async function load() {
|
||||
}
|
||||
})
|
||||
: result.documents.map(asRecord);
|
||||
const nextCopyDocuments = result.extended_documents?.length === nextDocuments.length ? result.extended_documents.map(asRecord) : nextDocuments;
|
||||
const hasTypePreservingCopyDocuments = result.extended_documents?.length === nextDocuments.length;
|
||||
const nextCopyDocuments = hasTypePreservingCopyDocuments ? result.extended_documents!.map(asRecord) : nextDocuments;
|
||||
documents.value = nextDocuments;
|
||||
copyDocuments.value = nextCopyDocuments;
|
||||
mongoCopyDocumentsAvailable.value = hasTypePreservingCopyDocuments;
|
||||
if (nextDocuments.length > 0) {
|
||||
const keySet = new Set<string>();
|
||||
keySet.add("_id");
|
||||
@@ -1569,6 +1573,7 @@ defineExpose({ focusSearch });
|
||||
:result="gridResult"
|
||||
context="results"
|
||||
:database-type="props.databaseType"
|
||||
:mongo-update-target="mongoUpdateTarget"
|
||||
editable
|
||||
:custom-save-handler="customSaveHandler"
|
||||
:loading="loading"
|
||||
|
||||
@@ -177,7 +177,7 @@ import {
|
||||
} from "@/lib/dataGrid/dataGridContextMenu";
|
||||
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { useDataGridExport } from "@/composables/useDataGridExport";
|
||||
import { useDataGridExport, type MongoCopyUpdateTarget } from "@/composables/useDataGridExport";
|
||||
import { eventTargetAllowsNativeClipboard, isPlainClipboardShortcut, readTextFromClipboard } from "@/lib/common/clipboard";
|
||||
import { claimDataGridPaste, clearDataGridClipboardCopy, parseDataGridClipboard, planDataGridPaste } from "@/lib/dataGrid/dataGridClipboard";
|
||||
import { DATA_GRID_ROW_NUM_WIDTH, useDataGridColumnResize } from "@/composables/useDataGridColumnResize";
|
||||
@@ -304,6 +304,7 @@ interface DataGridProps {
|
||||
allExportResults?: Array<{ sheetName: string; result: QueryResult; sql?: string }>;
|
||||
exportFileBaseName?: string;
|
||||
customSaveHandler?: import("@/composables/useDataGridEditor").CustomSaveHandler;
|
||||
mongoUpdateTarget?: MongoCopyUpdateTarget;
|
||||
queryEditabilityReason?: QueryEditabilityReason;
|
||||
allowInsertRows?: boolean;
|
||||
allowDeleteRows?: boolean;
|
||||
@@ -5103,6 +5104,7 @@ const {
|
||||
exportSql: computed(() => props.exportSql),
|
||||
tableMeta: computed(() => (props.tableMeta ? { ...props.tableMeta } : undefined)),
|
||||
copyInsertTargetLabel: computed(() => props.tableMeta?.tableName ?? props.customSaveHandler?.targetLabel),
|
||||
mongoUpdateTarget: computed(() => props.mongoUpdateTarget),
|
||||
databaseType: computed(() => props.databaseType),
|
||||
connectionId: computed(() => props.connectionId),
|
||||
database: computed(() => props.executionDatabase ?? props.database),
|
||||
|
||||
@@ -77,7 +77,7 @@ import { codeMirrorSqlDialect, codeMirrorSqlDialectForConnection, effectiveDatab
|
||||
import { chartableColumnIndexes } from "@/lib/dataGrid/chartData";
|
||||
import { elasticsearchJsonResponseForResult } from "@/lib/elasticsearch/elasticsearchJsonResponse";
|
||||
import * as api from "@/lib/backend/api";
|
||||
import { applyMongoGridChangesToDocument, buildMongoUpdateDocument, formatMongoShellLiteral, serializeMongoDocumentId, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues";
|
||||
import { applyMongoGridChangesToDocument, applyMongoGridChangesToDocumentBaseline, buildMongoUpdateDocument, formatMongoShellLiteral, serializeMongoDocumentId, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues";
|
||||
import type { SqlExecutionOverride } from "@/lib/sql/sqlExecutionTarget";
|
||||
import type { DataGridSortMode } from "@/lib/dataGrid/dataGridSort";
|
||||
import { DATA_GRID_COMPACT_TOPBAR_WIDTH, type DataGridReloadIntent } from "@/lib/dataGrid/dataGridToolbar";
|
||||
@@ -440,16 +440,22 @@ const mongoQueryResultSaveHandler = computed<CustomSaveHandler | undefined>(() =
|
||||
|
||||
// Replace the raw array only after every backend update succeeds, keeping
|
||||
// the grid and JSON preview atomic when a multi-row save partially fails.
|
||||
const replacements = new Map<unknown, unknown>();
|
||||
if (tab.resultLocalSortOriginalMongoDocuments) {
|
||||
tab.resultLocalSortOriginalMongoDocuments = applyMongoGridChangesToDocumentBaseline(tab.resultLocalSortOriginalMongoDocuments, documents, dirtyRows, columns);
|
||||
}
|
||||
tab.result!.mongo_documents = documents.map((document, rowIdx) => {
|
||||
const changes = dirtyRows.get(rowIdx);
|
||||
if (!changes) return document;
|
||||
const updated = applyMongoGridChangesToDocument(document, changes, columns);
|
||||
replacements.set(document, updated);
|
||||
return updated;
|
||||
return changes ? applyMongoGridChangesToDocument(document, changes, columns) : document;
|
||||
});
|
||||
if (tab.resultLocalSortOriginalMongoDocuments) {
|
||||
tab.resultLocalSortOriginalMongoDocuments = tab.resultLocalSortOriginalMongoDocuments.map((document) => replacements.get(document) ?? document);
|
||||
const copyDocuments = tab.result!.mongo_copy_documents;
|
||||
if (copyDocuments) {
|
||||
if (tab.resultLocalSortOriginalMongoCopyDocuments) {
|
||||
tab.resultLocalSortOriginalMongoCopyDocuments = applyMongoGridChangesToDocumentBaseline(tab.resultLocalSortOriginalMongoCopyDocuments, copyDocuments, dirtyRows, columns);
|
||||
}
|
||||
tab.result!.mongo_copy_documents = copyDocuments.map((document, rowIdx) => {
|
||||
const changes = dirtyRows.get(rowIdx);
|
||||
return changes ? applyMongoGridChangesToDocument(document, changes, columns) : document;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1168,6 +1174,7 @@ defineExpose({ focusSearch, refreshData, refreshQueryEditorCompletionCache, hand
|
||||
:editable="!!activeTab.queryAnalysis || !!mongoQueryResultSaveHandler"
|
||||
:source-columns="activeTab.querySourceColumns"
|
||||
:custom-save-handler="mongoQueryResultSaveHandler"
|
||||
:mongo-update-target="mongoQueryResultSaveHandler && activeTab.result.mongo_copy_documents?.length === activeTab.result.rows.length ? activeTab.mongoEditTarget : undefined"
|
||||
:query-editability-reason="activeTab.queryEditabilityReason"
|
||||
:allow-insert-rows="activeTab.queryAnalysis?.allowInsert !== false && activeTab.queryAnalysis?.allowInsertDelete !== false"
|
||||
:allow-delete-rows="activeTab.queryAnalysis?.allowInsertDelete !== false"
|
||||
|
||||
Reference in new issue
Block a user