2026-02-01 23:33:35 +00:00
|
|
|
import * as plugins from '../../plugins.js';
|
2026-01-31 11:33:11 +00:00
|
|
|
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
2026-02-01 16:02:03 +00:00
|
|
|
import type { IStoredDocument } from '../../types/interfaces.js';
|
2026-01-31 11:33:11 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* InsertHandler - Handles insert commands
|
|
|
|
|
*/
|
|
|
|
|
export class InsertHandler implements ICommandHandler {
|
|
|
|
|
async handle(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
|
|
|
const { storage, database, command, documentSequences } = context;
|
|
|
|
|
|
|
|
|
|
const collection = command.insert;
|
|
|
|
|
if (typeof collection !== 'string') {
|
|
|
|
|
return {
|
|
|
|
|
ok: 0,
|
|
|
|
|
errmsg: 'insert command requires a collection name',
|
|
|
|
|
code: 2,
|
|
|
|
|
codeName: 'BadValue',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get documents from command or document sequences
|
|
|
|
|
let documents: plugins.bson.Document[] = command.documents || [];
|
|
|
|
|
|
|
|
|
|
// Check for OP_MSG document sequences (for bulk inserts)
|
|
|
|
|
if (documentSequences && documentSequences.has('documents')) {
|
|
|
|
|
documents = documentSequences.get('documents')!;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Array.isArray(documents) || documents.length === 0) {
|
|
|
|
|
return {
|
|
|
|
|
ok: 0,
|
|
|
|
|
errmsg: 'insert command requires documents array',
|
|
|
|
|
code: 2,
|
|
|
|
|
codeName: 'BadValue',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ordered = command.ordered !== false;
|
|
|
|
|
const writeErrors: plugins.bson.Document[] = [];
|
|
|
|
|
let insertedCount = 0;
|
|
|
|
|
|
|
|
|
|
// Ensure collection exists
|
|
|
|
|
await storage.createCollection(database, collection);
|
|
|
|
|
|
2026-02-01 16:02:03 +00:00
|
|
|
const indexEngine = context.getIndexEngine(collection);
|
|
|
|
|
|
2026-01-31 11:33:11 +00:00
|
|
|
// Insert documents
|
|
|
|
|
for (let i = 0; i < documents.length; i++) {
|
|
|
|
|
const doc = documents[i];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Ensure _id exists
|
|
|
|
|
if (!doc._id) {
|
|
|
|
|
doc._id = new plugins.bson.ObjectId();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 16:02:03 +00:00
|
|
|
// Check index constraints before insert (doc now has _id)
|
|
|
|
|
await indexEngine.onInsert(doc as IStoredDocument);
|
|
|
|
|
|
2026-01-31 11:33:11 +00:00
|
|
|
await storage.insertOne(database, collection, doc);
|
|
|
|
|
insertedCount++;
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
const writeError: plugins.bson.Document = {
|
|
|
|
|
index: i,
|
|
|
|
|
code: error.code || 11000,
|
|
|
|
|
errmsg: error.message || 'Insert failed',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Check for duplicate key error
|
|
|
|
|
if (error.message?.includes('Duplicate key')) {
|
|
|
|
|
writeError.code = 11000;
|
|
|
|
|
writeError.keyPattern = { _id: 1 };
|
|
|
|
|
writeError.keyValue = { _id: doc._id };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writeErrors.push(writeError);
|
|
|
|
|
|
|
|
|
|
if (ordered) {
|
|
|
|
|
// Stop on first error for ordered inserts
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response: plugins.bson.Document = {
|
|
|
|
|
ok: 1,
|
|
|
|
|
n: insertedCount,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (writeErrors.length > 0) {
|
|
|
|
|
response.writeErrors = writeErrors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
}
|