@@ -57,6 +57,7 @@ class Watch implements Executable, /* @internal */ CommandSubscriber
57
57
private $ changeStreamOptions ;
58
58
private $ collectionName ;
59
59
private $ databaseName ;
60
+ private $ isFirstBatchEmpty = false ;
60
61
private $ operationTime ;
61
62
private $ pipeline ;
62
63
private $ resumeCallable ;
@@ -200,6 +201,11 @@ final public function commandFailed(CommandFailedEvent $event)
200
201
/** @internal */
201
202
final public function commandStarted (CommandStartedEvent $ event )
202
203
{
204
+ if ($ event ->getCommandName () !== 'aggregate ' ) {
205
+ return ;
206
+ }
207
+
208
+ $ this ->isFirstBatchEmpty = false ;
203
209
}
204
210
205
211
/** @internal */
@@ -211,9 +217,15 @@ final public function commandSucceeded(CommandSucceededEvent $event)
211
217
212
218
$ reply = $ event ->getReply ();
213
219
214
- if (isset ($ reply ->operationTime ) && $ reply ->operationTime instanceof TimestampInterface) {
220
+ /* Note: the spec only refers to collecting an operation time from the
221
+ * "original aggregation", so only capture it if we've not already. */
222
+ if (!isset ($ this ->operationTime ) && isset ($ reply ->operationTime ) && $ reply ->operationTime instanceof TimestampInterface) {
215
223
$ this ->operationTime = $ reply ->operationTime ;
216
224
}
225
+
226
+ if (isset ($ reply ->cursor ->firstBatch ) && is_array ($ reply ->cursor ->firstBatch )) {
227
+ $ this ->isFirstBatchEmpty = empty ($ reply ->cursor ->firstBatch );
228
+ }
217
229
}
218
230
219
231
/**
@@ -227,7 +239,9 @@ final public function commandSucceeded(CommandSucceededEvent $event)
227
239
*/
228
240
public function execute (Server $ server )
229
241
{
230
- return new ChangeStream ($ this ->executeAggregate ($ server ), $ this ->resumeCallable );
242
+ $ cursor = $ this ->executeAggregate ($ server );
243
+
244
+ return new ChangeStream ($ cursor , $ this ->resumeCallable , $ this ->isFirstBatchEmpty );
231
245
}
232
246
233
247
/**
@@ -255,40 +269,36 @@ private function createResumeCallable(Manager $manager)
255
269
unset($ this ->changeStreamOptions ['startAtOperationTime ' ]);
256
270
}
257
271
272
+ // Select a new server using the original read preference
273
+ $ server = $ manager ->selectServer ($ this ->aggregateOptions ['readPreference ' ]);
274
+
258
275
/* If we captured an operation time from the first aggregate command
259
276
* and there is no "resumeAfter" option, set "startAtOperationTime"
260
277
* so that we can resume from the original aggregate's time. */
261
- if ($ this ->operationTime !== null && ! isset ($ this ->changeStreamOptions ['resumeAfter ' ])) {
278
+ if ($ this ->operationTime !== null && ! isset ($ this ->changeStreamOptions ['resumeAfter ' ]) &&
279
+ \MongoDB \server_supports_feature ($ server , self ::$ wireVersionForStartAtOperationTime )) {
262
280
$ this ->changeStreamOptions ['startAtOperationTime ' ] = $ this ->operationTime ;
263
281
}
264
282
283
+ // Recreate the aggregate command and execute to obtain a new cursor
265
284
$ this ->aggregate = $ this ->createAggregate ();
285
+ $ cursor = $ this ->executeAggregate ($ server );
266
286
267
- /* Select a new server using the read preference, execute this
268
- * operation on it, and return the new ChangeStream. */
269
- $ server = $ manager ->selectServer ($ this ->aggregateOptions ['readPreference ' ]);
270
-
271
- return $ this ->execute ($ server );
287
+ return [$ cursor , $ this ->isFirstBatchEmpty ];
272
288
};
273
289
}
274
290
275
291
/**
276
- * Execute the aggregate command and optionally capture its operation time.
292
+ * Execute the aggregate command.
293
+ *
294
+ * The command will be executed using APM so that we can capture its
295
+ * operation time and/or firstBatch size.
277
296
*
278
297
* @param Server $server
279
298
* @return Cursor
280
299
*/
281
300
private function executeAggregate (Server $ server )
282
301
{
283
- /* If we've already captured an operation time or the server does not
284
- * support resuming from an operation time (e.g. MongoDB 3.6), execute
285
- * the aggregation directly and return its cursor. */
286
- if ($ this ->operationTime !== null || ! \MongoDB \server_supports_feature ($ server , self ::$ wireVersionForStartAtOperationTime )) {
287
- return $ this ->aggregate ->execute ($ server );
288
- }
289
-
290
- /* Otherwise, execute the aggregation using command monitoring so that
291
- * we can capture its operation time with commandSucceeded(). */
292
302
\MongoDB \Driver \Monitoring \addSubscriber ($ this );
293
303
294
304
try {
0 commit comments