diff --git a/apps/desktop/src/components/document/DocumentBrowser.vue b/apps/desktop/src/components/document/DocumentBrowser.vue index 0829ffb032..7de48712bc 100644 --- a/apps/desktop/src/components/document/DocumentBrowser.vue +++ b/apps/desktop/src/components/document/DocumentBrowser.vue @@ -68,7 +68,19 @@ import { import { applyDocumentStoreIdentityPlan, insertDocumentStoreDocument as insertDocumentStoreDocumentCore } from "@/lib/app/documentStoreSave"; import RedisJsonEditor from "@/components/redis/RedisJsonEditor.vue"; import { isLosslessJsonNumber, parseJsonPreservingLargeNumbers } from "@/lib/common/safeJsonFormat"; -import { buildMongoInsertDocument, buildMongoUpdateDocument, formatMongoShellLiteral, mongoDocumentDisplayValue, mongoDocumentGridColumnTypes, mongoDocumentIdForGrid, parseMongoDocumentInputValue, serializeMongoDocumentId, type MongoInputValue } from "@/lib/mongo/mongoDocumentValues"; +import { + buildMongoCopyDocumentFromOriginal, + buildMongoInsertDocument, + buildMongoUpdateDocument, + formatMongoShellLiteral, + mongoDocumentDisplayValue, + mongoDocumentGridColumnTypes, + mongoDocumentIdForGrid, + parseMongoDocumentInputValue, + serializeMongoDocumentId, + type MongoInputValue, +} from "@/lib/mongo/mongoDocumentValues"; +import type { GridNewRowMeta } from "@/lib/dataGrid/gridNewRowPlacement"; import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize"; import { findDocumentTextMatches, renderDocumentJsonHtml } from "@/lib/document/documentJsonSearch"; import { documentDataGridColumnLayoutScopeKey } from "@/lib/dataGrid/dataGridColumnLayoutStorage"; @@ -246,6 +258,7 @@ type DocumentGridChanges = { dirtyRows: Map>; deletedRows: Set; newRows: MongoInputValue[][]; + newRowMeta: GridNewRowMeta[]; columns: string[]; rows: MongoInputValue[][]; }; @@ -756,8 +769,9 @@ async function gridSave(changes: DocumentGridChanges) { await api.documentDeleteDocument(props.connectionId, props.database, props.collection, isEs ? String(documentId) : serializeMongoDocumentId(documentId), routing, documentType); } - for (const newRow of changes.newRows) { - const doc = isEs ? buildElasticsearchInsertDocument(newRow, cols) : buildMongoInsertDocument(newRow, cols); + for (const [newRowIndex, newRow] of changes.newRows.entries()) { + const newRowMeta = changes.newRowMeta[newRowIndex]; + const doc = isEs ? buildElasticsearchInsertDocument(newRow, cols) : buildMongoGridInsertDocument(newRow, cols, newRowMeta); if (isEs) { const id = documentIdFromGridValue(newRow[idColIdx]); const routing = documentRoutingFromGridRow(newRow, cols); @@ -768,7 +782,9 @@ async function gridSave(changes: DocumentGridChanges) { } continue; } - await api.documentInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc)); + const sourceIndex = newRowMeta?.sourceIndex; + const preserveBsonTypes = sourceIndex !== undefined && copyDocuments.value[sourceIndex] !== undefined; + await api.documentInsertDocument(props.connectionId, props.database, props.collection, JSON.stringify(doc), undefined, preserveBsonTypes); } if (isEs) resetElasticsearchTotals({ preservePaginationTotal: true }); @@ -786,6 +802,22 @@ function buildElasticsearchInsertDocument(row: MongoInputValue[], columns: strin return doc; } +function buildMongoGridInsertDocument(row: MongoInputValue[], columns: string[], meta?: GridNewRowMeta): Record { + const sourceIndex = meta?.sourceIndex; + const sourceDocument = sourceIndex === undefined ? undefined : copyDocuments.value[sourceIndex]; + if (!sourceDocument) return buildMongoInsertDocument(row, columns); + const editedColumns = new Set(meta?.editedColumns); + return ( + buildMongoCopyDocumentFromOriginal( + sourceDocument, + row, + columns, + columns.map((_, index) => editedColumns.has(index)), + { excludePrimaryKeys: true }, + ) ?? buildMongoInsertDocument(row, columns) + ); +} + function elasticsearchPathIdPreview(id: string): string { return encodeURIComponent(id); } @@ -805,7 +837,7 @@ function buildElasticsearchPartialUpdateDocument(changes: Map { - const { dirtyRows, deletedRows, newRows, columns, rows } = changes; + const { dirtyRows, deletedRows, newRows, newRowMeta, columns, rows } = changes; const idColIdx = columns.indexOf("_id"); const stmts: string[] = []; const coll = props.collection; @@ -837,8 +869,8 @@ async function previewDocumentChanges(changes: DocumentGridChanges): Promise= 0 ? documentIdFromGridValue(newRow[idColIdx]) : null; if (id) { diff --git a/apps/desktop/src/composables/useDataGridEditor.ts b/apps/desktop/src/composables/useDataGridEditor.ts index 0531915bf7..b2c337476f 100644 --- a/apps/desktop/src/composables/useDataGridEditor.ts +++ b/apps/desktop/src/composables/useDataGridEditor.ts @@ -60,9 +60,9 @@ type GridScrollerRef = }; export interface CustomSaveHandler { - save: (changes: { dirtyRows: Map>; newRows: CellValue[][]; deletedRows: Set; columns: string[]; rows: CellValue[][] }) => Promise; + save: (changes: { dirtyRows: Map>; newRows: CellValue[][]; newRowMeta: GridNewRowMeta[]; deletedRows: Set; columns: string[]; rows: CellValue[][] }) => Promise; applySavedChanges?: (changes: { dirtyRows: Map>; columns: string[] }) => void; - preview?: (changes: { dirtyRows: Map>; newRows: CellValue[][]; deletedRows: Set; columns: string[]; rows: CellValue[][] }) => Promise; + preview?: (changes: { dirtyRows: Map>; newRows: CellValue[][]; newRowMeta: GridNewRowMeta[]; deletedRows: Set; columns: string[]; rows: CellValue[][] }) => Promise; canInsert?: boolean; canDelete?: boolean; readonlyColumns?: string[]; @@ -126,6 +126,7 @@ interface PendingChangesSnapshot { interface PendingSaveSnapshot { newRows: CellValue[][]; newRowRefs: CellValue[][]; + newRowMeta: GridNewRowMeta[]; dirtyRows: Map>; deletedRows: Set; } @@ -220,11 +221,37 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { // Kept in lockstep with every structural mutation of newRows. const newRowMeta = ref([]); let nextNewRowToken = 1; - function allocateNewRowMeta(placement: GridNewRowPlacement | null): GridNewRowMeta { - return { token: nextNewRowToken++, placement }; + function allocateNewRowMeta(placement: GridNewRowPlacement | null, sourceIndex?: number, editedColumns?: readonly number[]): GridNewRowMeta { + return { token: nextNewRowToken++, placement, sourceIndex, editedColumns: editedColumns?.length ? [...editedColumns] : undefined }; } function cloneNewRowMeta(meta: readonly GridNewRowMeta[]): GridNewRowMeta[] { - return meta.map((item) => ({ token: item.token, placement: item.placement ? { ...item.placement } : null })); + return meta.map((item) => ({ token: item.token, placement: item.placement ? { ...item.placement } : null, sourceIndex: item.sourceIndex, editedColumns: item.editedColumns ? [...item.editedColumns] : undefined })); + } + + function updateClonedRowEditedColumns(newIndex: number, col: number, value: CellValue) { + const meta = newRowMeta.value[newIndex]; + if (!meta || meta.sourceIndex === undefined) return; + const baseline = result.value.rows[meta.sourceIndex]?.[col]; + const edited = new Set(meta.editedColumns); + if (value === baseline) edited.delete(col); + else edited.add(col); + meta.editedColumns = edited.size > 0 ? [...edited].sort((left, right) => left - right) : undefined; + } + + function clonedRowMeta(item: RowItem, row: readonly CellValue[]): GridNewRowMeta { + const inherited = item.newIndex === undefined ? undefined : newRowMeta.value[item.newIndex]; + const sourceIndex = item.sourceIndex ?? inherited?.sourceIndex; + const edited = new Set(inherited?.editedColumns); + if (item.sourceIndex !== undefined) { + for (const column of dirtyRows.value.get(item.sourceIndex)?.keys() ?? []) edited.add(column); + } + if (sourceIndex !== undefined) { + const baseline = result.value.rows[sourceIndex] ?? []; + row.forEach((value, column) => { + if (value !== baseline[column]) edited.add(column); + }); + } + return allocateNewRowMeta(null, sourceIndex, [...edited]); } // Restore a metadata snapshot and resume token allocation past its maximum so // newly created rows never collide with tokens held by restored rows (a fresh @@ -742,8 +769,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { if (changed) pushUndoSnapshot(); if (newRows.value[item.newIndex]) { newRows.value[item.newIndex][col] = newVal; + updateClonedRowEditedColumns(item.newIndex, col, newVal); } newRows.value = [...newRows.value]; + newRowMeta.value = [...newRowMeta.value]; if (changed) touchPendingChanges(); editingCell.value = null; isCommitting = false; @@ -849,9 +878,11 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { pushUndoSnapshot(); } row[col] = newVal; + updateClonedRowEditedColumns(item.newIndex, col, newVal); markBatchMutated(); if (!isBatching) { newRows.value = [...newRows.value]; + newRowMeta.value = [...newRowMeta.value]; touchPendingChanges(); } return; @@ -924,7 +955,9 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { if (!row || row[col] === null) return; pushUndoSnapshot(); row[col] = null; + updateClonedRowEditedColumns(item.newIndex, col, null); newRows.value = [...newRows.value]; + newRowMeta.value = [...newRowMeta.value]; touchPendingChanges(); return; } @@ -1111,7 +1144,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { pushUndoSnapshot(); rowStatusFilter.value = rowStatusFilterAfterAddingRow(rowStatusFilter.value); newRows.value.push(clonedData); - newRowMeta.value.push(allocateNewRowMeta(null)); + newRowMeta.value.push(clonedRowMeta(item, clonedData)); newRows.value = [...newRows.value]; newRowMeta.value = [...newRowMeta.value]; touchPendingChanges(); @@ -1134,7 +1167,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { for (const item of rowsToClone) { const clonedData = clonedRowData(item); newRows.value.push(clonedData); - newRowMeta.value.push(allocateNewRowMeta(null)); + newRowMeta.value.push(clonedRowMeta(item, clonedData)); } newRows.value = [...newRows.value]; newRowMeta.value = [...newRowMeta.value]; @@ -1263,6 +1296,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { dirtyRows: new Map([...dirtyRows.value.entries()].map(([rowIndex, changes]) => [rowIndex, new Map(changes)])), newRows: currentNewRows.map((row) => [...row]), newRowRefs: currentNewRows, + newRowMeta: cloneNewRowMeta(newRowMeta.value), deletedRows: new Set(deletedRows.value), }; } @@ -1452,6 +1486,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { await customHandler.save({ dirtyRows: snapshot.dirtyRows, newRows: snapshot.newRows, + newRowMeta: snapshot.newRowMeta, deletedRows: snapshot.deletedRows, columns: result.value.columns, rows: result.value.rows, @@ -1680,7 +1715,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) { try { if (customSaveHandler?.value) { const preview = customSaveHandler.value.preview; - if (preview) return await preview({ dirtyRows: dirtyRows.value, newRows: newRows.value, deletedRows: deletedRows.value, columns: result.value.columns, rows: result.value.rows }); + if (preview) return await preview({ dirtyRows: dirtyRows.value, newRows: newRows.value, newRowMeta: cloneNewRowMeta(newRowMeta.value), deletedRows: deletedRows.value, columns: result.value.columns, rows: result.value.rows }); return []; } const stmtOptions = saveStatementOptions(); diff --git a/apps/desktop/src/lib/backend/http.ts b/apps/desktop/src/lib/backend/http.ts index f3c1ba04fa..b688d9f049 100644 --- a/apps/desktop/src/lib/backend/http.ts +++ b/apps/desktop/src/lib/backend/http.ts @@ -3584,13 +3584,14 @@ export async function mongoInsertDocument(connectionId: string, database: string return documentInsertDocument(connectionId, database, collection, docJson, routing); } -export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string): Promise { +export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string, preserveBsonTypes?: boolean): Promise { return post("/api/document-store/insert-document", { connectionId, database, collection, docJson, routing, + preserveBsonTypes, }); } diff --git a/apps/desktop/src/lib/backend/tauri.ts b/apps/desktop/src/lib/backend/tauri.ts index 6788be2534..faf7330d89 100644 --- a/apps/desktop/src/lib/backend/tauri.ts +++ b/apps/desktop/src/lib/backend/tauri.ts @@ -3523,13 +3523,14 @@ export async function mongoInsertDocument(connectionId: string, database: string return documentInsertDocument(connectionId, database, collection, docJson, routing); } -export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string): Promise { +export async function documentInsertDocument(connectionId: string, database: string, collection: string, docJson: string, routing?: string, preserveBsonTypes?: boolean): Promise { return invoke("document_insert_document", { connectionId, database, collection, docJson, routing, + preserveBsonTypes, }); } diff --git a/apps/desktop/src/lib/dataGrid/gridNewRowPlacement.ts b/apps/desktop/src/lib/dataGrid/gridNewRowPlacement.ts index ccb4948e12..0f7559f9c2 100644 --- a/apps/desktop/src/lib/dataGrid/gridNewRowPlacement.ts +++ b/apps/desktop/src/lib/dataGrid/gridNewRowPlacement.ts @@ -25,6 +25,10 @@ export interface GridNewRowMeta { token: number; /** Display placement; `null` appends at the end of the result. */ placement: GridNewRowPlacement | null; + /** Source result row for a cloned pending row, when it has one. */ + sourceIndex?: number; + /** Columns whose values differ from the cloned source row. */ + editedColumns?: number[]; } export type GridOrderedRowEntry = { kind: "source"; sourceIndex: number } | { kind: "new"; newIndex: number }; diff --git a/crates/dbx-core/src/db/mongo_driver.rs b/crates/dbx-core/src/db/mongo_driver.rs index 1863a8fd61..49f0b7b73a 100644 --- a/crates/dbx-core/src/db/mongo_driver.rs +++ b/crates/dbx-core/src/db/mongo_driver.rs @@ -1651,6 +1651,21 @@ pub async fn insert_document( Ok(format!("{}", result.inserted_id)) } +/// Inserts a document from canonical Extended JSON without interpreting +/// Mongo shell-like strings such as `ISODate(...)`. +pub async fn insert_document_extended_json( + client: &Client, + database: &str, + collection: &str, + doc_json: &str, +) -> Result { + let value: serde_json::Value = serde_json::from_str(doc_json).map_err(|e| format!("Invalid JSON: {e}"))?; + let doc = json_object_to_document_extended_json(&value).map_err(|e| format!("Invalid document: {e}"))?; + let col = client.database(database).collection::(collection); + let result = col.insert_one(doc).await.map_err(|e| e.to_string())?; + Ok(format!("{}", result.inserted_id)) +} + pub async fn insert_documents( client: &Client, database: &str, @@ -3492,6 +3507,21 @@ mod tests { assert!(matches!(doc.get("count"), Some(Bson::Int64(42)))); } + #[test] + fn json_object_to_document_extended_json_keeps_shell_date_strings_literal() { + let value = serde_json::json!({ + "date_text": "ISODate(\"2026-08-10T00:00:00.000Z\")", + "actual_date": { "$date": "2026-08-10T00:00:00.000Z" }, + }); + let doc = json_object_to_document_extended_json(&value).unwrap(); + + assert!(matches!( + doc.get("date_text"), + Some(Bson::String(value)) if value == "ISODate(\"2026-08-10T00:00:00.000Z\")" + )); + assert!(matches!(doc.get("actual_date"), Some(Bson::DateTime(_)))); + } + #[test] fn parse_gridfs_file_id_accepts_extended_json_object_id() { let id = parse_gridfs_file_id(r#"{"$oid":"507f1f77bcf86cd799439011"}"#).unwrap(); diff --git a/crates/dbx-core/src/document_ops.rs b/crates/dbx-core/src/document_ops.rs index d6aa446697..ae72424adb 100644 --- a/crates/dbx-core/src/document_ops.rs +++ b/crates/dbx-core/src/document_ops.rs @@ -524,6 +524,27 @@ pub async fn insert_document_core( } } +pub async fn insert_document_preserving_bson_types_core( + state: &AppState, + connection_id: &str, + database: &str, + collection: &str, + doc_json: &str, + routing: Option<&str>, +) -> Result { + ensure_document_pool(state, connection_id).await?; + let connections = state.connections.read().await; + match connections.get(connection_id).ok_or("Not found")? { + PoolKind::MongoDb(client) => { + mongo_driver::insert_document_extended_json(client, database, collection, doc_json).await + } + _ => { + drop(connections); + insert_document_core(state, connection_id, database, collection, doc_json, routing).await + } + } +} + pub async fn update_document_core( state: &AppState, connection_id: &str, diff --git a/crates/dbx-web/src/routes/document_store.rs b/crates/dbx-web/src/routes/document_store.rs index 96309958ee..9da005e7ab 100644 --- a/crates/dbx-web/src/routes/document_store.rs +++ b/crates/dbx-web/src/routes/document_store.rs @@ -87,6 +87,7 @@ pub struct DocumentInsertRequest { pub collection: String, pub doc_json: String, pub routing: Option, + pub preserve_bson_types: Option, } #[derive(Deserialize)] @@ -222,15 +223,27 @@ pub async fn insert_document( Json(req): Json, ) -> Result, AppError> { ensure_writable(&state.app, &req.connection_id, "Insert").await?; - let result = dbx_core::document_ops::insert_document_core( - &state.app, - &req.connection_id, - &req.database, - &req.collection, - &req.doc_json, - req.routing.as_deref(), - ) - .await + let result = if req.preserve_bson_types.unwrap_or(false) { + dbx_core::document_ops::insert_document_preserving_bson_types_core( + &state.app, + &req.connection_id, + &req.database, + &req.collection, + &req.doc_json, + req.routing.as_deref(), + ) + .await + } else { + dbx_core::document_ops::insert_document_core( + &state.app, + &req.connection_id, + &req.database, + &req.collection, + &req.doc_json, + req.routing.as_deref(), + ) + .await + } .map_err(AppError::from)?; Ok(Json(result)) } diff --git a/packages/app-tests/dataGridEditor.test.ts b/packages/app-tests/dataGridEditor.test.ts index 44392e729d..2a62d3b0e3 100644 --- a/packages/app-tests/dataGridEditor.test.ts +++ b/packages/app-tests/dataGridEditor.test.ts @@ -109,7 +109,7 @@ function createQuickEntryEditor(options: { rowStatusFilter?: ReturnType>; filterRowsInGetRowItem?: boolean; supportsInsert?: boolean; - save?: (changes: { dirtyRows: Map>; newRows: CellValue[][] }) => Promise; + save?: (changes: { dirtyRows: Map>; newRows: CellValue[][]; newRowMeta: Array<{ sourceIndex?: number; editedColumns?: number[] }> }) => Promise; }) { const result = computed(() => ({ columns: ["id", "name"], @@ -376,6 +376,48 @@ test("cloning a row copies non-generated primary key values without executing sa assert.deepEqual(editor.newRows.value, []); }); +test("cloning a row preserves its source and edited columns for custom saves", async () => { + setActivePinia(createPinia()); + installBrowserTestGlobals(); + + let savedMeta: Array<{ sourceIndex?: number; editedColumns?: number[] }> | undefined; + const editor = createQuickEntryEditor({ + quickEntryEnabled: false, + save: async (changes) => { + savedMeta = changes.newRowMeta; + }, + }); + + editor.cloneRow(0); + assert.equal(editor.newRowMeta.value[0]?.sourceIndex, 0); + assert.deepEqual(editor.newRowMeta.value[0]?.editedColumns, undefined); + + editor.applyCellValue(-1, 1, "Grace"); + assert.deepEqual(editor.newRowMeta.value[0]?.editedColumns, [1]); + + await editor.saveChanges(); + assert.deepEqual(savedMeta, [ + { + token: 1, + placement: null, + sourceIndex: 0, + editedColumns: [1], + }, + ]); +}); + +test("cloning an edited source row marks its pending changes as explicit", () => { + setActivePinia(createPinia()); + installBrowserTestGlobals(); + + const editor = createQuickEntryEditor({ quickEntryEnabled: false }); + editor.applyCellValue(0, 1, "Lin"); + editor.cloneRow(0); + + assert.deepEqual(editor.newRows.value, [[1, "Lin"]]); + assert.deepEqual(editor.newRowMeta.value[0]?.editedColumns, [1]); +}); + test("cloning a row clears auto-generated key columns", async () => { setActivePinia(createPinia()); installBrowserTestGlobals(); diff --git a/packages/app-tests/documentBrowser.test.ts b/packages/app-tests/documentBrowser.test.ts index 4a492012ab..db2eec2ce6 100644 --- a/packages/app-tests/documentBrowser.test.ts +++ b/packages/app-tests/documentBrowser.test.ts @@ -31,6 +31,16 @@ test("mongo document table passes copy context to the data grid", () => { assert.match(source, /props\.databaseType === "mongodb" && mongoCopyDocumentsAvailable\.value/); }); +test("mongo document row clones reuse the type-preserving source document for saves and previews", () => { + const source = documentBrowserSource(); + assert.match(source, /type DocumentGridChanges = \{[\s\S]*?newRowMeta: GridNewRowMeta\[\];/); + assert.match(source, /function buildMongoGridInsertDocument\([\s\S]*?copyDocuments\.value\[sourceIndex\][\s\S]*?buildMongoCopyDocumentFromOriginal\([\s\S]*?excludePrimaryKeys: true/); + assert.match(source, /buildMongoGridInsertDocument\(newRow, cols, newRowMeta\)/); + assert.match(source, /buildMongoGridInsertDocument\(newRow, columns, newRowMeta\[newRowIndex\]\)/); + assert.match(source, /preserveBsonTypes = sourceIndex !== undefined && copyDocuments\.value\[sourceIndex\] !== undefined/); + assert.match(source, /documentInsertDocument\(props\.connectionId, props\.database, props\.collection, JSON\.stringify\(doc\), undefined, preserveBsonTypes\)/); +}); + test("document edit mode toggles whole JSON editing for insert and save", () => { const source = documentBrowserSource(); assert.match(source, /documentEditMode = ref<"fields" \| "json">\("json"\)/); diff --git a/src-tauri/src/commands/document_cmd.rs b/src-tauri/src/commands/document_cmd.rs index 569a9ce27d..3eff3f978e 100644 --- a/src-tauri/src/commands/document_cmd.rs +++ b/src-tauri/src/commands/document_cmd.rs @@ -105,17 +105,30 @@ pub async fn document_insert_document( collection: String, doc_json: String, routing: Option, + preserve_bson_types: Option, ) -> Result { ensure_connection_writable(&state, &connection_id, "Insert").await?; - dbx_core::document_ops::insert_document_core( - &state, - &connection_id, - &database, - &collection, - &doc_json, - routing.as_deref(), - ) - .await + if preserve_bson_types.unwrap_or(false) { + dbx_core::document_ops::insert_document_preserving_bson_types_core( + &state, + &connection_id, + &database, + &collection, + &doc_json, + routing.as_deref(), + ) + .await + } else { + dbx_core::document_ops::insert_document_core( + &state, + &connection_id, + &database, + &collection, + &doc_json, + routing.as_deref(), + ) + .await + } } #[tauri::command] diff --git a/src-tauri/src/commands/mongo_cmd.rs b/src-tauri/src/commands/mongo_cmd.rs index 400803f2b7..94f82636f2 100644 --- a/src-tauri/src/commands/mongo_cmd.rs +++ b/src-tauri/src/commands/mongo_cmd.rs @@ -430,6 +430,7 @@ pub async fn mongo_insert_document( collection, doc_json, routing, + None, ) .await }