Página inicial do Docs → Manual da Biblioteca PHP
Iteração de cursor tailable
Visão geral
Quando o driver executa uma query ou comando (por exemplo, aggregate), os resultados da operação são retornados por meio de um MongoDB\Driver\Cursor evento. A classe Cursor implementa o Iterador do PHP interface, o que permite que ela seja iterada com foreach
e interface com quaisquer funções PHP que funcionem com iteráveis. Semelhante aos objetos de resultado em outros drivers de banco de dados, os cursores no MongoDB suportam apenas iteração direta, o que significa que não podem ser revertidos ou usados com foreach
várias vezes.
Os cursores tailable são um tipo especial de cursor do MongoDB que permite ao cliente ler alguns resultados e aguardar até que mais documentos estejam disponíveis. Esses cursores são usados principalmente com Capped Collections e Change Streams.
Embora os cursores normais possam ser iterados uma vez com foreach
, essa abordagem não funcionará com cursores tailable. Quando foreach
é usado com um cursor tailable, o loop para ao chegar ao final do conjunto de resultados inicial. A tentativa de continuar a iteração no cursor com um segundo foreach
lançaria uma exceção, pois o PHP tenta retroceder o cursor. Portanto, a leitura de um cursor tailable exigirá o uso direto do Iterador API.
Observação
Antes da versão 1.9.0 da ext-mongodb
extensão , a classe do cursor não implementa o Iterador interface. Para iterar manualmente um cursor usando o método abaixo, ele deve primeiro ser envolto com um IteratorIterator.
Iteração manual de um cursor normal
Antes de ver como um cursor tailable pode ser iterado, começaremos examinando como os métodos Iterator
interagem com um cursor normal.
O exemplo a seguir localiza cinco restaurantes e usa foreach
para visualizar os resultados:
$collection = (new MongoDB\Client)->test->restaurants; $cursor = $collection->find([], ['limit' => 5]); foreach ($cursor as $document) { var_dump($document); }
Embora este exemplo seja bastante conciso, na verdade, há bastante ação. A foreach
construção começa rewind o iterável ($cursor
neste caso). Em seguida, verifica se a posição atual é válida. Se a posição não for válida, o loop termina. Caso contrário, a chave e o valor atuais são acessados adequadamente e o corpo do loop é executado. Supondo um intervalo não ocorreu, o iterador avança para a próxima posição, o controle volta para a verificação de validade e o loop continua.
Com o funcionamento interno do foreach
em nosso currículo, agora podemos traduzir o exemplo anterior para usar os métodos do Iterador diretamente:
$collection = (new MongoDB\Client)->test->restaurants; $cursor = $collection->find([], ['limit' => 5]); $cursor->rewind(); while ($cursor->valid()) { $document = $cursor->current(); var_dump($document); $cursor->next(); }
Observação
Chamar $cursor->next()
após o loop while
terminar naturalmente lançaria uma exceção, uma vez que todos os resultados no cursor foram esgotados.
O objetivo deste exemplo é demonstrar a equivalência funcional entre foreach
e a iteração manual com o Iterador do PHP API. Para cursores normais, há poucos motivos para iterar manualmente os resultados em vez de um foreach
loop conciso.
Iterando um cursor persistente
Para demonstrar um cursor persistente em ação, precisaremos de dois scripts: um "produtor" e um "consumidor". O script do produtor criará uma nova collection limitada usando MongoDB\Database::createCollection()
e inserirá um novo documento nessa collection a cada segundo.
$database = (new MongoDB\Client)->test; $database->createCollection('capped', [ 'capped' => true, 'size' => 16777216, ]); $collection = $database->selectCollection('capped'); while (true) { $collection->insertOne(['createdAt' => new MongoDB\BSON\UTCDateTime()]); sleep(1); }
Com o script do produtor ainda em execução, executaremos agora um script do consumidor para ler o documento inserido usando um cursor persistente, indicado pela opção cursorType
para MongoDB\Collection::find()
. Começaremos usando foreach
para ilustrar suas falhas:
$collection = (new MongoDB\Client)->test->capped; $cursor = $collection->find([], [ 'cursorType' => MongoDB\Operation\Find::TAILABLE_AWAIT, 'maxAwaitTimeMS' => 100, ]); foreach ($cursor as $document) { printf("Consumed document created at: %s\n", $document->createdAt); }
Se você executar esse script de consumidor, notará que ele esgota rapidamente todos os resultados na capped collection e, em seguida, termina. Não podemos adicionar um segundo foreach
, pois isso lançaria uma exceção ao tentar retroceder o cursor. Este é um caso de uso completo para controlar diretamente o processo de iteração usando o Iterador interface.
$collection = (new MongoDB\Client)->test->capped; $cursor = $collection->find([], [ 'cursorType' => MongoDB\Operation\Find::TAILABLE_AWAIT, 'maxAwaitTimeMS' => 100, ]); $cursor->rewind(); while (true) { if ($cursor->valid()) { $document = $cursor->current(); printf("Consumed document created at: %s\n", $document->createdAt); } $cursor->next(); }
Assim como no exemplo foreach
, essa versão no script do consumidor começará imprimindo rapidamente todos os resultados na capped collection; no entanto, ele não terminará ao atingir o final do conjunto de resultados inicial. Como estamos trabalhando com um cursor tailable, chamar next()
bloqueará e aguardará resultados adicionais em vez de lançar uma exceção. Também usaremos valid()
para verificar se há realmente dados disponíveis para leitura em cada etapa.
Como optamos por usar um cursor TAILABLE_AWAIT
, o servidor atrasará sua resposta ao driver por um período de tempo definido. Neste exemplo, solicitamos que o servidor bloqueie por aproximadamente 100 milissegundos especificando a opção maxAwaitTimeMS
para MongoDB\Collection::find()
.