From c710e0bc0b13686611201ff38d58e9f10b22158f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 13 Jul 2025 18:00:18 -0400 Subject: [PATCH 1/3] feat(NODE-4184): do not throw with explain and write conceern --- src/operations/aggregate.ts | 6 ------ test/unit/operations/aggregate.test.js | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index a11365a9e89..66d322f300c 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -85,12 +85,6 @@ export class AggregateOperation extends CommandOperation { delete this.options.writeConcern; } - if (this.explain && this.writeConcern) { - throw new MongoInvalidArgumentError( - 'Option "explain" cannot be used on an aggregate call with writeConcern' - ); - } - if (options?.cursor != null && typeof options.cursor !== 'object') { throw new MongoInvalidArgumentError('Cursor options must be an object'); } diff --git a/test/unit/operations/aggregate.test.js b/test/unit/operations/aggregate.test.js index 118140d95f9..1b179771ddf 100644 --- a/test/unit/operations/aggregate.test.js +++ b/test/unit/operations/aggregate.test.js @@ -7,6 +7,23 @@ describe('AggregateOperation', function () { const db = 'test'; describe('#constructor', function () { + context('when explain is in the options', function () { + context('when writeConcern is set', function () { + const operation = new AggregateOperation(db, [], { + explain: 'verbose', + writeConcern: { w: 1 } + }); + + it('sets explain on the operation', function () { + expect(operation.explain.verbosity).to.equal('verbose'); + }); + + it('sets the write concern on the operation', function () { + expect(operation.writeConcern.w).to.equal(1); + }); + }); + }); + context('when out is in the options', function () { const operation = new AggregateOperation(db, [], { out: 'test', dbName: db }); From 9edc40fe7e3e2e2ee104bb63bbb970042338ce94 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 13 Jul 2025 18:18:41 -0400 Subject: [PATCH 2/3] test: fix integration test --- test/integration/crud/aggregation.test.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/integration/crud/aggregation.test.ts b/test/integration/crud/aggregation.test.ts index e544bde3334..bf4865077bd 100644 --- a/test/integration/crud/aggregation.test.ts +++ b/test/integration/crud/aggregation.test.ts @@ -640,31 +640,29 @@ describe('Aggregation', function () { expect(error).to.be.instanceOf(MongoInvalidArgumentError); }); - it(`should fail if you try to use explain flag with { readConcern: { level: 'local' }, writeConcern: { j: true } }`, async function () { + it(`should succeed if you try to use explain flag with { readConcern: { level: 'local' }, writeConcern: { j: true } }`, async function () { const db = client.db(); const collection = db.collection('foo'); Object.assign(collection.s, { writeConcern: { j: true } }); - const error = await collection + const result = await collection .aggregate([{ $project: { _id: 0 } }, { $out: 'bar' }], { explain: true }) - .toArray() - .catch(error => error); + .toArray(); - expect(error).to.be.instanceOf(MongoInvalidArgumentError); + expect(result).to.not.be.null; }); - it('should fail if you try to use explain flag with { writeConcern: { j: true } }', async function () { + it('should succeed if you try to use explain flag with { writeConcern: { j: true } }', async function () { const db = client.db(); const collection = db.collection('foo'); Object.assign(collection.s, { writeConcern: { j: true } }); - const error = await collection + const result = await collection .aggregate([{ $project: { _id: 0 } }, { $out: 'bar' }], { explain: true }) - .toArray() - .catch(error => error); + .toArray(); - expect(error).to.be.instanceOf(MongoInvalidArgumentError); + expect(result).to.not.be.null; }); it('should ensure MaxTimeMS is correctly passed down into command execution when using a cursor', function (done) { From 10cb7b4fef2410c7339bb3d87efa13be18781cf7 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Sun, 13 Jul 2025 18:43:04 -0400 Subject: [PATCH 3/3] Revert "test: fix integration test" This reverts commit 9edc40fe7e3e2e2ee104bb63bbb970042338ce94. --- test/integration/crud/aggregation.test.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/integration/crud/aggregation.test.ts b/test/integration/crud/aggregation.test.ts index bf4865077bd..e544bde3334 100644 --- a/test/integration/crud/aggregation.test.ts +++ b/test/integration/crud/aggregation.test.ts @@ -640,29 +640,31 @@ describe('Aggregation', function () { expect(error).to.be.instanceOf(MongoInvalidArgumentError); }); - it(`should succeed if you try to use explain flag with { readConcern: { level: 'local' }, writeConcern: { j: true } }`, async function () { + it(`should fail if you try to use explain flag with { readConcern: { level: 'local' }, writeConcern: { j: true } }`, async function () { const db = client.db(); const collection = db.collection('foo'); Object.assign(collection.s, { writeConcern: { j: true } }); - const result = await collection + const error = await collection .aggregate([{ $project: { _id: 0 } }, { $out: 'bar' }], { explain: true }) - .toArray(); + .toArray() + .catch(error => error); - expect(result).to.not.be.null; + expect(error).to.be.instanceOf(MongoInvalidArgumentError); }); - it('should succeed if you try to use explain flag with { writeConcern: { j: true } }', async function () { + it('should fail if you try to use explain flag with { writeConcern: { j: true } }', async function () { const db = client.db(); const collection = db.collection('foo'); Object.assign(collection.s, { writeConcern: { j: true } }); - const result = await collection + const error = await collection .aggregate([{ $project: { _id: 0 } }, { $out: 'bar' }], { explain: true }) - .toArray(); + .toArray() + .catch(error => error); - expect(result).to.not.be.null; + expect(error).to.be.instanceOf(MongoInvalidArgumentError); }); it('should ensure MaxTimeMS is correctly passed down into command execution when using a cursor', function (done) {