PHP 8.4.1 Released!

Класс MongoDB\Driver\Cursor

(mongodb >=1.0.0)

Введение

Класс MongoDB\Driver\Cursor содержит результаты команды MongoDB command или запроса и может быть возвращён MongoDB\Driver\Manager::executeCommand() или MongoDB\Driver\Manager::executeQuery() соответственно.

Обзор класса

final class MongoDB\Driver\Cursor implements MongoDB\Driver\CursorInterface, Iterator {
/* Методы */
final private __construct()
final public isDead(): bool
public key(): int
public next(): void
public rewind(): void
final public setTypeMap(array $typemap): void
final public toArray(): array
public valid(): bool
}

Список изменений

Версия Описание
PECL mongodb 1.9.0 Реализует Iterator.
PECL mongodb 1.6.0 Реализует MongoDB\Driver\CursorInterface, который наследует Traversable.

Примеры

Пример #1 Reading a result set

Как MongoDB\Driver\Manager::executeCommand(), так MongoDB\Driver\Manager::executeQuery(), возвращают свои результаты в виде объекта MongoDB\Driver\Cursor.

Поскольку MongoDB\Driver\Cursor реализует интерфейс Traversable, вы можете итерировать по набору результата с помощью foreach.

<?php

$manager
= new MongoDB\Driver\Manager();

/* Вставить определённые документы, чтобы наш запрос вернул результаты */
$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);

/* Запрос на получение всех элементов в коллекции */
$query = new MongoDB\Driver\Query( [] );

/* Запрос коллекции "asteroids" базы данных "test" */
$cursor = $manager->executeQuery("test.asteroids", $query);

/* Теперь $cursor содержит объект, обёрнутый вокруг набора с результатом. Используйте
* foreach() для итеации по всему результату */
foreach ($cursor as $document) {
print_r($document);
}

?>

Вывод приведённого примера будет похож на:

stdClass Object
(
    [_id] => MongoDB\BSON\ObjectId Object
        (
            [oid] => 5a4cff2f122d3321565d8cc2
        )

    [name] => Ceres
    [size] => 946
    [distance] => 2.766
)
stdClass Object
(
    [_id] => MongoDB\BSON\ObjectId Object
        (
            [oid] => 5a4cff2f122d3321565d8cc3
        )

    [name] => Vesta
    [size] => 525
    [distance] => 2.362
}

Пример #2 Чтение набора результатов для хвостового курсора

» Хвостовые курсоры - - это особый тип курсора MongoDB, который позволяет клиенту читать некоторые результаты, а затем ждать, пока не появятся дополнительные документы. Эти курсоры в основном используются с » Capped Collections и » Change Streams.

Хотя обычные курсоры можно итерировать один раз с помощью foreach, этот подход не будет работать с хвостовыми курсорами. Когда foreach используется с хвостовым курсором, цикл останавливается по достижении конца начального набора результатов. Попытка продолжить итерацию курсора со вторым foreach выбросить исключение, поскольку PHP пытается перемотать курсор. Подобно объектам результатов в других драйверах баз данных, курсоры в MongoDB поддерживают только итерацию вперёд, что означает, что они не могут быть перемотаны.

Для непрерывного считывания с хвостового курсора объект курсора должен быть завернут с помощью IteratorIterator. Это позволяет приложению напрямую управлять итерацией курсора, избегать непреднамеренного перемотки курсора и решать, когда ждать новых результатов или полностью прекратить итерацию.

Чтобы продемонстрировать хвостовой курсор в действии, будут использоваться два скрипта: "производитель" (producer) и "потребитель" (consumer). Скрипт продюсера создаст новую capped-коллекцию, используя команду » create и начнёт вставку нового документа в эту коллекцию каждую секунду.

<?php

$manager
= new MongoDB\Driver\Manager;

$manager->executeCommand('test', new MongoDB\Driver\Command([
'create' => 'asteroids',
'capped' => true,
'size' => 1048576,
]));

while (
true) {
$bulkWrite = new MongoDB\Driver\BulkWrite;
$bulkWrite->insert(['createdAt' => new MongoDB\BSON\UTCDateTime]);
$manager->executeBulkWrite('test.asteroids', $bulkWrite);

sleep(1);
}

?>

Когда скрипт продюсера (producer) всё ещё запущен, может быть выполнен второй пользовательский скрипт для чтения вставленных документов с помощью хвостового (tailable) курсора, обозначенного параметрами tailable и awaitData для MongoDB\Driver\Query::__construct().

<?php

$manager
= new MongoDB\Driver\Manager;

$query = new MongoDB\Driver\Query([], [
'tailable' => true,
'awaitData' => true,
]);

$cursor = $manager->executeQuery('test.asteroids', $query);

$iterator = new IteratorIterator($cursor);

$iterator->rewind();

while (
true) {
if (
$iterator->valid()) {
$document = $iterator->current();
printf("Пользовательский документ создан: %s\n", $document->createdAt);
}

$iterator->next();
}

?>

Пользовательский скрипт начнёт с быстрой печати всех доступных документов в заблокированной коллекции (как если бы использовался foreach); однако при достижении конца начального набора результатов он не завершится. Так как курсор является хвостовым, вызов IteratorIterator::valid() будет блокировать и ждать дополнительных результатов. IteratorIterator::valid() также используется для проверки наличия на каждом этапе данных, доступных для чтения.

Замечание: В этом примере используется опция запроса awaitData, чтобы проинструктировать сервер блокировать в течение короткого периода (например, одну секунду) в конце набора результатов перед возвратом ответа драйверу. Это используется для предотвращения агрессивного опроса (polling) сервера при отсутствии результатов. Параметр maxAwaitTimeMS может использоваться в сочетании с tailable и awaitData, чтобы указать время, которое сервер должен блокировать, когда он достигнет конца набора результатов.

Ошибки

При итерации по объекту курсора данные BSON преобразуются в переменные PHP. Эта итерация может вызвать следующие исключения:

Содержание

Добавить

Примечания пользователей 5 notes

up
16
max-p at max-p dot me
8 years ago
As one might notice, this class does not implement a hasNext() or next() method as opposed to the now deprecated Mongo driver.

If, for any reason, you need to pull data from the cursor procedurally or otherwise need to override the behavior of foreach while iterating on the cursor, the SPL \IteratorIterator class can be used. When doing so, it is important to rewind the iterator before using it, otherwise you won't get any data back.

<?php
$cursor
= $collection->find();
$it = new \IteratorIterator($cursor);
$it->rewind(); // Very important

while($doc = $it->current()) {
var_dump($doc);
$it->next();
}
?>

I used this trick to build a backward compatibility wrapper emulating the old Mongo driver in order to upgrade an older codebase.
up
5
tdrpic
8 years ago
If you find that it would be easier to use arrays (instead of objects) for the returned documents, add the following after executing your query:

$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
up
4
mikemartin2016 at gmail dot com
8 years ago
I noticed that ->sort is missing from the cursor. Seems like the old driver has more functionality.

[red.: The way how cursors are created is different between the drivers. In the old driver, the cursor would not be created until after the first rewind() call on the iterator.

In the new driver the cursor already exists. Because sort (and limit and skip) parameters need to be send to the server, they can not be called after the cursor already has been created.

You can use sort (and limit and skip) with the new driver as well, by specifying them as options to the Query as shown in this example: http://php.net/manual/en/mongodb-driver-query.construct.php#refsect1-mongodb-driver-query.construct-examples]
up
4
peichi40233 at gmail dot com
7 years ago
There used to be a count() method in the old mongo extension (http://docs.php.net/manual/en/mongocursor.count.php), however, this feature seems to be deleted in mongodb.

I've seen some people use executeCommand() to do that, but I found it much more earier to just use the toArray() method and count the returned array.

For example:
$manager = new MongoDB\Driver\Manager();
$query = new MongoDB\Driver\Query($filter, $options);
$cursor = $manager->executeQuery('db.collection', $query)->toArray();
var_dump(count($cursor));
up
1
cg at papoo dot de
3 years ago
Since php-mongodb version 1.9.0 Cursor implements Iterator, but if you need to support older versions too, you can conditionally wrap the cursor with IteratorIterator:

<?php
$iterator
= $collection->find();

if (!(
$iterator implements Iterator)) {
$iterator = new \IteratorIterator($iterator);
}
?>
To Top