マニュアル
PHP Manual

問い合わせ

問い合わせのスレーブへの分散

注意: 1.1.0+

» レプリカセット とバージョン 1.1.0 以降のドライバを使っている場合、 ドライバは読み込みを自動的にスレーブにまわします。 これは以前のバージョンのドライバにはなかった挙動で、"通常の" マスタ-スレーブでは 使えません

デフォルトでは、ドライバはすべての問い合わせをマスタに送信します。 "slaveOkay" オプションを設定すると、ドライバはすべての問い合わせを (もし可能なら) プライマリ以外のサーバに送信します。 "slaveOkay" オプションは、 接続データベースコレクション および カーソル のすべてのレベルで設定できます。各クラスは上位の "slaveOkay" 設定を継承するので、たとえば

<?php

$db
->setSlaveOkay(true);
$c $db->myCollection;

$cursor $c->find();

?>

のようにすると、この問い合わせはスレーブに対して実行されます (コレクションがデータベースから "slaveOkay" の設定を継承し、カーソルはコレクションからその設定を継承します)。

スレーブはどのように選ばれるか

Mongo インスタンスはそれぞれ自身のスレーブを持ち、 それは読み込み可能なスレーブの中からランダムに選ばれます。 持続的接続を使っている場合でもこれは保持されます。つまり、次のようなコード

<?php

// メンバー ip0、ip1 および ip2 からなるレプリカセットに接続します
// ip0 がプライマリ、ip1 と ip2 がセカンダリです
$m1 = new Mongo("mongodb://ip0", array("replicaSet" => true"persist" => "x"));
echo 
"m1's slave is ".$m1->getSlave()."\n";

// $m1 と同じ接続を使います
$m2 = new Mongo("mongodb://ip0", array("replicaSet" => true"persist" => "x"));
echo 
"m2's slave is ".$m2->getSlave()."\n";

?>

を試すと、おそらくこのような結果となります。


m1's slave is: ip2
m2's slave is: ip1

新しい Mongo を作り続けると、 ip1 と ip2 が同程度に分散した結果を得られることになります。 コマンドが取り上げたすべてのセカンダリが (たとえ優先度が 0 であったり slaveDelay が設定されていたりしたとしても) 読み込み可能なサーバとなることに注意しましょう。 読み込みしたくないサーバがある場合は、読み込みのルーティングを手動でするか、 サーバのレプリカセットの設定で hidden オプションを追加します。

スレーブはインスタンス単位で選ばれ、 (レプリカセットに何か問題が発生して強制的に変更される場合を除いて) 変わることはありません。

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

読み込み可能な非プライマリサーバがない場合は、 (たとえ "slaveOkay" が設定されていても) ドライバはプライマリから読み込みを行います。 あるサーバが読み込み可能であると判定される条件は、その state が 2 (SECONDARY) かつ health が 1 であることです。 これらをチェックするには Mongo::getHosts() を使います (いくつかのフィールドは省略しています)。

Array
(
    [ubuntu:27017] => Array
        (
            [_id] => 0
            [name] => ip0
            [health] => 1
            [state] => 1
            [stateStr] => PRIMARY
        )

    [ubuntu:27019] => Array
        (
            [_id] => 2
            [name] => ip1
            [health] => 1
            [state] => 2
            [stateStr] => SECONDARY
        )

    [ubuntu:27018] => Array
        (
            [_id] => 1
            [name] => ip2
            [health] => 1
            [state] => 2
            [stateStr] => SECONDARY
        )

)

上のセットには、読み込み可能なサーバが "ip1" と "ip2" のふたつあります。 これらの両方がダウンしたり古くなったりすると、読み込みは "ip0" に向けられるようになります。

あまり関わるべきではないところに首を突っ込んで楽しみたいのなら、 Mongo::switchSlave() をコールして別のスレーブを使わせることもできます。 これは、ランダムに新しいスレーブを選んでそれを使います。 自分が何をしているのかをしっかり把握しているのでない限り、これを使ってはいけません。

その他の注意点

書き込みは、常にプライマリに送られます。データベースコマンドについても、 たとえ読み込み専用のコマンドであっても常にプライマリに送られます。

スレーブの health と state は、5 秒おき あるいは次の操作の 5 秒前にチェックされます。 ドライバがサーバに到達できずに何か問題が発生したときは、同時に設定の再チェックも行います。 これらの情報の更新を手動で強制的に行うには Mongo::getHosts() をコールします。

非プライマリサーバは、操作時はプライマリの背後に隠れています。 そのため、最新でないデータを受け取っても大丈夫なようにアプリケーション側で対応しなければなりません (あるいは、すべての書き込みで w を使わなければなりません)。

_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"))));

?>

マニュアル
PHP Manual