Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vCore: syncs deleted documents across Table/Tree/JSON view #2512

Merged
merged 5 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion src/mongoClusters/MongoClusterSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -109,6 +110,45 @@ export class MongoClustersSession {
return toSlickGridTree(this._currentRawDocuments);
}

async deleteDocuments(databaseName: string, collectionName: string, documentIds: string[]): Promise<boolean> {
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,
Expand Down
16 changes: 8 additions & 8 deletions src/mongoClusters/MongoClustersClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
// 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 } });

Expand Down
12 changes: 7 additions & 5 deletions src/webviews/mongoClusters/collectionView/CollectionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(
Expand All @@ -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,
});
Expand Down
Loading