diff --git a/apps/desktop/src/lib/api.ts b/apps/desktop/src/lib/api.ts index 4a2f00b22..73f22eab7 100644 --- a/apps/desktop/src/lib/api.ts +++ b/apps/desktop/src/lib/api.ts @@ -214,8 +214,11 @@ export const mongoListCollections = forward("mongoListCollections"); export const mongoFindDocuments = forward("mongoFindDocuments"); export const mongoAggregateDocuments = forward("mongoAggregateDocuments"); export const mongoInsertDocument = forward("mongoInsertDocument"); +export const mongoInsertDocuments = forward("mongoInsertDocuments"); export const mongoUpdateDocument = forward("mongoUpdateDocument"); +export const mongoUpdateDocuments = forward("mongoUpdateDocuments"); export const mongoDeleteDocument = forward("mongoDeleteDocument"); +export const mongoDeleteDocuments = forward("mongoDeleteDocuments"); // History export const saveHistory = forward("saveHistory"); diff --git a/apps/desktop/src/lib/http.ts b/apps/desktop/src/lib/http.ts index 5a36f0543..5593ada30 100644 --- a/apps/desktop/src/lib/http.ts +++ b/apps/desktop/src/lib/http.ts @@ -1304,6 +1304,15 @@ export async function mongoInsertDocument( return post("/api/mongo/insert-document", { connectionId, database, collection, docJson }); } +export async function mongoInsertDocuments( + connectionId: string, + database: string, + collection: string, + docsJson: string, +): Promise<{ affected_rows: number }> { + return post("/api/mongo/insert-documents", { connectionId, database, collection, docsJson }); +} + export async function mongoUpdateDocument( connectionId: string, database: string, @@ -1314,6 +1323,17 @@ export async function mongoUpdateDocument( return post("/api/mongo/update-document", { connectionId, database, collection, id, docJson }); } +export async function mongoUpdateDocuments( + connectionId: string, + database: string, + collection: string, + filterJson: string, + updateJson: string, + many: boolean, +): Promise<{ affected_rows: number }> { + return post("/api/mongo/update-documents", { connectionId, database, collection, filterJson, updateJson, many }); +} + export async function mongoDeleteDocument( connectionId: string, database: string, @@ -1323,6 +1343,16 @@ export async function mongoDeleteDocument( return post("/api/mongo/delete-document", { connectionId, database, collection, id }); } +export async function mongoDeleteDocuments( + connectionId: string, + database: string, + collection: string, + filterJson: string, + many: boolean, +): Promise<{ affected_rows: number }> { + return post("/api/mongo/delete-documents", { connectionId, database, collection, filterJson, many }); +} + // --------------------------------------------------------------------------- // History // --------------------------------------------------------------------------- diff --git a/apps/desktop/src/lib/mongoShellCommand.ts b/apps/desktop/src/lib/mongoShellCommand.ts index 288c7dde8..282c90c57 100644 --- a/apps/desktop/src/lib/mongoShellCommand.ts +++ b/apps/desktop/src/lib/mongoShellCommand.ts @@ -18,6 +18,11 @@ export interface MongoAggregateCommand { pipeline: string; } +export type MongoWriteCommand = + | { kind: "insert"; collection: string; docsJson: string } + | { kind: "update"; collection: string; filter: string; update: string; many: boolean } + | { kind: "delete"; collection: string; filter: string; many: boolean }; + export interface MongoAggregateSafetyOptions { allowWrites?: boolean; allowDangerous?: boolean; @@ -107,6 +112,51 @@ export function parseMongoAggregateCommand(input: string): MongoAggregateCommand }; } +export function parseMongoWriteCommand(input: string): MongoWriteCommand | null { + const source = input.trim().replace(/;$/, "").trim(); + const insertOne = parseCollectionMethodTarget(source, "insertOne"); + if (insertOne) { + const args = parseMethodArgs(source, insertOne.methodCallIndex); + if (!args || args.length !== 1) return null; + const doc = normalizeJsonArgument(args[0]); + return doc ? { kind: "insert", collection: insertOne.collection, docsJson: doc } : null; + } + + const insertMany = parseCollectionMethodTarget(source, "insertMany"); + if (insertMany) { + const args = parseMethodArgs(source, insertMany.methodCallIndex); + if (!args || args.length !== 1) return null; + const docs = normalizeJsonArgument(args[0]); + if (!docs) return null; + return Array.isArray(JSON.parse(docs)) + ? { kind: "insert", collection: insertMany.collection, docsJson: docs } + : null; + } + + for (const method of ["updateOne", "updateMany"] as const) { + const target = parseCollectionMethodTarget(source, method); + if (!target) continue; + const args = parseMethodArgs(source, target.methodCallIndex); + if (!args || args.length !== 2) return null; + const filter = normalizeJsonArgument(args[0]); + const update = normalizeJsonArgument(args[1]); + if (!filter || !update) return null; + return { kind: "update", collection: target.collection, filter, update, many: method === "updateMany" }; + } + + for (const method of ["deleteOne", "deleteMany"] as const) { + const target = parseCollectionMethodTarget(source, method); + if (!target) continue; + const args = parseMethodArgs(source, target.methodCallIndex); + if (!args || args.length !== 1) return null; + const filter = normalizeJsonArgument(args[0]); + if (!filter) return null; + return { kind: "delete", collection: target.collection, filter, many: method === "deleteMany" }; + } + + return null; +} + export function mongoAggregateWriteStage(pipelineJson: string): "$out" | "$merge" | null { try { const pipeline = JSON.parse(pipelineJson); @@ -179,6 +229,15 @@ export function mongoCountToQueryResult(total: number, executionTimeMs: number): }; } +export function mongoWriteToQueryResult(affectedRows: number, executionTimeMs: number): QueryResult { + return { + columns: [], + rows: [], + affected_rows: affectedRows, + execution_time_ms: Math.max(0, Math.round(executionTimeMs)), + }; +} + function parseFindTarget(source: string): { collection: string; findCallIndex: number } | null { const direct = parseCollectionMethodTarget(source, "find"); if (direct) { @@ -217,7 +276,9 @@ function parseCollectionMethodTarget( function normalizeJsonArgument(value: string): string | null { const trimmed = value.trim(); if (!trimmed) return "{}"; - const preprocessed = trimmed.replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}'); + const preprocessed = quoteUnquotedObjectKeys( + convertSingleQuotedStrings(trimmed.replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}')), + ); try { JSON.parse(preprocessed); return preprocessed; @@ -226,6 +287,105 @@ function normalizeJsonArgument(value: string): string | null { } } +function parseMethodArgs(source: string, methodCallIndex: number): string[] | null { + const openIndex = source.indexOf("(", methodCallIndex); + const closeIndex = findMatchingParen(source, openIndex); + if (closeIndex < 0 || source.slice(closeIndex + 1).trim()) return null; + return splitTopLevel(source.slice(openIndex + 1, closeIndex)); +} + +function convertSingleQuotedStrings(source: string): string { + let result = ""; + let copiedUntil = 0; + let quote: string | null = null; + let start = 0; + let value = ""; + let escaped = false; + + for (let i = 0; i < source.length; i += 1) { + const char = source[i]; + if (!quote) { + if (char === "'") { + quote = char; + start = i; + value = ""; + escaped = false; + } else if (char === '"') { + quote = char; + } + continue; + } + + if (quote === '"') { + if (escaped) escaped = false; + else if (char === "\\") escaped = true; + else if (char === '"') quote = null; + continue; + } + + if (escaped) { + value += char; + escaped = false; + } else if (char === "\\") { + escaped = true; + } else if (char === "'") { + result += source.slice(copiedUntil, start) + JSON.stringify(value); + copiedUntil = i + 1; + quote = null; + } else { + value += char; + } + } + + return quote === "'" ? source : result + source.slice(copiedUntil); +} + +function quoteUnquotedObjectKeys(source: string): string { + let result = ""; + let quote: string | null = null; + let escaped = false; + + for (let i = 0; i < source.length; i += 1) { + const char = source[i]; + if (quote) { + result += char; + if (escaped) escaped = false; + else if (char === "\\") escaped = true; + else if (char === quote) quote = null; + continue; + } + + if (char === '"' || char === "'") { + quote = char; + result += char; + continue; + } + + if (/[A-Za-z_$]/.test(char) && shouldQuoteObjectKey(source, i)) { + let end = i + 1; + while (/[\w$]/.test(source[end] || "")) end += 1; + result += `"${source.slice(i, end)}"`; + i = end - 1; + continue; + } + + result += char; + } + + return result; +} + +function shouldQuoteObjectKey(source: string, index: number): boolean { + let before = index - 1; + while (/\s/.test(source[before] || "")) before -= 1; + if (source[before] !== "{" && source[before] !== ",") return false; + + let after = index + 1; + while (/[\w$]/.test(source[after] || "")) after += 1; + while (/\s/.test(source[after] || "")) after += 1; + return source[after] === ":"; +} + function readChainedIntegerArgument(source: string, name: string, fallback: number): number | null { const raw = readChainedCallArgument(source, name); if (raw === undefined) return fallback; diff --git a/apps/desktop/src/lib/tauri.ts b/apps/desktop/src/lib/tauri.ts index a6dfa05ac..7fdab330d 100644 --- a/apps/desktop/src/lib/tauri.ts +++ b/apps/desktop/src/lib/tauri.ts @@ -1141,6 +1141,16 @@ export async function mongoInsertDocument( return invoke("mongo_insert_document", { connectionId, database, collection, docJson }); } +export async function mongoInsertDocuments( + connectionId: string, + database: string, + collection: string, + docsJson: string, +): Promise<{ affected_rows: number }> { + const affectedRows = await invoke("mongo_insert_documents", { connectionId, database, collection, docsJson }); + return { affected_rows: affectedRows }; +} + export async function mongoUpdateDocument( connectionId: string, database: string, @@ -1151,6 +1161,25 @@ export async function mongoUpdateDocument( return invoke("mongo_update_document", { connectionId, database, collection, id, docJson }); } +export async function mongoUpdateDocuments( + connectionId: string, + database: string, + collection: string, + filterJson: string, + updateJson: string, + many: boolean, +): Promise<{ affected_rows: number }> { + const affectedRows = await invoke("mongo_update_documents", { + connectionId, + database, + collection, + filterJson, + updateJson, + many, + }); + return { affected_rows: affectedRows }; +} + export async function mongoDeleteDocument( connectionId: string, database: string, @@ -1160,6 +1189,23 @@ export async function mongoDeleteDocument( return invoke("mongo_delete_document", { connectionId, database, collection, id }); } +export async function mongoDeleteDocuments( + connectionId: string, + database: string, + collection: string, + filterJson: string, + many: boolean, +): Promise<{ affected_rows: number }> { + const affectedRows = await invoke("mongo_delete_documents", { + connectionId, + database, + collection, + filterJson, + many, + }); + return { affected_rows: affectedRows }; +} + // --- History --- export interface HistoryEntry { id: string; diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index e8f9ab7e5..a102065eb 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -13,9 +13,11 @@ import { evaluateMongoAggregateSafety, mongoCountToQueryResult, mongoDocumentsToQueryResult, + mongoWriteToQueryResult, parseMongoAggregateCommand, parseMongoCountDocumentsCommand, parseMongoFindCommand, + parseMongoWriteCommand, type MongoAggregateSafetyOptions, } from "@/lib/mongoShellCommand"; import { AGENT_DRIVER_TYPES } from "@/lib/databaseCapabilities"; @@ -797,6 +799,64 @@ export const useQueryStore = defineStore("query", () => { return; } + const mongoWrite = conn?.db_type === "mongodb" ? parseMongoWriteCommand(sql) : null; + if (mongoWrite) { + await connStore.ensureConnected(tab.connectionId); + console.info("[DBX][executeTabSql:mongo-write:start]", { + traceId, + kind: mongoWrite.kind, + collection: mongoWrite.collection, + }); + let affectedRows = 0; + if (mongoWrite.kind === "insert") { + const result = await api.mongoInsertDocuments( + tab.connectionId, + tab.database, + mongoWrite.collection, + mongoWrite.docsJson, + ); + affectedRows = result.affected_rows; + } else if (mongoWrite.kind === "update") { + const result = await api.mongoUpdateDocuments( + tab.connectionId, + tab.database, + mongoWrite.collection, + mongoWrite.filter, + mongoWrite.update, + mongoWrite.many, + ); + affectedRows = result.affected_rows; + } else { + const result = await api.mongoDeleteDocuments( + tab.connectionId, + tab.database, + mongoWrite.collection, + mongoWrite.filter, + mongoWrite.many, + ); + affectedRows = result.affected_rows; + } + console.info("[DBX][executeTabSql:mongo-write:done]", { + traceId, + affectedRows, + elapsed: elapsed(), + }); + const current = tabs.value.find((t) => t.id === id); + if (current?.executionId === executionId) { + current.results = undefined; + current.activeResultIndex = undefined; + current.result = mongoWriteToQueryResult(affectedRows, performance.now() - startedAt); + touchResult(current); + current.queryAnalysis = undefined; + current.querySourceColumns = undefined; + current.queryEditabilityReason = undefined; + current.tableMeta = undefined; + current.resultBaseSql = options?.resultBaseSql ?? sql; + current.resultSortedSql = options?.resultSortedSql; + } + return; + } + console.info("[DBX][executeTabSql:execute-multi:start]", { traceId, elapsed: elapsed() }); const clientSessionId = tab.mode === "query" ? tab.id : undefined; const executionOptions = { diff --git a/packages/app-tests/mongoShellCommand.test.ts b/packages/app-tests/mongoShellCommand.test.ts index 1a5879ba6..a5c898e98 100644 --- a/packages/app-tests/mongoShellCommand.test.ts +++ b/packages/app-tests/mongoShellCommand.test.ts @@ -8,6 +8,7 @@ import { parseMongoAggregateCommand, parseMongoCountDocumentsCommand, parseMongoFindCommand, + parseMongoWriteCommand, } from "../../apps/desktop/src/lib/mongoShellCommand.ts"; test("parseMongoFindCommand parses db collection find with an empty JSON filter", () => { @@ -35,8 +36,43 @@ test("parseMongoFindCommand parses getCollection find with chained sort skip and ); }); +test("parseMongoFindCommand accepts Compass-style unquoted keys and ObjectId", () => { + const command = parseMongoFindCommand("db.products.find({_id: ObjectId('6a045a92d2971e44243771a1')}).limit(1)"); + assert.ok(command); + assert.equal(command.collection, "products"); + assert.equal(command.limit, 1); + assert.deepEqual(JSON.parse(command.filter), { _id: { $oid: "6a045a92d2971e44243771a1" } }); +}); + +test("parseMongoFindCommand accepts single-quoted string values and unquoted sort keys", () => { + const command = parseMongoFindCommand("db.products.find({category: 'Electronics'}).sort({price: -1}).limit(2)"); + assert.ok(command); + assert.equal(command.collection, "products"); + assert.equal(command.limit, 2); + assert.deepEqual(JSON.parse(command.filter), { category: "Electronics" }); + assert.deepEqual(JSON.parse(command.sort || "{}"), { price: -1 }); +}); + test("parseMongoFindCommand rejects unsupported mongo shell commands", () => { - assert.equal(parseMongoFindCommand("db.users.insertOne({})"), null); + assert.equal(parseMongoFindCommand("db.users.drop()"), null); +}); + +test("parseMongoWriteCommand accepts unquoted insert and update commands", () => { + assert.deepEqual(parseMongoWriteCommand("db.products.insertOne({name: 'demo', price: 1})"), { + kind: "insert", + collection: "products", + docsJson: '{"name": "demo", "price": 1}', + }); + assert.deepEqual( + parseMongoWriteCommand("db.products.updateOne({_id: ObjectId('507f1f77bcf86cd799439011')}, {$set: {stock: 3}})"), + { + kind: "update", + collection: "products", + filter: '{"_id": {"$oid":"507f1f77bcf86cd799439011"}}', + update: '{"$set": {"stock": 3}}', + many: false, + }, + ); }); test("parseMongoCountDocumentsCommand parses db collection countDocuments", () => { @@ -47,10 +83,13 @@ test("parseMongoCountDocumentsCommand parses db collection countDocuments", () = }); test("parseMongoAggregateCommand parses db collection aggregate", () => { - assert.deepEqual(parseMongoAggregateCommand('db.products.aggregate([{"$match":{"active":true}},{"$count":"total"}])'), { - collection: "products", - pipeline: '[{"$match":{"active":true}},{"$count":"total"}]', - }); + assert.deepEqual( + parseMongoAggregateCommand('db.products.aggregate([{"$match":{"active":true}},{"$count":"total"}])'), + { + collection: "products", + pipeline: '[{"$match":{"active":true}},{"$count":"total"}]', + }, + ); }); test("parseMongoAggregateCommand accepts an empty pipeline", () => { @@ -68,13 +107,13 @@ test("parseMongoAggregateCommand rejects non-array pipelines and extra arguments test("parseMongoAggregateCommand normalises ObjectId arguments with either quote style", () => { const oid = "507f1f77bcf86cd799439011"; - for (const quote of ["\"", "'"]) { + for (const quote of ['"', "'"]) { const command = parseMongoAggregateCommand( `db.orders.aggregate([{"$match":{"_id":ObjectId(${quote}${oid}${quote})}}])`, ); assert.ok(command, `quote=${quote} should parse`); assert.equal(command.collection, "orders"); - assert.deepEqual(JSON.parse(command.pipeline), [{ "$match": { "_id": { "$oid": oid } } }]); + assert.deepEqual(JSON.parse(command.pipeline), [{ $match: { _id: { $oid: oid } } }]); } }); @@ -87,7 +126,10 @@ test("evaluateMongoAggregateSafety blocks write stages unless MCP write flags al const merge = parseMongoAggregateCommand('db.products.aggregate([{"$merge":{"into":"products_copy"}}])'); assert.ok(merge); assert.equal(mongoAggregateWriteStage(merge.pipeline), "$merge"); - assert.match(evaluateMongoAggregateSafety(merge, { allowWrites: true }).reason || "", /DBX_MCP_ALLOW_DANGEROUS_SQL=1/); + assert.match( + evaluateMongoAggregateSafety(merge, { allowWrites: true }).reason || "", + /DBX_MCP_ALLOW_DANGEROUS_SQL=1/, + ); assert.equal(evaluateMongoAggregateSafety(merge, { allowWrites: true, allowDangerous: true }).allowed, true); }); diff --git a/packages/node-core/src/database.ts b/packages/node-core/src/database.ts index f360e355d..abc2d4f6a 100644 --- a/packages/node-core/src/database.ts +++ b/packages/node-core/src/database.ts @@ -849,7 +849,9 @@ function readChainedIntegerArgument(chain: string, method: string, fallback: num } function normalizeJsonArgument(arg: string): string | null { - const value = (arg.trim() || "{}").replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}'); + const value = quoteUnquotedObjectKeys( + convertSingleQuotedStrings((arg.trim() || "{}").replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}')), + ); try { JSON.parse(value); return value; @@ -858,6 +860,98 @@ function normalizeJsonArgument(arg: string): string | null { } } +function convertSingleQuotedStrings(source: string): string { + let result = ""; + let copiedUntil = 0; + let quote: string | null = null; + let start = 0; + let value = ""; + let escaped = false; + + for (let i = 0; i < source.length; i += 1) { + const char = source[i]; + if (!quote) { + if (char === "'") { + quote = char; + start = i; + value = ""; + escaped = false; + } else if (char === '"') { + quote = char; + } + continue; + } + + if (quote === '"') { + if (escaped) escaped = false; + else if (char === "\\") escaped = true; + else if (char === '"') quote = null; + continue; + } + + if (escaped) { + value += char; + escaped = false; + } else if (char === "\\") { + escaped = true; + } else if (char === "'") { + result += source.slice(copiedUntil, start) + JSON.stringify(value); + copiedUntil = i + 1; + quote = null; + } else { + value += char; + } + } + + return quote === "'" ? source : result + source.slice(copiedUntil); +} + +function quoteUnquotedObjectKeys(source: string): string { + let result = ""; + let quote: string | null = null; + let escaped = false; + + for (let i = 0; i < source.length; i += 1) { + const char = source[i]; + if (quote) { + result += char; + if (escaped) escaped = false; + else if (char === "\\") escaped = true; + else if (char === quote) quote = null; + continue; + } + + if (char === '"' || char === "'") { + quote = char; + result += char; + continue; + } + + if (/[A-Za-z_$]/.test(char) && shouldQuoteObjectKey(source, i)) { + let end = i + 1; + while (/[\w$]/.test(source[end] || "")) end += 1; + result += `"${source.slice(i, end)}"`; + i = end - 1; + continue; + } + + result += char; + } + + return result; +} + +function shouldQuoteObjectKey(source: string, index: number): boolean { + let before = index - 1; + while (/\s/.test(source[before] || "")) before -= 1; + if (source[before] !== "{" && source[before] !== ",") return false; + + let after = index + 1; + while (/[\w$]/.test(source[after] || "")) after += 1; + while (/\s/.test(source[after] || "")) after += 1; + return source[after] === ":"; +} + function isEmptyJsonObject(json: string): boolean { try { const parsed = JSON.parse(json); diff --git a/packages/node-core/tests/mongo-query.test.ts b/packages/node-core/tests/mongo-query.test.ts index a43fa08b3..f6f39b397 100644 --- a/packages/node-core/tests/mongo-query.test.ts +++ b/packages/node-core/tests/mongo-query.test.ts @@ -21,6 +21,24 @@ test("parseMongoFindCommand accepts shell-style find commands", () => { }); }); +test("parseMongoFindCommand accepts Compass-style unquoted keys and ObjectId", () => { + const command = parseMongoFindCommand("db.products.find({_id: ObjectId('6a045a92d2971e44243771a1')}).limit(1)"); + assert.ok(command); + assert.equal(command.collection, "products"); + assert.equal(command.limit, 1); + assert.deepEqual(JSON.parse(command.filter), { _id: { $oid: "6a045a92d2971e44243771a1" } }); +}); + +test("parseMongoWriteCommand accepts unquoted update operator keys", () => { + assert.deepEqual(parseMongoWriteCommand("db.projects.updateOne({_id: ObjectId('507f1f77bcf86cd799439011')}, {$set: {name: 'next'}})"), { + kind: "update", + collection: "projects", + filter: '{"_id": {"$oid":"507f1f77bcf86cd799439011"}}', + update: '{"$set": {"name": "next"}}', + many: false, + }); +}); + test("parseMongoCountDocumentsCommand accepts shell-style count commands", () => { assert.deepEqual(parseMongoCountDocumentsCommand('db.projects.countDocuments({"active":true})'), { collection: "projects", diff --git a/src-tauri/src/commands/mongo_cmd.rs b/src-tauri/src/commands/mongo_cmd.rs index 65069eaeb..5c4f9e313 100644 --- a/src-tauri/src/commands/mongo_cmd.rs +++ b/src-tauri/src/commands/mongo_cmd.rs @@ -76,6 +76,17 @@ pub async fn mongo_insert_document( dbx_core::mongo_ops::mongo_insert_document_core(&state, &connection_id, &database, &collection, &doc_json).await } +#[tauri::command] +pub async fn mongo_insert_documents( + state: State<'_, Arc>, + connection_id: String, + database: String, + collection: String, + docs_json: String, +) -> Result { + dbx_core::mongo_ops::mongo_insert_documents_core(&state, &connection_id, &database, &collection, &docs_json).await +} + #[tauri::command] pub async fn mongo_update_document( state: State<'_, Arc>, @@ -89,6 +100,28 @@ pub async fn mongo_update_document( .await } +#[tauri::command] +pub async fn mongo_update_documents( + state: State<'_, Arc>, + connection_id: String, + database: String, + collection: String, + filter_json: String, + update_json: String, + many: bool, +) -> Result { + dbx_core::mongo_ops::mongo_update_documents_core( + &state, + &connection_id, + &database, + &collection, + &filter_json, + &update_json, + many, + ) + .await +} + #[tauri::command] pub async fn mongo_delete_document( state: State<'_, Arc>, @@ -99,3 +132,16 @@ pub async fn mongo_delete_document( ) -> Result { dbx_core::mongo_ops::mongo_delete_document_core(&state, &connection_id, &database, &collection, &id).await } + +#[tauri::command] +pub async fn mongo_delete_documents( + state: State<'_, Arc>, + connection_id: String, + database: String, + collection: String, + filter_json: String, + many: bool, +) -> Result { + dbx_core::mongo_ops::mongo_delete_documents_core(&state, &connection_id, &database, &collection, &filter_json, many) + .await +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 3aa6703bd..0885c0f1d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -480,8 +480,11 @@ pub fn run() { commands::mongo_cmd::mongo_find_documents, commands::mongo_cmd::mongo_aggregate_documents, commands::mongo_cmd::mongo_insert_document, + commands::mongo_cmd::mongo_insert_documents, commands::mongo_cmd::mongo_update_document, + commands::mongo_cmd::mongo_update_documents, commands::mongo_cmd::mongo_delete_document, + commands::mongo_cmd::mongo_delete_documents, commands::history::save_history, commands::history::load_history, commands::history::clear_history,