1
1
import Denque = require( 'denque' ) ;
2
2
import { EventEmitter } from 'events' ;
3
3
import { MongoError , AnyError , isResumableError } from './error' ;
4
- import { Cursor , CursorOptions , CursorStream , CursorStreamOptions } from './cursor/cursor' ;
5
4
import { AggregateOperation , AggregateOptions } from './operations/aggregate' ;
6
5
import {
7
6
relayEvents ,
@@ -21,9 +20,18 @@ import type { CollationOptions } from './cmap/wire_protocol/write_command';
21
20
import { MongoClient } from './mongo_client' ;
22
21
import { Db } from './db' ;
23
22
import { Collection } from './collection' ;
23
+ import type { Readable } from 'stream' ;
24
+ import {
25
+ AbstractCursor ,
26
+ AbstractCursorOptions ,
27
+ CursorStreamOptions
28
+ } from './cursor/abstract_cursor' ;
29
+ import type { ClientSession } from './sessions' ;
30
+ import { executeOperation , ExecutionResult } from './operations/execute_operation' ;
24
31
25
32
const kResumeQueue = Symbol ( 'resumeQueue' ) ;
26
33
const kCursorStream = Symbol ( 'cursorStream' ) ;
34
+ const kClosed = Symbol ( 'closed' ) ;
27
35
28
36
const CHANGE_STREAM_OPTIONS = [ 'resumeAfter' , 'startAfter' , 'startAtOperationTime' , 'fullDocument' ] ;
29
37
const CURSOR_OPTIONS = [ 'batchSize' , 'maxAwaitTimeMS' , 'collation' , 'readPreference' ] . concat (
@@ -162,13 +170,6 @@ interface UpdateDescription {
162
170
removedFields : string [ ] ;
163
171
}
164
172
165
- /** @internal */
166
- export class ChangeStreamStream extends CursorStream {
167
- constructor ( cursor : ChangeStreamCursor ) {
168
- super ( cursor ) ;
169
- }
170
- }
171
-
172
173
/**
173
174
* Creates a new Change Stream instance. Normally created using {@link Collection#watch|Collection.watch()}.
174
175
* @public
@@ -180,10 +181,10 @@ export class ChangeStream extends EventEmitter {
180
181
namespace : MongoDBNamespace ;
181
182
type : symbol ;
182
183
cursor ?: ChangeStreamCursor ;
183
- closed : boolean ;
184
184
streamOptions ?: CursorStreamOptions ;
185
185
[ kResumeQueue ] : Denque ;
186
- [ kCursorStream ] ?: CursorStream ;
186
+ [ kCursorStream ] ?: Readable ;
187
+ [ kClosed ] : boolean ;
187
188
188
189
/** @event */
189
190
static readonly CLOSE = 'close' as const ;
@@ -241,7 +242,7 @@ export class ChangeStream extends EventEmitter {
241
242
// Create contained Change Stream cursor
242
243
this . cursor = createChangeStreamCursor ( this , options ) ;
243
244
244
- this . closed = false ;
245
+ this [ kClosed ] = false ;
245
246
246
247
// Listen for any `change` listeners being added to ChangeStream
247
248
this . on ( 'newListener' , eventName => {
@@ -252,13 +253,13 @@ export class ChangeStream extends EventEmitter {
252
253
253
254
this . on ( 'removeListener' , eventName => {
254
255
if ( eventName === 'change' && this . listenerCount ( 'change' ) === 0 && this . cursor ) {
255
- this [ kCursorStream ] ?. removeAllListeners ( CursorStream . DATA ) ;
256
+ this [ kCursorStream ] ?. removeAllListeners ( 'data' ) ;
256
257
}
257
258
} ) ;
258
259
}
259
260
260
261
/** @internal */
261
- get cursorStream ( ) : CursorStream | undefined {
262
+ get cursorStream ( ) : Readable | undefined {
262
263
return this [ kCursorStream ] ;
263
264
}
264
265
@@ -296,23 +297,20 @@ export class ChangeStream extends EventEmitter {
296
297
}
297
298
298
299
/** Is the cursor closed */
299
- isClosed ( ) : boolean {
300
- return this . closed || ( this . cursor ?. isClosed ( ) ?? false ) ;
300
+ get closed ( ) : boolean {
301
+ return this [ kClosed ] || ( this . cursor ?. closed ?? false ) ;
301
302
}
302
303
303
304
/** Close the Change Stream */
304
305
close ( callback ?: Callback ) : Promise < void > | void {
305
- return maybePromise ( callback , cb => {
306
- if ( this . closed ) return cb ( ) ;
306
+ this [ kClosed ] = true ;
307
307
308
- // flag the change stream as explicitly closed
309
- this . closed = true ;
310
-
311
- if ( ! this . cursor ) return cb ( ) ;
308
+ return maybePromise ( callback , cb => {
309
+ if ( ! this . cursor ) {
310
+ return cb ( ) ;
311
+ }
312
312
313
- // Tidy up the existing cursor
314
313
const cursor = this . cursor ;
315
-
316
314
return cursor . close ( err => {
317
315
endStream ( this ) ;
318
316
this . cursor = undefined ;
@@ -325,7 +323,7 @@ export class ChangeStream extends EventEmitter {
325
323
* Return a modified Readable stream including a possible transform method.
326
324
* @throws MongoError if this.cursor is undefined
327
325
*/
328
- stream ( options ?: CursorStreamOptions ) : ChangeStreamStream {
326
+ stream ( options ?: CursorStreamOptions ) : Readable {
329
327
this . streamOptions = options ;
330
328
if ( ! this . cursor ) {
331
329
throw new MongoError ( 'ChangeStream has no cursor, unable to stream' ) ;
@@ -335,28 +333,34 @@ export class ChangeStream extends EventEmitter {
335
333
}
336
334
337
335
/** @public */
338
- export interface ChangeStreamCursorOptions extends CursorOptions {
336
+ export interface ChangeStreamCursorOptions extends AbstractCursorOptions {
339
337
startAtOperationTime ?: OperationTime ;
340
338
resumeAfter ?: ResumeToken ;
341
339
startAfter ?: boolean ;
342
340
}
343
341
344
342
/** @internal */
345
- export class ChangeStreamCursor extends Cursor < AggregateOperation , ChangeStreamCursorOptions > {
343
+ export class ChangeStreamCursor extends AbstractCursor {
346
344
_resumeToken : ResumeToken ;
347
345
startAtOperationTime ?: OperationTime ;
348
346
hasReceived ?: boolean ;
349
347
resumeAfter : ResumeToken ;
350
348
startAfter : ResumeToken ;
349
+ options : ChangeStreamCursorOptions ;
350
+
351
+ postBatchResumeToken ?: ResumeToken ;
352
+ pipeline : Document [ ] ;
351
353
352
354
constructor (
353
355
topology : Topology ,
354
- operation : AggregateOperation ,
355
- options : ChangeStreamCursorOptions
356
+ namespace : MongoDBNamespace ,
357
+ pipeline : Document [ ] = [ ] ,
358
+ options : ChangeStreamCursorOptions = { }
356
359
) {
357
- super ( topology , operation , options ) ;
360
+ super ( topology , namespace , options ) ;
358
361
359
- options = options || { } ;
362
+ this . pipeline = pipeline ;
363
+ this . options = options ;
360
364
this . _resumeToken = null ;
361
365
this . startAtOperationTime = options . startAtOperationTime ;
362
366
@@ -421,18 +425,28 @@ export class ChangeStreamCursor extends Cursor<AggregateOperation, ChangeStreamC
421
425
}
422
426
}
423
427
424
- _initializeCursor ( callback : Callback ) : void {
425
- super . _initializeCursor ( ( err , response ) => {
428
+ _initialize ( session : ClientSession , callback : Callback < ExecutionResult > ) : void {
429
+ const aggregateOperation = new AggregateOperation (
430
+ { s : { namespace : this . namespace } } ,
431
+ this . pipeline ,
432
+ {
433
+ ...this . cursorOptions ,
434
+ ...this . options ,
435
+ session
436
+ }
437
+ ) ;
438
+
439
+ executeOperation ( this . topology , aggregateOperation , ( err , response ) => {
426
440
if ( err || response == null ) {
427
- callback ( err , response ) ;
428
- return ;
441
+ return callback ( err ) ;
429
442
}
430
443
444
+ const server = aggregateOperation . server ;
431
445
if (
432
446
this . startAtOperationTime == null &&
433
447
this . resumeAfter == null &&
434
448
this . startAfter == null &&
435
- maxWireVersion ( this . server ) >= 7
449
+ maxWireVersion ( server ) >= 7
436
450
) {
437
451
this . startAtOperationTime = response . operationTime ;
438
452
}
@@ -441,15 +455,16 @@ export class ChangeStreamCursor extends Cursor<AggregateOperation, ChangeStreamC
441
455
442
456
this . emit ( 'init' , response ) ;
443
457
this . emit ( 'response' ) ;
444
- callback ( err , response ) ;
458
+
459
+ // TODO: NODE-2882
460
+ callback ( undefined , { server, session, response } ) ;
445
461
} ) ;
446
462
}
447
463
448
- _getMore ( callback : Callback ) : void {
449
- super . _getMore ( ( err , response ) => {
464
+ _getMore ( batchSize : number , callback : Callback ) : void {
465
+ super . _getMore ( batchSize , ( err , response ) => {
450
466
if ( err ) {
451
- callback ( err ) ;
452
- return ;
467
+ return callback ( err ) ;
453
468
}
454
469
455
470
this . _processBatch ( 'nextBatch' , response ) ;
@@ -466,26 +481,32 @@ export class ChangeStreamCursor extends Cursor<AggregateOperation, ChangeStreamC
466
481
* @internal
467
482
*/
468
483
function createChangeStreamCursor (
469
- self : ChangeStream ,
484
+ changeStream : ChangeStream ,
470
485
options : ChangeStreamOptions
471
486
) : ChangeStreamCursor {
472
487
const changeStreamStageOptions : Document = { fullDocument : options . fullDocument || 'default' } ;
473
488
applyKnownOptions ( changeStreamStageOptions , options , CHANGE_STREAM_OPTIONS ) ;
474
- if ( self . type === CHANGE_DOMAIN_TYPES . CLUSTER ) {
489
+ if ( changeStream . type === CHANGE_DOMAIN_TYPES . CLUSTER ) {
475
490
changeStreamStageOptions . allChangesForCluster = true ;
476
491
}
477
492
478
- const pipeline = [ { $changeStream : changeStreamStageOptions } as Document ] . concat ( self . pipeline ) ;
493
+ const pipeline = [ { $changeStream : changeStreamStageOptions } as Document ] . concat (
494
+ changeStream . pipeline
495
+ ) ;
496
+
479
497
const cursorOptions = applyKnownOptions ( { } , options , CURSOR_OPTIONS ) ;
480
498
const changeStreamCursor = new ChangeStreamCursor (
481
- getTopology ( self . parent ) ,
482
- new AggregateOperation ( self . parent , pipeline , options ) ,
499
+ getTopology ( changeStream . parent ) ,
500
+ changeStream . namespace ,
501
+ pipeline ,
483
502
cursorOptions
484
503
) ;
485
504
486
- relayEvents ( changeStreamCursor , self , [ 'resumeTokenChanged' , 'end' , 'close' ] ) ;
505
+ relayEvents ( changeStreamCursor , changeStream , [ 'resumeTokenChanged' , 'end' , 'close' ] ) ;
506
+ if ( changeStream . listenerCount ( ChangeStream . CHANGE ) > 0 ) {
507
+ streamEvents ( changeStream , changeStreamCursor ) ;
508
+ }
487
509
488
- if ( self . listenerCount ( ChangeStream . CHANGE ) > 0 ) streamEvents ( self , changeStreamCursor ) ;
489
510
return changeStreamCursor ;
490
511
}
491
512
@@ -532,24 +553,24 @@ function waitForTopologyConnected(
532
553
}
533
554
534
555
function closeWithError ( changeStream : ChangeStream , error : AnyError , callback ?: Callback ) : void {
535
- if ( ! callback ) changeStream . emit ( ChangeStream . ERROR , error ) ;
556
+ if ( ! callback ) {
557
+ changeStream . emit ( ChangeStream . ERROR , error ) ;
558
+ }
559
+
536
560
changeStream . close ( ( ) => callback && callback ( error ) ) ;
537
561
}
538
562
539
563
function streamEvents ( changeStream : ChangeStream , cursor : ChangeStreamCursor ) : void {
540
564
const stream = changeStream [ kCursorStream ] || cursor . stream ( ) ;
541
565
changeStream [ kCursorStream ] = stream ;
542
- stream . on ( CursorStream . DATA , change => processNewChange ( changeStream , change ) ) ;
543
- stream . on ( CursorStream . ERROR , error => processError ( changeStream , error ) ) ;
566
+ stream . on ( 'data' , change => processNewChange ( changeStream , change ) ) ;
567
+ stream . on ( 'error' , error => processError ( changeStream , error ) ) ;
544
568
}
545
569
546
570
function endStream ( changeStream : ChangeStream ) : void {
547
571
const cursorStream = changeStream [ kCursorStream ] ;
548
572
if ( cursorStream ) {
549
- [ CursorStream . DATA , CursorStream . CLOSE , CursorStream . END , CursorStream . ERROR ] . forEach ( event =>
550
- cursorStream . removeAllListeners ( event )
551
- ) ;
552
-
573
+ [ 'data' , 'close' , 'end' , 'error' ] . forEach ( event => cursorStream . removeAllListeners ( event ) ) ;
553
574
cursorStream . destroy ( ) ;
554
575
}
555
576
@@ -561,7 +582,7 @@ function processNewChange(
561
582
change : ChangeStreamDocument ,
562
583
callback ?: Callback
563
584
) {
564
- if ( changeStream . closed ) {
585
+ if ( changeStream [ kClosed ] ) {
565
586
if ( callback ) callback ( CHANGESTREAM_CLOSED_ERROR ) ;
566
587
return ;
567
588
}
@@ -591,8 +612,8 @@ function processError(changeStream: ChangeStream, error: AnyError, callback?: Ca
591
612
const cursor = changeStream . cursor ;
592
613
593
614
// If the change stream has been closed explicitly, do not process error.
594
- if ( changeStream . closed ) {
595
- if ( callback ) callback ( new MongoError ( 'ChangeStream is closed' ) ) ;
615
+ if ( changeStream [ kClosed ] ) {
616
+ if ( callback ) callback ( CHANGESTREAM_CLOSED_ERROR ) ;
596
617
return ;
597
618
}
598
619
@@ -604,7 +625,10 @@ function processError(changeStream: ChangeStream, error: AnyError, callback?: Ca
604
625
605
626
// otherwise, raise an error and close the change stream
606
627
function unresumableError ( err : AnyError ) {
607
- if ( ! callback ) changeStream . emit ( ChangeStream . ERROR , err ) ;
628
+ if ( ! callback ) {
629
+ changeStream . emit ( ChangeStream . ERROR , err ) ;
630
+ }
631
+
608
632
changeStream . close ( ( ) => processResumeQueue ( changeStream , err ) ) ;
609
633
}
610
634
@@ -648,8 +672,8 @@ function processError(changeStream: ChangeStream, error: AnyError, callback?: Ca
648
672
* @param changeStream - the parent ChangeStream
649
673
*/
650
674
function getCursor ( changeStream : ChangeStream , callback : Callback < ChangeStreamCursor > ) {
651
- if ( changeStream . isClosed ( ) ) {
652
- callback ( new MongoError ( 'ChangeStream is closed.' ) ) ;
675
+ if ( changeStream [ kClosed ] ) {
676
+ callback ( CHANGESTREAM_CLOSED_ERROR ) ;
653
677
return ;
654
678
}
655
679
@@ -672,10 +696,11 @@ function getCursor(changeStream: ChangeStream, callback: Callback<ChangeStreamCu
672
696
function processResumeQueue ( changeStream : ChangeStream , err ?: Error ) {
673
697
while ( changeStream [ kResumeQueue ] . length ) {
674
698
const request = changeStream [ kResumeQueue ] . pop ( ) ;
675
- if ( changeStream . isClosed ( ) && ! err ) {
676
- request ( new MongoError ( 'Change Stream is not open.' ) ) ;
699
+ if ( changeStream [ kClosed ] && ! err ) {
700
+ request ( CHANGESTREAM_CLOSED_ERROR ) ;
677
701
return ;
678
702
}
703
+
679
704
request ( err , changeStream . cursor ) ;
680
705
}
681
706
}
0 commit comments