2024年PHP日本大会

MongoDB\Driver\Cursor 类

(mongodb >=1.0.0)

简介

MongoDB\Driver\Cursor 类封装了 MongoDB 命令或查询的结果,并且可能由 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 读取结果集

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( [] );

/* 查询 "test" 数据库的 "asteroids" 集合 */
$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 游标,允许客户端读取一些结果,然后等待更多文档可用。这些游标主要与 » 限定集合» 更改流 一起使用。

虽然可以使用 foreach 一次迭代普通游标,但这方法不适用于可尾随游标。当使用可尾随游标时使用 foreach,循环将在到达初始结果集的末尾时停止。尝试使用第二个 foreach 继续迭代游标将抛出异常,因为PHP尝试回绕游标。类似于其他数据库驱动程序中的结果对象,MongoDB 中的游标只支持正向迭代,这意味着它们不能回绕。

为了持续读取可追加游标,必须使用 IteratorIterator 包装 Cursor 对象。这允许应用程序直接控制游标的迭代,避免无意中回绕游标,并决定何时等待新结果或完全停止迭代。

为了演示可追加游标的实际应用,将使用两个脚本:“生产者”和“消费者”。生产者脚本将使用 » 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);
}

?>

在生产者脚本仍在运行的情况下,可以执行第二个消费者脚本,使用可追加游标读取插入的文档,这由 MongoDB\Driver\Query::__construct()tailableawaitData 选项指示。

<?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("Consumed document created at: %s\n", $document->createdAt);
}

$iterator->next();
}

?>

消费者脚本将首先快速打印带上限集合中所有可用的文档(就像使用了 foreach 一样);但是,它在到达初始结果集的末尾时不会终止。由于游标是可追加的,调用 IteratorIterator::next() 将阻塞并等待更多结果。IteratorIterator::valid() 也用于检查每一步是否有实际可读的数据。

注意此示例使用 awaitData 查询选项指示服务器在结果集末尾阻塞一小段时间(例如一秒钟),然后再向驱动程序返回响应。这用于防止驱动程序在没有可用结果时积极轮询服务器。maxAwaitTimeMS 选项可以与 tailableawaitData 一起使用,以指定服务器在到达结果集末尾时应阻塞的时间量。

错误/异常

在迭代游标对象时,BSON 数据将转换为 PHP 变量。此迭代可能导致以下异常

目录

添加注释

用户贡献注释 5 条注释

max-p at max-p dot me
8 年前
正如人们可能注意到的,与现在已弃用的 Mongo 驱动程序相比,此类不实现 hasNext() 或 next() 方法。

如果出于任何原因,您需要以过程方式从游标中提取数据,或者需要在迭代游标时覆盖 foreach 的行为,可以使用 SPL \IteratorIterator 类。这样做时,务必在使用迭代器之前对其进行回绕,否则您将无法获得任何数据。

<?php
$cursor
= $collection->find();
$it = new \IteratorIterator($cursor);
$it->rewind(); // 非常重要

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

我使用了这个技巧来构建一个向后兼容的包装器,模拟旧的 Mongo 驱动程序,以便升级旧的代码库。
tdrpic
8 年前
如果您发现对于返回的文档使用数组(而不是对象)更容易,请在执行查询后添加以下内容

$cursor->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']);
mikemartin2016 at gmail dot com
8 年前
我注意到游标中缺少 ->sort。旧的驱动程序似乎具有更多功能。

[红色:驱动程序之间创建游标的方式不同。在旧的驱动程序中,游标直到在迭代器上第一次调用 rewind() 后才会创建。

在新驱动程序中,游标已经存在。因为需要将 sort(和 limit 和 skip)参数发送到服务器,所以不能在游标已创建后调用它们。

您也可以在新驱动程序中使用 sort(和 limit 和 skip),方法是将它们指定为此示例中所示的 Query 的选项: https://php.net/manual/en/mongodb-driver-query.construct.php#refsect1-mongodb-driver-query.construct-examples]
peichi40233 at gmail dot com
7 年前
旧的 mongo 扩展中曾经有一个 count() 方法(http://docs.php.net/manual/en/mongocursor.count.php),但是,此功能似乎已在 mongodb 中删除。

我看到有些人使用 executeCommand() 来做到这一点,但我发现使用 toArray() 方法并计算返回的数组要容易得多。

例如
$manager = new MongoDB\Driver\Manager();
$query = new MongoDB\Driver\Query($filter, $options);
$cursor = $manager->executeQuery('db.collection', $query)->toArray();
var_dump(count($cursor));
cg at papoo dot de
3 年前
从 php-mongodb 1.9.0 版本开始,Cursor 实现 Iterator,但是如果您还需要支持旧版本,则可以有条件地使用 IteratorIterator 包装游标

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

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