@@ -38,6 +38,8 @@ export class ClientBulkWriteCommandBuilder {
3838 models : AnyClientBulkWriteModel [ ] ;
3939 options : ClientBulkWriteOptions ;
4040 pkFactory : PkFactory ;
41+ currentModelIndex : number ;
42+ lastOperations : Document [ ] ;
4143
4244 /**
4345 * Create the command builder.
@@ -51,6 +53,8 @@ export class ClientBulkWriteCommandBuilder {
5153 this . models = models ;
5254 this . options = options ;
5355 this . pkFactory = pkFactory ?? DEFAULT_PK_FACTORY ;
56+ this . currentModelIndex = 0 ;
57+ this . lastOperations = [ ] ;
5458 }
5559
5660 /**
@@ -65,68 +69,55 @@ export class ClientBulkWriteCommandBuilder {
6569 }
6670
6771 /**
68- * Build the bulk write commands from the models.
72+ * Determines if there is another batch to process.
73+ * @returns True if not all batches have been built.
6974 */
70- buildCommands ( maxMessageSizeBytes : number , maxWriteBatchSize : number ) : ClientBulkWriteCommand [ ] {
71- // Iterate the models to build the ops and nsInfo fields.
72- // We need to do this in a loop which creates one command each up
73- // to the max bson size or max message size.
74- const commands : ClientBulkWriteCommand [ ] = [ ] ;
75- let currentCommandLength = 0 ;
75+ hasNextBatch ( ) : boolean {
76+ return this . currentModelIndex < this . models . length ;
77+ }
78+
79+ /**
80+ * Build a single batch of a client bulk write command.
81+ * @param maxMessageSizeBytes - The max message size in bytes.
82+ * @param maxWriteBatchSize - The max write batch size.
83+ * @returns The client bulk write command.
84+ */
85+ buildBatch ( maxMessageSizeBytes : number , maxWriteBatchSize : number ) : ClientBulkWriteCommand {
86+ let commandLength = 0 ;
7687 let currentNamespaceIndex = 0 ;
77- let currentCommand : ClientBulkWriteCommand = this . baseCommand ( ) ;
88+ const command : ClientBulkWriteCommand = this . baseCommand ( ) ;
7889 const namespaces = new Map < string , number > ( ) ;
7990
80- for ( const model of this . models ) {
91+ while ( this . currentModelIndex < this . models . length ) {
92+ const model = this . models [ this . currentModelIndex ] ;
8193 const ns = model . namespace ;
82- const index = namespaces . get ( ns ) ;
83-
84- /**
85- * Convenience function for resetting everything when a new batch
86- * is started.
87- */
88- const reset = ( ) => {
89- commands . push ( currentCommand ) ;
90- namespaces . clear ( ) ;
91- currentNamespaceIndex = 0 ;
92- currentCommand = this . baseCommand ( ) ;
93- namespaces . set ( ns , currentNamespaceIndex ) ;
94- } ;
94+ const nsIndex = namespaces . get ( ns ) ;
9595
96- if ( index != null ) {
97- // Pushing to the ops document sequence returns the bytes length added .
98- const operation = buildOperation ( model , index , this . pkFactory ) ;
96+ if ( nsIndex != null ) {
97+ // Build the operation and serialize it to get the bytes buffer .
98+ const operation = buildOperation ( model , nsIndex , this . pkFactory ) ;
9999 const operationBuffer = BSON . serialize ( operation ) ;
100100
101- // Check if the operation buffer can fit in the current command. If it can,
101+ // Check if the operation buffer can fit in the command. If it can,
102102 // then add the operation to the document sequence and increment the
103103 // current length as long as the ops don't exceed the maxWriteBatchSize.
104104 if (
105- currentCommandLength + operationBuffer . length < maxMessageSizeBytes &&
106- currentCommand . ops . documents . length < maxWriteBatchSize
105+ commandLength + operationBuffer . length < maxMessageSizeBytes &&
106+ command . ops . documents . length < maxWriteBatchSize
107107 ) {
108108 // Pushing to the ops document sequence returns the total byte length of the document sequence.
109- currentCommandLength =
110- MESSAGE_OVERHEAD_BYTES + this . addOperation ( currentCommand , operation , operationBuffer ) ;
109+ commandLength = MESSAGE_OVERHEAD_BYTES + command . ops . push ( operation , operationBuffer ) ;
110+ // Increment the builder's current model index.
111+ this . currentModelIndex ++ ;
111112 } else {
112- // We need to batch. Push the current command to the commands
113- // array and create a new current command. We aslo need to clear the namespaces
114- // map for the new command.
115- reset ( ) ;
116-
117- const nsInfo = { ns : ns } ;
118- const nsInfoBuffer = BSON . serialize ( nsInfo ) ;
119- currentCommandLength =
120- MESSAGE_OVERHEAD_BYTES +
121- this . addOperationAndNsInfo (
122- currentCommand ,
123- operation ,
124- operationBuffer ,
125- nsInfo ,
126- nsInfoBuffer
127- ) ;
113+ // The operation cannot fit in the current command and will need to
114+ // go in the next batch. Exit the loop and set the last ops.
115+ this . lastOperations = command . ops . documents ;
116+ break ;
128117 }
129118 } else {
119+ // The namespace is not already in the nsInfo so we will set it in the map, and
120+ // construct our nsInfo and ops documents and buffers.
130121 namespaces . set ( ns , currentNamespaceIndex ) ;
131122 const nsInfo = { ns : ns } ;
132123 const nsInfoBuffer = BSON . serialize ( nsInfo ) ;
@@ -138,68 +129,27 @@ export class ClientBulkWriteCommandBuilder {
138129 // sequences and increment the current length as long as the ops don't exceed
139130 // the maxWriteBatchSize.
140131 if (
141- currentCommandLength + nsInfoBuffer . length + operationBuffer . length <
142- maxMessageSizeBytes &&
143- currentCommand . ops . documents . length < maxWriteBatchSize
132+ commandLength + nsInfoBuffer . length + operationBuffer . length < maxMessageSizeBytes &&
133+ command . ops . documents . length < maxWriteBatchSize
144134 ) {
145- currentCommandLength =
135+ // Pushing to the ops document sequence returns the total byte length of the document sequence.
136+ commandLength =
146137 MESSAGE_OVERHEAD_BYTES +
147- this . addOperationAndNsInfo (
148- currentCommand ,
149- operation ,
150- operationBuffer ,
151- nsInfo ,
152- nsInfoBuffer
153- ) ;
138+ command . nsInfo . push ( nsInfo , nsInfoBuffer ) +
139+ command . ops . push ( operation , operationBuffer ) ;
140+ // We've added a new namespace, increment the namespace index.
141+ currentNamespaceIndex ++ ;
142+ // Increment the builder's current model index.
143+ this . currentModelIndex ++ ;
154144 } else {
155- // We need to batch. Push the current command to the commands
156- // array and create a new current command. Aslo clear the namespaces map.
157- reset ( ) ;
158-
159- currentCommandLength =
160- MESSAGE_OVERHEAD_BYTES +
161- this . addOperationAndNsInfo (
162- currentCommand ,
163- operation ,
164- operationBuffer ,
165- nsInfo ,
166- nsInfoBuffer
167- ) ;
145+ // The operation cannot fit in the current command and will need to
146+ // go in the next batch. Exit the loop and set the last ops.
147+ this . lastOperations = command . ops . documents ;
148+ break ;
168149 }
169- // We've added a new namespace, increment the namespace index.
170- currentNamespaceIndex ++ ;
171150 }
172151 }
173-
174- // After we've finisihed iterating all the models put the last current command
175- // only if there are operations in it.
176- if ( currentCommand . ops . documents . length > 0 ) {
177- commands . push ( currentCommand ) ;
178- }
179-
180- return commands ;
181- }
182-
183- private addOperation (
184- command : ClientBulkWriteCommand ,
185- operation : Document ,
186- operationBuffer : Uint8Array
187- ) : number {
188- // Pushing to the ops document sequence returns the total byte length of the document sequence.
189- return command . ops . push ( operation , operationBuffer ) ;
190- }
191-
192- private addOperationAndNsInfo (
193- command : ClientBulkWriteCommand ,
194- operation : Document ,
195- operationBuffer : Uint8Array ,
196- nsInfo : Document ,
197- nsInfoBuffer : Uint8Array
198- ) : number {
199- // Pushing to the nsInfo document sequence returns the total byte length of the document sequence.
200- const nsInfoLength = command . nsInfo . push ( nsInfo , nsInfoBuffer ) ;
201- const opsLength = this . addOperation ( command , operation , operationBuffer ) ;
202- return nsInfoLength + opsLength ;
152+ return command ;
203153 }
204154
205155 private baseCommand ( ) : ClientBulkWriteCommand {
0 commit comments