fix(mongodb): preserve BSON dates in copied inserts
This commit is contained in:
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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user