Skip to content
Advertisement

Trying to iterate over a mongodb cursor twice – failing

I am executing a query against my mongodb database… and then in two separate spots in my front end logic, I need to iterate through the results to extract / show different information.

Problem

The second time I try to iterate through, I get the following error message:

Fatal error: Uncaught MongoDBDriverExceptionLogicException: Cursors cannot yield multiple iterators in /var/www/localhost/htdocs/widgets/exception_details.php:46 Stack trace: #0

Backend Code

Here’s the logic to query the database in my model:

    try{
        $id = new MongoDBBSONObjectId($docid);
        $filter = ['_id' => $id];
        $options = [];
        $query = new MongoDBDriverQuery($filter, $options);
        $rows = $m->executeQuery('widgets.play_summary', $query);
        return $rows;
    } catch (Exception $e) {
        echo 'Exception:'. $e->getMessage();
        return false;
    }

Front End Logic

Here’s the logic that attempts the iteration.

First time:

foreach ($rows as $id=>$value) {
        if ( count($value->unreachable_servers > 0) ) {
            foreach($value->unreachable_servers as $key=>$value) {
                echo "<tr>";
                echo "<td>$value</td>";
                echo "</tr>";
            }
        }
 }

And then later, I try to do this:

$it = new IteratorIterator($rows);  //line 46
$it->rewind();  
foreach ($rows as $id=>$value) {
    if ( count($value->failed_plays > 0) ) { 
            foreach($value->failed_plays as $key=>$value) {
                    echo "<tr>";
                    echo "<td>$value</td>";
                    echo "</tr>";
            }
    }
 }

Additional Information

Commenting out the following lines gives the exact same error, but just on line 48 instead of line 46:

$it = new IteratorIterator($rows);
$it->rewind();  

Any tips would be appreciated.

Thanks.

EDIT 1

As a test, I changed the front end code to use rewind and next like this:

$it = new IteratorIterator($rows);
$it->rewind(); 
//foreach ($rows as $id=>$value) {
while($value = $it->current()) {
        if ( count($value->unreachable_servers > 0) ) {
            foreach($value->unreachable_servers as $key=>$value) {
                echo "<tr>";
                echo "<td>$value</td>";
                echo "</tr>";
            }
        }
  $it->next();
 }

And for the second loop:

$it = new IteratorIterator($rows);
$it->rewind();  
//foreach ($rows as $id=>$value) { 
while($value = $it->current()) {
    if ( count($value->failed_plays > 0) ) { 
            foreach($value->failed_plays as $key=>$value) {
                    echo "<tr>";
                    echo "<td>$value</td>";
                    echo "</tr>";
            }
    }
    $it->next();
 }

I get the same results. It works the first time but the second time it bombs with the same error message.

Advertisement

Answer

Cursor cannot be iterated twice. Fetch it into array, if it fits into memory:

    $rows = $m->executeQuery('widgets.play_summary', $query);
    return $rows->toArray();

UPDATE

Since the answer still attracts attention 5 years later, there was https://jira.mongodb.org/browse/PHPC-1691 to implement full Iterator interface, not only Traversable.

The implementation does not resolve OPs issue tho.

Technically https://www.php.net/manual/en/class.mongodb-driver-cursor.php does have the rewind method, but it throws an exception Cursors cannot rewind after starting iteration when called after first document was received: https://github.com/mongodb/mongo-php-driver/blob/0960a204b3b60f8a80375abb6390735b95c821f1/src/MongoDB/Cursor.c#L333

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement