From a4a3827e70c81219349a0eccf9607960133afad4 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 2 Jul 2026 12:47:04 +0100 Subject: [PATCH 1/2] fix: key unordered bulk insertedIds by originating operation index --- src/bulk/unordered.ts | 2 +- test/unit/bulk.test.ts | 63 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/unit/bulk.test.ts diff --git a/src/bulk/unordered.ts b/src/bulk/unordered.ts index c718377722..77c4f33082 100644 --- a/src/bulk/unordered.ts +++ b/src/bulk/unordered.ts @@ -112,7 +112,7 @@ export class UnorderedBulkOperation extends BulkOperationBase { if (batchType === BatchType.INSERT) { this.s.currentInsertBatch = this.s.currentBatch; this.s.bulkResult.insertedIds.push({ - index: this.s.bulkResult.insertedIds.length, + index: this.s.currentIndex - 1, _id: (document as Document)._id }); } else if (batchType === BatchType.UPDATE) { diff --git a/test/unit/bulk.test.ts b/test/unit/bulk.test.ts new file mode 100644 index 0000000000..88d4482659 --- /dev/null +++ b/test/unit/bulk.test.ts @@ -0,0 +1,63 @@ +import { expect } from 'chai'; + +import { + DEFAULT_PK_FACTORY, + MongoDBCollectionNamespace, + OrderedBulkOperation, + UnorderedBulkOperation +} from '../mongodb'; + +describe('Bulk Operation insertedIds', function () { + function makeFakeCollection() { + const topology = { + lastHello() { + return { maxBsonObjectSize: 16 * 1024 * 1024, maxWriteBatchSize: 1000 }; + }, + s: { options: {} } + }; + const collection: any = { + client: { topology }, + topology, + db: { options: {} }, + bsonOptions: {}, + s: { + namespace: new MongoDBCollectionNamespace('test', 'coll'), + pkFactory: DEFAULT_PK_FACTORY, + bsonOptions: {}, + collection: undefined + } + }; + collection.s.collection = collection; + return collection; + } + + function addMixedOperations(bulk: OrderedBulkOperation | UnorderedBulkOperation) { + bulk.raw({ insertOne: { document: { _id: 'a', x: 1 } } }); + bulk.raw({ updateOne: { filter: { x: 1 }, update: { $set: { y: 2 } } } }); + bulk.raw({ insertOne: { document: { _id: 'b', x: 3 } } }); + bulk.raw({ deleteOne: { filter: { x: 3 } } }); + bulk.raw({ insertOne: { document: { _id: 'c', x: 5 } } }); + } + + it('ordered bulk keys insertedIds by the originating operation index', function () { + const bulk = new OrderedBulkOperation(makeFakeCollection(), { ordered: true } as any); + addMixedOperations(bulk); + const insertedIds = (bulk as any).s.bulkResult.insertedIds; + expect(insertedIds).to.deep.equal([ + { index: 0, _id: 'a' }, + { index: 2, _id: 'b' }, + { index: 4, _id: 'c' } + ]); + }); + + it('unordered bulk keys insertedIds by the originating operation index', function () { + const bulk = new UnorderedBulkOperation(makeFakeCollection(), { ordered: false } as any); + addMixedOperations(bulk); + const insertedIds = (bulk as any).s.bulkResult.insertedIds; + expect(insertedIds).to.deep.equal([ + { index: 0, _id: 'a' }, + { index: 2, _id: 'b' }, + { index: 4, _id: 'c' } + ]); + }); +}); From ac1dbbc1d63e41df8837aea6cb4b9f65870a7bff Mon Sep 17 00:00:00 2001 From: Yarchik Date: Tue, 29 Sep 2026 20:17:26 +0100 Subject: [PATCH 2/2] test(NODE-7659): cover insertedIds when inserts are mixed with other operations The existing unordered bulkWrite case is four inserts, so the count of inserts seen and the originating operation index coincide and the mismatch is invisible. This adds an unordered bulkWrite whose inserts sit at operation indexes 0, 2 and 4, with the middle one failing on the unique index. getSuccessfullyInsertedIds filters insertedIds by comparing their index against writeErrors[].index, and that error index is remapped through batch.originalIndexes in common.ts, so the two must be in the same numbering. With the running count the assertion sees { 0: 0, 1: 2 } instead of { 0: 0, 4: 4 }. --- test/integration/crud/bulk.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/integration/crud/bulk.test.ts b/test/integration/crud/bulk.test.ts index 5390babe16..f48ae9e3cb 100644 --- a/test/integration/crud/bulk.test.ts +++ b/test/integration/crud/bulk.test.ts @@ -338,6 +338,23 @@ describe('Bulk', function () { { 0: 0, 3: 3 } ); }); + + it('contains the correct insertedIds when inserts are mixed with other operations', async function () { + // The inserts sit at operation indexes 0, 2 and 4, so the count of inserts seen + // differs from the originating operation index. The update and delete match nothing; + // they are only here to move the inserts off the positions a running count produces. + await assertFailsWithDuplicateFields( + [ + { insertOne: { _id: 0, a: 1 } }, + { updateOne: { filter: { nothing: 1 }, update: { $set: { b: 1 } } } }, + { insertOne: { _id: 2, a: 1 } }, + { deleteOne: { filter: { nothing: 1 } } }, + { insertOne: { _id: 4, a: 2 } } + ], + false, + { 0: 0, 4: 4 } + ); + }); }); }); });