diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 6361584066..e0444787eb 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -784,7 +784,7 @@ export class FindOperators { } /** Add a multiple update operation to the bulk operation */ - update(updateDocument: Document): BulkOperationBase { + update(updateDocument: Document | Document[]): BulkOperationBase { const currentOp = buildCurrentOp(this.bulkOperation); return this.bulkOperation.addToOperationsList( BatchType.UPDATE, @@ -796,7 +796,7 @@ export class FindOperators { } /** Add a single update operation to the bulk operation */ - updateOne(updateDocument: Document): BulkOperationBase { + updateOne(updateDocument: Document | Document[]): BulkOperationBase { if (!hasAtomicOperators(updateDocument)) { throw new MongoInvalidArgumentError('Update document requires atomic operators'); } @@ -868,6 +868,16 @@ export class FindOperators { this.bulkOperation.s.currentOp.arrayFilters = arrayFilters; return this; } + + /** Specifies hint for the bulk operation. */ + hint(hint: Hint): this { + if (!this.bulkOperation.s.currentOp) { + this.bulkOperation.s.currentOp = {}; + } + + this.bulkOperation.s.currentOp.hint = hint; + return this; + } } /** @internal */ diff --git a/src/operations/update.ts b/src/operations/update.ts index a389878a14..43760c1431 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -22,7 +22,7 @@ export interface UpdateOptions extends CommandOperationOptions { /** Specifies a collation */ collation?: CollationOptions; /** Specify that the update query should only consider plans using the hinted index */ - hint?: string | Document; + hint?: Hint; /** When true, creates a new document if no document matches the query */ upsert?: boolean; /** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */ @@ -280,7 +280,7 @@ export class ReplaceOneOperation extends UpdateOperation { export function makeUpdateStatement( filter: Document, - update: Document, + update: Document | Document[], options: UpdateOptions & { multi?: boolean } ): UpdateStatement { if (filter == null || typeof filter !== 'object') { diff --git a/test/integration/crud/bulk.test.ts b/test/integration/crud/bulk.test.ts index 786a99fb6f..70ea459b72 100644 --- a/test/integration/crud/bulk.test.ts +++ b/test/integration/crud/bulk.test.ts @@ -1688,6 +1688,51 @@ describe('Bulk', function () { } }); + it('should apply hint via FindOperators', { + metadata: { requires: { mongodb: '>= 4.4' } }, + async test() { + const bulk = client.db().collection('coll').initializeOrderedBulkOp(); + + const events = []; + client.on('commandStarted', event => { + if (['update', 'delete'].includes(event.commandName)) { + events.push(event); + } + }); + + // updates + bulk + .find({ b: 1 }) + .hint({ b: 1 }) + .updateOne({ $set: { b: 2 } }); + bulk + .find({ b: 2 }) + .hint({ b: 1 }) + .update({ $set: { b: 3 } }); + bulk.find({ b: 3 }).hint({ b: 1 }).replaceOne({ b: 2 }); + + // deletes + bulk.find({ b: 2 }).hint({ b: 1 }).deleteOne(); + bulk.find({ b: 1 }).hint({ b: 1 }).delete(); + + await bulk.execute(); + + expect(events).to.be.an('array').with.length.at.least(1); + expect(events[0]).property('commandName').to.equal('update'); + const updateCommand = events[0].command; + expect(updateCommand).property('updates').to.be.an('array').with.length(3); + updateCommand.updates.forEach(statement => { + expect(statement).property('hint').to.eql({ b: 1 }); + }); + expect(events[1]).property('commandName').to.equal('delete'); + const deleteCommand = events[1].command; + expect(deleteCommand).property('deletes').to.be.an('array').with.length(2); + deleteCommand.deletes.forEach(statement => { + expect(statement).property('hint').to.eql({ b: 1 }); + }); + } + }); + it('should apply arrayFilters to bulk updates via FindOperators', { metadata: { requires: { mongodb: '>= 3.6' } }, test: function (done) { @@ -1737,6 +1782,24 @@ describe('Bulk', function () { } }); + it('should accept pipeline-style updates', { + metadata: { requires: { mongodb: '>= 4.2' } }, + async test() { + const coll = client.db().collection('coll'); + const bulk = coll.initializeOrderedBulkOp(); + + coll.insertMany([{ a: 1 }, { a: 2 }]); + + bulk.find({ a: 1 }).updateOne([{ $project: { a: { $add: ['$a', 10] } } }]); + bulk.find({ a: 2 }).update([{ $project: { a: { $add: ['$a', 100] } } }]); + + await bulk.execute(); + + const contents = await coll.find().project({ _id: 0 }).toArray(); + expect(contents).to.deep.equal([{ a: 11 }, { a: 102 }]); + } + }); + it('should throw an error if raw operations are passed to bulkWrite', function () { const coll = client.db().collection('single_bulk_write_error'); return coll