Menu Docs

Página inicial do DocsManual da Biblioteca PHP

Iteração de cursor tailable

Nesta página

  • Visão geral
  • Iteração manual de um cursor normal
  • Iterando um cursor persistente

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.

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:

<?php
$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:

<?php
$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.

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.

<?php
$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:

<?php
$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.

<?php
$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().

← Índices