問い合わせ

問い合わせのセカンダリへの分散

すべての問い合わせ (読み書き両方) は、デフォルトではレプリカセットのプライマリメンバーにだけ送られます。 しかし、優先読み込み を使えばこれは簡単に変更できます。全般的な優先読み込み設定 (たとえばセカンダリからの読み込みは一番近いサーバーを選ぶなど) もできるし、特定の国やデータセンターあるいはハードウェア上にある特定のサーバーを指定することもできます。 そのときには レプリカセットのタグセット を使います。

優先読み込みの設定は、ドライバのあらゆるレベルでできます。

「親」コンテキストで指定した優先読み込み設定を継承します。

例1 データベースレベルからカーソルへの優先読み込みの継承

<?php
$db
->setReadPreference(MongoClient::RP_SECONDARY_PREFERRED);
$c $db->myCollection;

$cursor $c->find();
?>

この例のようにすると、問い合わせはセカンダリに対して実行されます (コレクションはデータベースから MongoClient::RP_SECONDARY_PREFERRED を引き継ぎ、カーソルがコレクションからそれを引き継ぎます)。

セカンダリはどのように選ばれるか

MongoClient インスタンスは、 利用可能なセカンダリの中から、ping の所要時間が最も短いものを自分のセカンダリとして選択します。 つまり、PHP クライアントがヨーロッパとオーストラリアにあって それぞれのデータセンターにひとつずつセカンダリがあるとすると、 このようにすることができます。

<?php
$options 
= array("replicaSet" => "setName""readPreference" => MongoClient::RP_SECONDARY_PREFERRED);

// オーストラリアのクライアント
$m = new MongoClient("mongodb://primary,australianhost.secondary,europeanhost.secondary"$options);
$cursor $m->foo->bar->find();
$cursor->getNext();
echo 
"Reading from: "$cursor->info()["server"], "\n";

// ヨーロッパのクライアント
$m = new MongoClient("mongodb://primary,australianhost.secondary,europeanhost.secondary"$options);
$cursor $m->foo->bar->find();
$cursor->getNext();
echo 
"Reading from: "$cursor->info()["server"], "\n";
?>

上の例の出力は、 たとえば以下のようになります。

Reading from: australianHost
Reading from: europeanHost

クエリを実行してからでないとセカンダリの選択が行われないことに注意しましょう。 セカンダリの選択は、必要になった時点でドライバが行います。

セットのメンバーの現在の状態がどうなっているかを知るには、 MongoClient::getHosts() あるいは MongoClient::getConnections() を実行します。

読み込み可能な非プライマリサーバーがない場合は、 ドライバはプライマリから読み込みを行います。 MongoClient::RP_SECONDARY_PREFERRED を指定した場合は、セカンダリが使えない場合の代替策としてプライマリでクエリを実行するからです。 あるサーバーが読み込み可能であると判定される条件は、その state が 2 (SECONDARY) かつ health が 1 であることです。 これらをチェックするには MongoClient::getHosts() および MongoClient::getConnections() を使います。

その他の注意

書き込みは常にプライマリに送られます。そして、 デフォルトではすべての読み込みもプライマリに送られます。

_id による問い合わせ

追加されたすべてのオブジェクトには、一意な _id フィールドが自動的に付加されます。 これは、問い合わせで使うフィールドとして便利です。 これは "get last insert ID" と同じような働きをしますが、 _idクライアント が選ぶものであるという点が違います。

今追加したばかりのドキュメントを探すことを考えてみましょう。 追加するとドキュメントに _id フィールドができるので、それを問い合わせればいいのです。

<?php
$person 
= array("name" => "joe");

$people->insert($person);

// $joe には _id フィールドがあります
$joe $people->findOne(array("_id" => $person['_id']));
?>

ユーザーが別途指定しない限り、_id フィールドは MongoId となります。ありがちな間違いは、 文字列を MongoId とマッチさせようとすることです。 文字列とは別の型であり、そのままではマッチしないことを覚えておきましょう。 これは、文字列 "array()" と空の配列が別のものであるというのと同じことです。 次の例を参照ください。

<?php
$person 
= array("name" => "joe");

$people->insert($person);

// _id を文字列に変換します
$pid $person['_id'] . "";

// 失敗 - $pid は文字列であり、MongoId ではありません
$joe $people->findOne(array("_id" => $pid));
?>

配列

配列には特殊な点がいくつかあります。 まず、MongoDB が扱う配列には二種類あります。 "普通の" 配列と連想配列です。連想配列には、任意の型のキーと値を組み合わせることができます。 "普通の" 配列は、0 から始まってひとつずつ増えていく数値のインデックスに それぞれ要素を関連づけます。 これらは、ほぼ PHP の配列や連想配列と同じようなものです。

たとえば、受賞の一覧をドキュメントに保存するときには次のようにできます。

<?php

$collection
->save(array("awards" => array("gold""silver""bronze")));

?>

問い合わせでは、配列の要素も探すことができます。 指定した値が配列の要素に含まれるすべてのドキュメントを探すことを考えましょう。 たとえば、受賞 (awards) に金賞 (gold) が含まれる次のようなドキュメントです。

{ "_id" : ObjectId("4b06c282edb87a281e09dad9"), "awards" : ["gold", "silver", "bronze"]}

これは、単純なクエリで問い合わせることができます。"awards" が配列であるということを気にせず、次のようにすればいいのです。

<?php

  $cursor 
$collection->find(array("awards" => "gold"));

?>

もう少し複雑なオブジェクトを考えてみましょう。 配列の各要素のオブジェクトになっている、次のような例だとどうでしょう。

{
     "_id" : ObjectId("4b06c282edb87a281e09dad9"),
     "awards" :
     [
        {
            "first place" : "gold"
        },
        {
            "second place" : "silver"
        },
        {
            "third place" :  "bronze"
        }
     ]
}

このような場合でも、配列であることを特別視する必要はありません。 内部のオブジェクトへの問い合わせには、ドット記法が使えます。

<?php

$cursor 
$collection->find(array("awards.first place" => "gold"));

?>

フィールド名にスペースが含まれていてもかまわないことに注目しましょう (スペースを使わないにこしたことはありませんが、可読性を考慮しました)。

配列を使って、取り得る値を複数指定した問い合わせを行うこともできます。 "gold" あるいは "copper" を含むドキュメントを探すには、このようにします。

<?php

$cursor 
$collection->find(array("awards" => array('$in' => array("gold""copper"))));

?>

変更履歴

バージョン 説明
1.3.0 優先読み込み の仕組みが導入され、セカンダリからの読み込みに関してよりきめ細やかな制御ができるようになりました。
1.3.0 slaveOkay が非推奨になりました。かわりに 優先読み込み を使いましょう。
1.1.0 読み込みをレプリカセットのセカンダリに振り向ける仕組みとして Mongo::setSlaveOkay() が導入されました。