From 461bcf664386ff1dc1724236198c752de01a31b5 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Mon, 5 Mar 2018 18:12:27 +0000 Subject: [PATCH] Update example for iterating a tailable MongoDB\Driver\Cursor https://jira.mongodb.org/browse/PHPC-962 git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@344449 c90b9560-bf6c-de11-be94-00142212c4b1 --- reference/mongodb/mongodb/driver/cursor.xml | 203 +++++++++----------- 1 file changed, 93 insertions(+), 110 deletions(-) diff --git a/reference/mongodb/mongodb/driver/cursor.xml b/reference/mongodb/mongodb/driver/cursor.xml index d22e7d67b5..5ab8b2cabf 100644 --- a/reference/mongodb/mongodb/driver/cursor.xml +++ b/reference/mongodb/mongodb/driver/cursor.xml @@ -54,14 +54,15 @@ MongoDB\Driver\Manager::executeCommand and MongoDB\Driver\Manager::executeQuery both return - their results as a MongoDB\Driver\Cursor object. - This object can be used to iterator over the result set that the command - or cursor returned. + their result(s) as a MongoDB\Driver\Cursor object. + This object can be used to iterate over the result set of the command or + query. - Because this class implements the + Because MongoDB\Driver\Cursor implements the Traversable interface, you can simply - iterator over the result set by using foreach. + iterate over the result set with + foreach. - Reading from a tailable cursors + Reading a result set for a tailable cursor - Tailable cursors are special cursors in MongoDB that allow you to read - results from a cursor, and then wait until more documents become - available in the collection that match the query. This functionality only - works with Capped Collections and Change Streams, and - requires a different way of iteration. + Tailable cursors + are a special type of MongoDB cursor that allows the client to read some + results and then wait until more documents become available. These cursors + are primarily used with + Capped Collections + and Change Streams. - Where normal cursors can be iterated over with - foreach, the same does not work with tailable - cursors as you can not call foreach twice. In order - to continuously read from a Tailable - Cursor, you will need to wrap the - MongoDB\Driver\Cursor object with an - IteratorIterator. + While normal cursors can be iterated once with foreach, + that approach will not work with tailable cursors. When + foreach is used with a tailable cursor, the loop will + stop upon reaching the end of the initial result set. Attempting to + continue iteration on the cursor with a second + foreach would throw an exception, since PHP attempts to + rewind the cursor. Similar to result objects in other database drivers, + cursors in MongoDB only support forward iteration, which means they cannot + be rewound. - In the example below, we set up a Capped - Collection, into which we insert a few documents. We then run the - query with the special cursor options tailable, - awaitData and maxAwaitTimeMS. These - options instruct the server to: keep the cursor open after the initial - result set has been returned (tailable), and to block - the read (awaitData) for at most - maxAwaitTimeMS milliseconds. We then wrap the cursor - in IteratorIterator and use - IteratorIterator::valid and - IteratorIterator::next to iterate over the - result set. + In order to continuously read from a tailable cursor, the Cursor object + must be wrapped with an IteratorIterator. This + allows the application to directly control the cursor's iteration, avoid + inadvertently rewinding the cursor, and decide when to wait for new results + or stop iteration entirely. - + + In order to demonstrate a tailable cursor in action, two scripts will be + used: a "producer" and a "consumer". The producer script will create a new + capped collection using the + create command + and proceed to insert a new document into that collection each second. + + executeCommand("test", new MongoDB\Driver\Command([ - 'create' => "asteroids", +$manager->executeCommand('test', new MongoDB\Driver\Command([ + 'create' => 'asteroids', 'capped' => true, 'size' => 1048576, ])); -/* Insert some documents so that our query returns information */ -$bulkWrite = new MongoDB\Driver\BulkWrite; -$bulkWrite->insert(['name' => 'Ceres', 'size' => 946, 'distance' => 2.766]); -$bulkWrite->insert(['name' => 'Vesta', 'size' => 525, 'distance' => 2.362]); -$manager->executeBulkWrite("test.asteroids", $bulkWrite); +while (true) { + $bulkWrite = new MongoDB\Driver\BulkWrite; + $bulkWrite->insert(['createdAt' => new MongoDB\BSON\UTCDateTime]); + $manager->executeBulkWrite('test.asteroids', $bulkWrite); -/* Query for all the items in the collection, and make it tailable with a - * maximum wait time of 10 seconds */ -$query = new MongoDB\Driver\Query( - [], - [ - 'tailable' => true, - 'awaitData' => true, - 'maxAwaitTimeMS' => 10000, - ] -); + sleep(1); +} -/* Query the "asteroids" collection of the "test" database */ -$cursor = $manager->executeQuery("test.asteroids", $query); +?> +]]> + + + With the producer script still running, a second consumer script may be + executed to read the inserted documents using a tailable cursor, indicated + by the tailable and awaitData options + to MongoDB\Driver\Query::__construct. + + + true, + 'awaitData' => true, +]); + +$cursor = $manager->executeQuery('test.asteroids', $query); -/* Wrap cursor and rewind */ $iterator = new IteratorIterator($cursor); + $iterator->rewind(); -/* Loop over the result set with valid() and next() */ -while ($iterator->valid()) { - $document = $iterator->current(); - echo date(DateTime::ISO8601), "\n"; - print_r($document); +while (true) { + if ($iterator->valid()) { + $document = $iterator->current(); + printf("Consumed document created at: %s\n", $document->createdAt); + } $iterator->next(); } ?> ]]> - - - During the second "Waiting", we insert another document into the - collection. You can see on the date/time stamps that there are several - seconds between the second and third result being returned. - - &example.outputs.similar; - - MongoDB\BSON\ObjectId Object - ( - [oid] => 5a4d0be9122d3324914394e2 - ) - - [name] => Ceres - [size] => 946 - [distance] => 2.766 -) - -2018-01-03T16:59:21+0000 -stdClass Object -( - [_id] => MongoDB\BSON\ObjectId Object - ( - [oid] => 5a4d0be9122d3324914394e3 - ) - - [name] => Vesta - [size] => 525 - [distance] => 2.362 -) - -2018-01-03T16:59:24+0000 -stdClass Object -( - [_id] => MongoDB\BSON\ObjectId Object - ( - [oid] => 5a4d0becca67a2be8d10550c - ) - - [name] => Pallas - [size] => 512 - [distance] => 2.773 -) -]]> - + + + The consumer script will start by quickly printing all available documents + in the capped collection (as if foreach had been used); + however, it will not terminate upon reaching the end of the initial result + set. Since the cursor is tailable, calling + IteratorIterator::next will block and wait for + additional results. IteratorIterator::valid is also + used to check if there is actually data available to read at each step. + + + + This example uses the awaitData query option to + instruct the server to block for a short period (e.g. one second) at the + end of the result set before returning a response to the driver. This is + used to prevent the driver from aggressively polling the server when there + are no results available. The maxAwaitTimeMS option may + be used in conjunction with tailable and + awaitData to specify the amount of time that the server + should block when it reaches the end of the result set. + +