116 lines
3.4 KiB
TypeScript
116 lines
3.4 KiB
TypeScript
import * as plugins from '../../tsmdb.plugins.js';
|
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
import type { IStoredDocument } from '../../types/interfaces.js';
|
|
import { QueryEngine } from '../../engine/QueryEngine.js';
|
|
|
|
/**
|
|
* DeleteHandler - Handles delete commands
|
|
*/
|
|
export class DeleteHandler implements ICommandHandler {
|
|
async handle(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
const { storage, database, command, documentSequences } = context;
|
|
|
|
const collection = command.delete;
|
|
if (typeof collection !== 'string') {
|
|
return {
|
|
ok: 0,
|
|
errmsg: 'delete command requires a collection name',
|
|
code: 2,
|
|
codeName: 'BadValue',
|
|
};
|
|
}
|
|
|
|
// Get deletes from command or document sequences
|
|
let deletes: plugins.bson.Document[] = command.deletes || [];
|
|
|
|
// Check for OP_MSG document sequences
|
|
if (documentSequences && documentSequences.has('deletes')) {
|
|
deletes = documentSequences.get('deletes')!;
|
|
}
|
|
|
|
if (!Array.isArray(deletes) || deletes.length === 0) {
|
|
return {
|
|
ok: 0,
|
|
errmsg: 'delete command requires deletes array',
|
|
code: 2,
|
|
codeName: 'BadValue',
|
|
};
|
|
}
|
|
|
|
const ordered = command.ordered !== false;
|
|
const writeErrors: plugins.bson.Document[] = [];
|
|
let totalDeleted = 0;
|
|
|
|
// Check if collection exists
|
|
const exists = await storage.collectionExists(database, collection);
|
|
if (!exists) {
|
|
// Collection doesn't exist, return success with 0 deleted
|
|
return { ok: 1, n: 0 };
|
|
}
|
|
|
|
const indexEngine = context.getIndexEngine(collection);
|
|
|
|
for (let i = 0; i < deletes.length; i++) {
|
|
const deleteSpec = deletes[i];
|
|
const filter = deleteSpec.q || deleteSpec.filter || {};
|
|
const limit = deleteSpec.limit;
|
|
|
|
// limit: 0 means delete all matching, limit: 1 means delete one
|
|
const deleteAll = limit === 0;
|
|
|
|
try {
|
|
// Try to use index-accelerated query
|
|
const candidateIds = await indexEngine.findCandidateIds(filter);
|
|
|
|
let documents: IStoredDocument[];
|
|
if (candidateIds !== null) {
|
|
documents = await storage.findByIds(database, collection, candidateIds);
|
|
} else {
|
|
documents = await storage.findAll(database, collection);
|
|
}
|
|
|
|
// Apply filter
|
|
const matchingDocs = QueryEngine.filter(documents, filter);
|
|
|
|
if (matchingDocs.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
// Determine which documents to delete
|
|
const docsToDelete = deleteAll ? matchingDocs : matchingDocs.slice(0, 1);
|
|
|
|
// Update indexes for deleted documents
|
|
for (const doc of docsToDelete) {
|
|
await indexEngine.onDelete(doc as any);
|
|
}
|
|
|
|
// Delete the documents
|
|
const idsToDelete = docsToDelete.map(doc => doc._id);
|
|
const deleted = await storage.deleteByIds(database, collection, idsToDelete);
|
|
totalDeleted += deleted;
|
|
} catch (error: any) {
|
|
writeErrors.push({
|
|
index: i,
|
|
code: error.code || 1,
|
|
errmsg: error.message || 'Delete failed',
|
|
});
|
|
|
|
if (ordered) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const response: plugins.bson.Document = {
|
|
ok: 1,
|
|
n: totalDeleted,
|
|
};
|
|
|
|
if (writeErrors.length > 0) {
|
|
response.writeErrors = writeErrors;
|
|
}
|
|
|
|
return response;
|
|
}
|
|
}
|