diff --git a/src/mongoClusters/MongoClusterSession.ts b/src/mongoClusters/MongoClusterSession.ts index 0b651198..9c5a85d9 100644 --- a/src/mongoClusters/MongoClusterSession.ts +++ b/src/mongoClusters/MongoClusterSession.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { type Document, type WithId } from 'mongodb'; +import { EJSON } from 'bson'; +import { ObjectId, type Document, type WithId } from 'mongodb'; import { type JSONSchema } from '../utils/json/JSONSchema'; import { getPropertyNamesAtLevel, updateSchemaWithDocument } from '../utils/json/mongo/SchemaAnalyzer'; import { getDataAtPath } from '../utils/slickgrid/mongo/toSlickGridTable'; @@ -109,6 +110,45 @@ export class MongoClustersSession { return toSlickGridTree(this._currentRawDocuments); } + async deleteDocuments(databaseName: string, collectionName: string, documentIds: string[]): Promise { + const acknowledged = await this._client.deleteDocuments(databaseName, collectionName, documentIds); + + if (acknowledged) { + this._currentRawDocuments = this._currentRawDocuments.filter((doc) => { + // Convert documentIds to BSON types and compare them with doc._id + return !documentIds.some((id) => { + let parsedId; + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + parsedId = EJSON.parse(id); + } catch { + if (ObjectId.isValid(id)) { + parsedId = new ObjectId(id); + } else { + parsedId = id; + } + } + + /** + * deep equality for _id is tricky as we'd have to consider embedded objects, + * arrays, etc. For now, we'll just stringify the _id and compare the strings. + * The reasoning here is that this operation is used during interactive work + * and were not expecting to delete a large number of documents at once. + * Hence, the performance impact of this approach is negligible, and it's more + * about simplicity here. + */ + + const docIdStr = EJSON.stringify(doc._id, { relaxed: false }, 0); + const parsedIdStr = EJSON.stringify(parsedId, { relaxed: false }, 0); + + return docIdStr === parsedIdStr; + }); + }); + } + + return acknowledged; + } + public getCurrentPageAsTable(path: string[]): TableData { const responsePack: TableData = { path: path, diff --git a/src/mongoClusters/MongoClustersClient.ts b/src/mongoClusters/MongoClustersClient.ts index 727fbaf2..8c0d9d85 100644 --- a/src/mongoClusters/MongoClustersClient.ts +++ b/src/mongoClusters/MongoClustersClient.ts @@ -213,27 +213,27 @@ export class MongoClustersClient { } } + // TODO: revisit, maybe we can work on BSON here for the documentIds, and the conversion from string etc., + // will remain in the MongoClusterSession class async deleteDocuments(databaseName: string, collectionName: string, documentIds: string[]): Promise { - // convert input data - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const parsedDocumentIds: any[] = documentIds.map((id) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let parsedId: any; + // Convert input data to BSON types + const parsedDocumentIds = documentIds.map((id) => { + let parsedId; try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment parsedId = EJSON.parse(id); - } catch (error) { + } catch { if (ObjectId.isValid(id)) { parsedId = new ObjectId(id); } else { - throw error; + throw new Error(`Invalid document ID: ${id}`); } } // eslint-disable-next-line @typescript-eslint/no-unsafe-return return parsedId; }); - // connect and extecute + // Connect and execute const collection = this._mongoClient.db(databaseName).collection(collectionName); const deleteResult: DeleteResult = await collection.deleteMany({ _id: { $in: parsedDocumentIds } }); diff --git a/src/webviews/mongoClusters/collectionView/CollectionView.tsx b/src/webviews/mongoClusters/collectionView/CollectionView.tsx index b5c96814..9d4a155e 100644 --- a/src/webviews/mongoClusters/collectionView/CollectionView.tsx +++ b/src/webviews/mongoClusters/collectionView/CollectionView.tsx @@ -224,9 +224,11 @@ export const CollectionView = (): JSX.Element => { return; } - // TODO: update cached data in the controller - - // TODO: update the current view, not all views. + /** + * The data on the server has been deleted and our extension code has updated its + * cache as well. Now we need to update the view locally, so that the user sees + * the changes immediately without potential focus/table resizing issues etc. + */ setCurrentQueryResults((prev) => ({ ...prev, @@ -248,9 +250,9 @@ export const CollectionView = (): JSX.Element => { }) .catch((error: unknown) => { if (error instanceof Error) { - console.error('Error adding document:', error.message); + console.error('Error deleting the document:', error.message); } else { - console.error('Unexpected error adding document:', error); + console.error('Unexpected error when deleting a document:', error); } }); } diff --git a/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts b/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts index 950de065..8c7ea767 100644 --- a/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts +++ b/src/webviews/mongoClusters/collectionView/collectionViewRouter.ts @@ -6,7 +6,6 @@ import * as vscode from 'vscode'; import { type JSONSchema } from 'vscode-json-languageservice'; import { z } from 'zod'; -import { type MongoClustersClient } from '../../../mongoClusters/MongoClustersClient'; import { MongoClustersSession } from '../../../mongoClusters/MongoClusterSession'; import { getConfirmationAsInSettings } from '../../../utils/dialogs/getConfirmation'; import { getKnownFields, type FieldEntry } from '../../../utils/json/mongo/autocomplete/getKnownFields'; @@ -169,9 +168,8 @@ export const collectionsViewRouter = router({ return false; } - const client: MongoClustersClient = MongoClustersSession.getSession(myCtx.sessionId).getClient(); - - const acknowledged = await client.deleteDocuments(myCtx.databaseName, myCtx.collectionName, input); + const session: MongoClustersSession = MongoClustersSession.getSession(myCtx.sessionId); + const acknowledged = await session.deleteDocuments(myCtx.databaseName, myCtx.collectionName, input); if (acknowledged) { showConfirmationAsInSettings( @@ -187,9 +185,7 @@ export const collectionsViewRouter = router({ input.length, ), ); - } - - if (!acknowledged) { + } else { void vscode.window.showErrorMessage('Failed to delete documents. Unknown error.', { modal: true, });