@@ -429,10 +429,60 @@ export interface OpMsgOptions {
429429
430430/** @internal */
431431export class DocumentSequence {
432+ field : string ;
432433 documents : Document [ ] ;
434+ serializedDocumentsLength : number ;
435+ private chunks : Uint8Array [ ] ;
436+ private header : Buffer ;
433437
434- constructor ( documents : Document [ ] ) {
435- this . documents = documents ;
438+ /**
439+ * Create a new document sequence for the provided field.
440+ * @param field - The field it will replace.
441+ */
442+ constructor ( field : string , documents ?: Document [ ] ) {
443+ this . field = field ;
444+ this . documents = [ ] ;
445+ this . chunks = [ ] ;
446+ this . serializedDocumentsLength = 0 ;
447+ // Document sequences starts with type 1 at the first byte.
448+ // Field strings must always be UTF-8.
449+ const buffer = Buffer . allocUnsafe ( 1 + 4 + this . field . length + 1 ) ;
450+ buffer [ 0 ] = 1 ;
451+ // Third part is the field name at offset 5 with trailing null byte.
452+ encodeUTF8Into ( buffer , `${ this . field } \0` , 5 ) ;
453+ this . chunks . push ( buffer ) ;
454+ this . header = buffer ;
455+ if ( documents ) {
456+ for ( const doc of documents ) {
457+ this . push ( doc , BSON . serialize ( doc ) ) ;
458+ }
459+ }
460+ }
461+
462+ /**
463+ * Push a document to the document sequence. Will serialize the document
464+ * as well and return the current serialized length of all documents.
465+ * @param document - The document to add.
466+ * @param buffer - The serialized document in raw BSON.
467+ * @returns The new total document sequence length.
468+ */
469+ push ( document : Document , buffer : Uint8Array ) : number {
470+ this . serializedDocumentsLength += buffer . length ;
471+ // Push the document.
472+ this . documents . push ( document ) ;
473+ // Push the document raw bson.
474+ this . chunks . push ( buffer ) ;
475+ // Write the new length.
476+ this . header ?. writeInt32LE ( 4 + this . field . length + 1 + this . serializedDocumentsLength , 1 ) ;
477+ return this . serializedDocumentsLength + this . header . length ;
478+ }
479+
480+ /**
481+ * Get the fully serialized bytes for the document sequence section.
482+ * @returns The section bytes.
483+ */
484+ toBin ( ) : Uint8Array {
485+ return Buffer . concat ( this . chunks ) ;
436486 }
437487}
438488
@@ -543,21 +593,7 @@ export class OpMsgRequest {
543593 const chunks = [ ] ;
544594 for ( const [ key , value ] of Object . entries ( document ) ) {
545595 if ( value instanceof DocumentSequence ) {
546- // Document sequences starts with type 1 at the first byte.
547- const buffer = Buffer . allocUnsafe ( 1 + 4 + key . length + 1 ) ;
548- buffer [ 0 ] = 1 ;
549- // Third part is the field name at offset 5 with trailing null byte.
550- encodeUTF8Into ( buffer , `${ key } \0` , 5 ) ;
551- chunks . push ( buffer ) ;
552- // Fourth part are the documents' bytes.
553- let docsLength = 0 ;
554- for ( const doc of value . documents ) {
555- const docBson = this . serializeBson ( doc ) ;
556- docsLength += docBson . length ;
557- chunks . push ( docBson ) ;
558- }
559- // Second part of the sequence is the length at offset 1;
560- buffer . writeInt32LE ( 4 + key . length + 1 + docsLength , 1 ) ;
596+ chunks . push ( value . toBin ( ) ) ;
561597 // Why are we removing the field from the command? This is because it needs to be
562598 // removed in the OP_MSG request first section, and DocumentSequence is not a
563599 // BSON type and is specific to the MongoDB wire protocol so there's nothing
0 commit comments