Docs 菜单

Docs 主页PHP 库手册

可追加游标迭代

在此页面上

  • 概述
  • 手动遍历普通游标
  • 迭代可追加游标

当驱动程序执行查询或命令(例如 aggregate )时,操作结果将通过 MongoDB\Driver\Cursor 对象。 Cursor 类实现 PHP 的 迭代器 接口,允许使用 进行迭代,并与任何使用foreach 可迭代对象 的 PHP 函数连接 。与其他数据库驱动程序中的结果对象类似,MongoDB 中的游标仅支持前向迭代,这意味着它们不能回滚或与foreach 一起使用多次。

可追加游标是一种特殊类型的 MongoDB 游标,允许客户端读取一些结果,然后等待,直到有更多文档可用。这些游标主要与固定大小集合变更流一起使用。

虽然普通游标可以使用foreach 迭代一次,但该方法不适用于可追加游标。当foreach 与可追加游标一起使用时,循环将在到达初始结果集的末尾时停止。尝试使用第二个foreach 继续在游标上迭代会引发异常,因为 PHP 会尝试倒回游标。因此,从可追加游标读取数据需要直接使用 迭代器 API。

注意

在了解如何迭代可追加游标之前,我们将首先研究Iterator方法如何与普通游标交互。

以下示例找到五家餐厅并使用foreach查看结果:

<?php
$collection = (new MongoDB\Client)->test->restaurants;
$cursor = $collection->find([], ['limit' => 5]);
foreach ($cursor as $document) {
var_dump($document);
}

虽然这个示例非常简洁,但实际上发生了很多事情。foreach 构造首先会倒回迭代器(在本例中为$cursor )。然后检查当前位置是否有效。如果该位置无效,则循环结束。否则,将相应地访问当前键和值并执行循环体。假设 休息 ,然后迭代器前进到下一个位置,控制跳回有效性检查,然后继续循环。

掌握了foreach的内部运作方式后,我们现在可以将前面的示例转换为直接使用迭代器方法:

<?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();
}

注意

while循环自然结束后调用$cursor->next()会引发异常,因为游标上的所有结果均已用尽。

此示例的目的是演示foreach 和使用 PHP 迭代器 进行手动迭代之间的功能等价 API。对于普通游标,没有理由手动迭代结果而不是简洁的foreach 循环。

为了演示实际操作中的可追加游标,我们需要两个脚本:“生产者”和“消费者”。 生产者脚本将使用MongoDB\Database::createCollection()创建一个新的固定大小集合,并继续每秒将一个新文档插入到该集合中。

<?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);
}

在生产者脚本仍在运行的情况下,我们现在将执行使用者脚本,以使用可追加游标读取插入的文档,如MongoDB\Collection::find()cursorType选项所示。我们将首先使用foreach来说明其缺点:

<?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);
}

如果执行此使用者脚本,您会注意到它很快耗尽固定大小集合中的所有结果,然后终止。我们无法添加第二个foreach ,因为这会在尝试倒带游标时引发异常。这是使用 迭代器 直接控制迭代过程的成熟用例 接口。

<?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();
}

foreach示例非常相似,此版本的使用者脚本将首先快速打印固定大小集合中的所有结果;但是,它不会在到达初始结果集的末尾时终止。 由于我们使用的是可追加游标,因此调用next()会阻塞并等待其他结果,而不是引发异常。 我们还将使用valid()在每一步检查是否确实有数据可供读取。

由于我们选择使用TAILABLE_AWAIT游标,因此服务器将对驱动程序的响应延迟设定的时间。 在此示例中,我们通过将maxAwaitTimeMS选项指定为MongoDB\Collection::find()来请求服务器阻塞大约 100 毫秒。

← 索引