@@ -429,10 +429,68 @@ 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+ this . init ( ) ;
448+ if ( documents ) {
449+ for ( const doc of documents ) {
450+ this . push ( doc ) ;
451+ }
452+ }
453+ }
454+
455+ /**
456+ * Initialize the buffer chunks.
457+ */
458+ private init ( ) {
459+ // Document sequences starts with type 1 at the first byte.
460+ const buffer = Buffer . allocUnsafe ( 1 + 4 + this . field . length + 1 ) ;
461+ buffer [ 0 ] = 1 ;
462+ // Third part is the field name at offset 5 with trailing null byte.
463+ encodeUTF8Into ( buffer , `${ this . field } \0` , 5 ) ;
464+ this . chunks . push ( buffer ) ;
465+ this . header = buffer ;
466+ }
467+
468+ /**
469+ * Push a document to the document sequence. Will serialize the document
470+ * as well and return the current serialized length of all documents.
471+ * @param document - The document to add.
472+ * @returns The serialized documents length.
473+ */
474+ push ( document : Document ) : number {
475+ // First serialize the document and recalculate the documents length.
476+ const docBuffer = BSON . serialize ( document ) ;
477+ this . serializedDocumentsLength += docBuffer . length ;
478+ // Push the document.
479+ this . documents . push ( document ) ;
480+ // Push the document raw bson.
481+ this . chunks . push ( docBuffer ) ;
482+ // Write the new length.
483+ this . header ?. writeInt32LE ( 4 + this . field . length + 1 + this . serializedDocumentsLength , 1 ) ;
484+ return this . serializedDocumentsLength ;
485+ }
486+
487+ /**
488+ * Get the fully serialized bytes for the document sequence section.
489+ * @returns The section bytes.
490+ */
491+ toBin ( ) : Uint8Array {
492+ // TODO: What to do if no documents?
493+ return Buffer . concat ( this . chunks ) ;
436494 }
437495}
438496
@@ -543,21 +601,7 @@ export class OpMsgRequest {
543601 const chunks = [ ] ;
544602 for ( const [ key , value ] of Object . entries ( document ) ) {
545603 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 ) ;
604+ chunks . push ( value . toBin ( ) ) ;
561605 // Why are we removing the field from the command? This is because it needs to be
562606 // removed in the OP_MSG request first section, and DocumentSequence is not a
563607 // BSON type and is specific to the MongoDB wire protocol so there's nothing
0 commit comments