fix(mongodb): preserve BSON dates in copied inserts

This commit is contained in:
zipg authored and GitHub committed 2026-07-15 21:01:40 +08:00
1 parent 711d0ec57b
commit 9234c33e24
12 files changed
+160 -26

No files matched your search

@@ -55,6 +55,7 @@ type JsonRecord = Record<string, unknown>;
type ViewMode = "document" | "table";
const documents = ref<JsonRecord[]>([]);
const copyDocuments = ref<JsonRecord[]>([]);
const lastGridColumns = ref<string[]>([]);
const total = ref(0);
const loading = ref(false);
@@ -172,7 +173,7 @@ const gridResult = computed<QueryResult>(() => {
}),
);
return { columns, rows, mongo_documents: docs, affected_rows: 0, execution_time_ms: 0, truncated: false };
return { columns, rows, mongo_documents: docs, mongo_copy_documents: copyDocuments.value, affected_rows: 0, execution_time_ms: 0, truncated: false };
});
const documentFilterFieldOptions = computed(() => gridResult.value.columns);
const documentStructuredFilterCount = computed(() => (appliedDocumentFilter.value ? 1 : 0));
@@ -487,7 +488,9 @@ async function load() {
}
})
: result.documents.map(asRecord);
const nextCopyDocuments = result.extended_documents?.length === nextDocuments.length ? result.extended_documents.map(asRecord) : nextDocuments;
documents.value = nextDocuments;
copyDocuments.value = nextCopyDocuments;
if (nextDocuments.length > 0) {
const keySet = new Set<string>();
keySet.add("_id");
@@ -6651,7 +6651,7 @@ const {
database: computed(() => props.executionDatabase ?? props.database),
context: computed(() => props.context),
sourceColumns: visibleSourceColumns,
mongoDocuments: computed(() => props.result.mongo_documents),
mongoDocuments: computed(() => props.result.mongo_copy_documents ?? props.result.mongo_documents),
columnTypes: visibleColumnTypes,
whereInput: computed(() => currentWhereInput()),
orderBy: computed(() => currentOrderBy()),
@@ -212,10 +212,10 @@ describe("useDataGridExport prepared row statements", () => {
});
it("preserves original Mongo string types in INSERT and applies explicit edits", async () => {
const item = { ...row(["123", "true", '{"kind":"literal"}', "2024-01-01 00:00:00", '{"role":"maintainer"}']), sourceIndex: 0 };
item.isDirtyCol = [false, false, false, false, true];
const item = { ...row(["123", "true", '{"kind":"literal"}', "2024-01-01 00:00:00", '{"role":"maintainer"}', 'ISODate("2025-05-06T08:35:32Z")']), sourceIndex: 0 };
item.isDirtyCol = [false, false, false, false, true, false];
const state = createMongoExportState({
columns: ["numericText", "booleanText", "jsonText", "dateText", "profile"],
columns: ["numericText", "booleanText", "jsonText", "dateText", "profile", "lastUpdatedDate"],
item,
mongoDocuments: [
{
@@ -224,12 +224,22 @@ describe("useDataGridExport prepared row statements", () => {
jsonText: '{"kind":"literal"}',
dateText: "2024-01-01 00:00:00",
profile: { role: "admin" },
lastUpdatedDate: { $date: "2025-05-06T08:35:32Z" },
},
],
});
await state.copyRowAsInsert();
expect(copyToClipboard).toHaveBeenCalledWith('db.getCollection("documents").insert({"numericText":"123","booleanText":"true","jsonText":"{\\"kind\\":\\"literal\\"}","dateText":"2024-01-01 00:00:00","profile":{"role":"maintainer"}});');
expect(copyToClipboard).toHaveBeenCalledWith(`db.getCollection("documents").insert({
"numericText": "123",
"booleanText": "true",
"jsonText": "{\\"kind\\":\\"literal\\"}",
"dateText": "2024-01-01 00:00:00",
"profile": {
"role": "maintainer"
},
"lastUpdatedDate": ISODate("2025-05-06T08:35:32Z")
});`);
});
});
@@ -13,6 +13,7 @@ import { uuid } from "@/lib/common/utils";
import { useSettingsStore } from "@/stores/settingsStore";
import { expandNestedJsonStringsForCopy } from "@/lib/common/jsonCopyValue";
import { buildMongoCopyDocumentFromOriginal, buildMongoCopyInsertDocument, formatMongoShellLiteral, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues";
import { formatMongoShellText } from "@/lib/mongo/mongoFormatter";
import type { DatabaseType, QueryResult } from "@/types/database";
import type { QueryResultExportRequest } from "@/lib/backend/api";
import { usesSyntheticRowIdKey } from "@/lib/table/tableEditing";
@@ -352,15 +353,17 @@ export function useDataGridExport(options: UseDataGridExportOptions) {
const promise = Promise.resolve().then(async () => {
const statement =
databaseType.value === "mongodb"
? buildMongoCopyInsertStatement({
collection: copyInsertTargetLabel?.value || tableMeta.value?.tableName || "collection",
columns: columns.value,
sourceColumns: sourceColumns.value,
rows,
mongoDocuments: options.mongoDocuments?.value,
excludePrimaryKeys,
insertMode,
})
? formatMongoCopyInsertStatement(
buildMongoCopyInsertStatement({
collection: copyInsertTargetLabel?.value || tableMeta.value?.tableName || "collection",
columns: columns.value,
sourceColumns: sourceColumns.value,
rows,
mongoDocuments: options.mongoDocuments?.value,
excludePrimaryKeys,
insertMode,
}),
)
: await buildDataGridCopyInsertStatement({
databaseType: databaseType.value,
tableMeta: tableMeta.value,
@@ -1375,6 +1378,15 @@ function buildMongoCopyInsertStatement(options: { collection: string; columns: s
return `${collection}.insertMany(${formatMongoShellLiteral(documents)});`;
}
function formatMongoCopyInsertStatement(statement: string | undefined): string | undefined {
if (!statement) return undefined;
try {
return formatMongoShellText(statement);
} catch {
return statement;
}
}
function compactLocalTimestamp(date = new Date()): string {
const yy = String(date.getFullYear() % 100).padStart(2, "0");
const month = String(date.getMonth() + 1).padStart(2, "0");
+1
View File
@@ -1752,6 +1752,7 @@ export async function zookeeperDelete(connectionId: string, key: string): Promis
export interface MongoDocumentResult {
documents: any[];
raw_documents?: string[];
extended_documents?: any[];
total: number;
}
+2
View File
@@ -515,6 +515,8 @@ export interface QueryResult {
* preview. This is populated only for MongoDB document query results.
*/
mongo_documents?: unknown[];
/** Type-preserving Extended JSON documents used when copying MongoDB values. */
mongo_copy_documents?: unknown[];
affected_rows: number;
execution_time_ms: number;
truncated?: boolean;