PHP Conference Japan 2024

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()
最終 公開 已失效(): 布林值
公開 鍵值(): 整數
公開 下一個():
公開 重設():
最終 公開 設定類型映射(陣列 $typemap):
最終 公開 轉換為陣列(): 陣列
公開 有效(): 布林值
}

更新日誌

版本 說明
PECL mongodb 1.9.0 實作 迭代器
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 讀取 tailable cursor 的結果集

» Tailable cursors 是一種特殊的 MongoDB 游標,它允許客戶端讀取一些結果,然後等待更多文件可用。這些游標主要與 » 固定集合 (Capped Collections)» 變更串流 (Change Streams) 一起使用。

一般遊標可以使用 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() 方法。

如果基於任何原因,您需要以程序方式從 Cursor 中提取資料,或者需要在迭代 Cursor 時覆寫 foreach 的行為,則可以使用 SPL 的 \IteratorIterator 類別。這樣做的時候,務必在使用迭代器之前先將其 rewind,否則您將無法取回任何資料。

<?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),方法是在查詢中將它們指定為選項,如此範例所示: https://php.dev.org.tw/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