Skip to content

Commit 0cca729

Browse files
authored
feat: add withReadConcern builder to AbstractCursor (#2645)
This patch adds a builder method to add a read concern to a cursor similar to the existing method to build with a ReadPreference. It also changes `setReadPreference` to `withReadPreference` so the two follow a common convention. NODE-2806
1 parent 8d44cc2 commit 0cca729

File tree

10 files changed

+72
-20
lines changed

10 files changed

+72
-20
lines changed

docs/reference/content/tutorials/crud.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ collection.find({}).max(10) // Set the cursor
699699
collection.find({}).maxTimeMS(1000) // Set the cursor maxTimeMS
700700
collection.find({}).min(100) // Set the cursor min
701701
collection.find({}).returnKey(10) // Set the cursor returnKey
702-
collection.find({}).setReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreference
702+
collection.find({}).withReadPreference(ReadPreference.PRIMARY) // Set the cursor readPreference
703+
collection.find({}).withReadConcern('majority') // Set the cursor readConcern
703704
collection.find({}).showRecordId(true) // Set the cursor showRecordId
704705
collection.find({}).sort([['a', 1]]) // Sets the sort order of the cursor query
705706
collection.find({}).hint('a_1') // Set the cursor hint

src/cursor/abstract_cursor.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Topology } from '../sdam/topology';
88
import { Readable, Transform } from 'stream';
99
import { EventEmitter } from 'events';
1010
import type { ExecutionResult } from '../operations/execute_operation';
11+
import { ReadConcern, ReadConcernLike } from '../read_concern';
1112

1213
const kId = Symbol('id');
1314
const kDocuments = Symbol('documents');
@@ -50,6 +51,7 @@ export type CursorFlag = typeof CURSOR_FLAGS[number];
5051
export interface AbstractCursorOptions extends BSONSerializeOptions {
5152
session?: ClientSession;
5253
readPreference?: ReadPreferenceLike;
54+
readConcern?: ReadConcernLike;
5355
batchSize?: number;
5456
maxTimeMS?: number;
5557
comment?: Document | string;
@@ -62,6 +64,7 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
6264
export type InternalAbstractCursorOptions = Omit<AbstractCursorOptions, 'readPreference'> & {
6365
// resolved
6466
readPreference: ReadPreference;
67+
readConcern?: ReadConcern;
6568

6669
// cursor flags, some are deprecated
6770
oplogReplay?: boolean;
@@ -107,6 +110,11 @@ export abstract class AbstractCursor extends EventEmitter {
107110
...pluckBSONSerializeOptions(options)
108111
};
109112

113+
const readConcern = ReadConcern.fromOptions(options);
114+
if (readConcern) {
115+
this[kOptions].readConcern = readConcern;
116+
}
117+
110118
if (typeof options.batchSize === 'number') {
111119
this[kOptions].batchSize = options.batchSize;
112120
}
@@ -144,6 +152,10 @@ export abstract class AbstractCursor extends EventEmitter {
144152
return this[kOptions].readPreference;
145153
}
146154

155+
get readConcern(): ReadConcern | undefined {
156+
return this[kOptions].readConcern;
157+
}
158+
147159
get session(): ClientSession | undefined {
148160
return this[kSession];
149161
}
@@ -434,7 +446,7 @@ export abstract class AbstractCursor extends EventEmitter {
434446
*
435447
* @param readPreference - The new read preference for the cursor.
436448
*/
437-
setReadPreference(readPreference: ReadPreferenceLike): this {
449+
withReadPreference(readPreference: ReadPreferenceLike): this {
438450
assertUninitialized(this);
439451
if (readPreference instanceof ReadPreference) {
440452
this[kOptions].readPreference = readPreference;
@@ -447,6 +459,21 @@ export abstract class AbstractCursor extends EventEmitter {
447459
return this;
448460
}
449461

462+
/**
463+
* Set the ReadPreference for the cursor.
464+
*
465+
* @param readPreference - The new read preference for the cursor.
466+
*/
467+
withReadConcern(readConcern: ReadConcernLike): this {
468+
assertUninitialized(this);
469+
const resolvedReadConcern = ReadConcern.fromOptions({ readConcern });
470+
if (resolvedReadConcern) {
471+
this[kOptions].readConcern = resolvedReadConcern;
472+
}
473+
474+
return this;
475+
}
476+
450477
/**
451478
* Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
452479
*

src/gridfs-stream/download.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ function init(stream: GridFSBucketReadStream): void {
349349
stream.s.cursor = stream.s.chunks.find(filter).sort({ n: 1 });
350350

351351
if (stream.s.readPreference) {
352-
stream.s.cursor.setReadPreference(stream.s.readPreference);
352+
stream.s.cursor.withReadPreference(stream.s.readPreference);
353353
}
354354

355355
stream.s.expectedEnd = Math.ceil(doc.length / doc.chunkSize);

src/operations/find.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,8 @@ export class FindOperation extends CommandOperation<FindOptions, Document> {
173173
findCommand.maxTimeMS = options.maxTimeMS;
174174
}
175175

176-
if (this.readConcern && (!this.session || !this.session.inTransaction())) {
177-
findCommand.readConcern = this.readConcern;
176+
if (this.readConcern) {
177+
findCommand.readConcern = this.readConcern.toJSON();
178178
}
179179

180180
if (options.max) {

src/read_concern.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Document } from './bson';
2+
13
/** @public */
24
export enum ReadConcernLevel {
35
local = 'local',
@@ -78,4 +80,8 @@ export class ReadConcern {
7880
static get SNAPSHOT(): string {
7981
return ReadConcernLevel.snapshot;
8082
}
83+
84+
toJSON(): Document {
85+
return { level: this.level };
86+
}
8187
}

test/functional/apm.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ describe('APM', function () {
373373
.batchSize(2)
374374
.comment('some comment')
375375
.maxTimeMS(5000)
376-
.setReadPreference(ReadPreference.PRIMARY)
376+
.withReadPreference(ReadPreference.PRIMARY)
377377
.addCursorFlag('noCursorTimeout', true)
378378
.toArray();
379379
})
@@ -445,7 +445,7 @@ describe('APM', function () {
445445
.batchSize(2)
446446
.comment('some comment')
447447
.maxTimeMS(5000)
448-
.setReadPreference(ReadPreference.PRIMARY)
448+
.withReadPreference(ReadPreference.PRIMARY)
449449
.addCursorFlag('noCursorTimeout', true)
450450
.toArray();
451451
})

test/functional/buffering_proxy.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ describe.skip('Buffering Proxy', function () {
217217

218218
db.collection('test')
219219
.find({})
220-
.setReadPreference(new ReadPreference(ReadPreference.SECONDARY))
220+
.withReadPreference(new ReadPreference(ReadPreference.SECONDARY))
221221
.toArray(function (err) {
222222
expect(err).to.not.exist;
223223
results.push('find');
@@ -439,7 +439,7 @@ describe.skip('Buffering Proxy', function () {
439439

440440
db.collection('test')
441441
.find({})
442-
.setReadPreference(new ReadPreference(ReadPreference.SECONDARY))
442+
.withReadPreference(new ReadPreference(ReadPreference.SECONDARY))
443443
.toArray(function (err) {
444444
expect(err).to.not.exist;
445445
results.push('find');

test/functional/cursor.test.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,19 +2321,37 @@ describe('Cursor', function () {
23212321
try {
23222322
db.collection('shouldFailToSetReadPreferenceOnCursor')
23232323
.find()
2324-
.setReadPreference('notsecondary');
2324+
.withReadPreference('notsecondary');
23252325
test.ok(false);
23262326
} catch (err) {} // eslint-disable-line
23272327

23282328
db.collection('shouldFailToSetReadPreferenceOnCursor')
23292329
.find()
2330-
.setReadPreference('secondary');
2330+
.withReadPreference('secondary');
23312331

23322332
done();
23332333
});
23342334
}
23352335
});
23362336

2337+
it('should allow setting the cursors readConcern through a builder', {
2338+
metadata: { requires: { mongodb: '>=3.2' } },
2339+
test: withMonitoredClient(['find'], function (client, events, done) {
2340+
const db = client.db(this.configuration.db);
2341+
const cursor = db.collection('foo').find().withReadConcern('local');
2342+
expect(cursor).property('readConcern').to.have.property('level').equal('local');
2343+
2344+
cursor.toArray(err => {
2345+
expect(err).to.not.exist;
2346+
2347+
expect(events).to.have.length(1);
2348+
const findCommand = events[0];
2349+
expect(findCommand).nested.property('command.readConcern').to.eql({ level: 'local' });
2350+
done();
2351+
});
2352+
})
2353+
});
2354+
23372355
it('shouldNotFailDueToStackOverflowEach', {
23382356
// Add a tag that our runner can trigger on
23392357
// in this case we are setting that node needs to be higher than 0.10.X to run

test/functional/max_staleness.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ describe('Max Staleness', function () {
166166
// Get a db with a new readPreference
167167
db.collection('test')
168168
.find({})
169-
.setReadPreference(readPreference)
169+
.withReadPreference(readPreference)
170170
.toArray(function (err) {
171171
expect(err).to.not.exist;
172172
expect(test.checkCommand).to.eql({

test/functional/readpreference.test.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -478,15 +478,15 @@ describe('ReadPreference', function () {
478478
})
479479
});
480480

481-
it('should set hedge using [.setReadPreference & empty hedge] ', {
481+
it('should set hedge using [.withReadPreference & empty hedge] ', {
482482
metadata: { requires: { mongodb: '>=3.6.0' } },
483483
test: withMonitoredClient(['find'], function (client, events, done) {
484484
const rp = new ReadPreference(ReadPreference.SECONDARY, null, { hedge: {} });
485485
client
486486
.db(this.configuration.db)
487487
.collection('test')
488488
.find({})
489-
.setReadPreference(rp)
489+
.withReadPreference(rp)
490490
.toArray(err => {
491491
expect(err).to.not.exist;
492492
const expected = { mode: ReadPreference.SECONDARY, hedge: {} };
@@ -496,15 +496,15 @@ describe('ReadPreference', function () {
496496
})
497497
});
498498

499-
it('should set hedge using [.setReadPreference & enabled hedge] ', {
499+
it('should set hedge using [.withReadPreference & enabled hedge] ', {
500500
metadata: { requires: { mongodb: '>=3.6.0' } },
501501
test: withMonitoredClient(['find'], function (client, events, done) {
502502
const rp = new ReadPreference(ReadPreference.SECONDARY, null, { hedge: { enabled: true } });
503503
client
504504
.db(this.configuration.db)
505505
.collection('test')
506506
.find({})
507-
.setReadPreference(rp)
507+
.withReadPreference(rp)
508508
.toArray(err => {
509509
expect(err).to.not.exist;
510510
const expected = { mode: ReadPreference.SECONDARY, hedge: { enabled: true } };
@@ -514,7 +514,7 @@ describe('ReadPreference', function () {
514514
})
515515
});
516516

517-
it('should set hedge using [.setReadPreference & disabled hedge] ', {
517+
it('should set hedge using [.withReadPreference & disabled hedge] ', {
518518
metadata: { requires: { mongodb: '>=3.6.0' } },
519519
test: withMonitoredClient(['find'], function (client, events, done) {
520520
const rp = new ReadPreference(ReadPreference.SECONDARY, null, {
@@ -524,7 +524,7 @@ describe('ReadPreference', function () {
524524
.db(this.configuration.db)
525525
.collection('test')
526526
.find({})
527-
.setReadPreference(rp)
527+
.withReadPreference(rp)
528528
.toArray(err => {
529529
expect(err).to.not.exist;
530530
const expected = { mode: ReadPreference.SECONDARY, hedge: { enabled: false } };
@@ -534,15 +534,15 @@ describe('ReadPreference', function () {
534534
})
535535
});
536536

537-
it('should set hedge using [.setReadPreference & undefined hedge] ', {
537+
it('should set hedge using [.withReadPreference & undefined hedge] ', {
538538
metadata: { requires: { mongodb: '>=3.6.0' } },
539539
test: withMonitoredClient(['find'], function (client, events, done) {
540540
const rp = new ReadPreference(ReadPreference.SECONDARY, null);
541541
client
542542
.db(this.configuration.db)
543543
.collection('test')
544544
.find({})
545-
.setReadPreference(rp)
545+
.withReadPreference(rp)
546546
.toArray(err => {
547547
expect(err).to.not.exist;
548548
const expected = { mode: ReadPreference.SECONDARY };

0 commit comments

Comments
 (0)