経験則として、ドキュメントで動的なフィールド名 (例: objectives.37
) を使用すると、インデックス作成とクエリの柔軟性が低下します。まず、マルチキー インデックスを利用することはできません。
それにもかかわらず、 map/reduceを使用して、既存のスキーマ内の個別の ID を集計およびカウントできます。以下の例を簡潔にするために、フィールド名を短くしてo
、データ フィクスチャからの無関係なデータにしました。
<?php
$m = new Mongo();
$db = $m->test;
$db->foo->drop();
$db->foo->insert(['o' => [36 => [], 63 => [], 64 => []]]);
$db->foo->insert(['o' => [12 => [], 36 => [], 97 => []]]);
$result = $db->command([
'mapreduce' => 'foo',
'map' => new MongoCode('
function() {
for (var key in this.o) emit(key, { count: 1 });
}
'),
'reduce' => new MongoCode('
function(key, values) {
var r = { count: 0 };
values.forEach(function(v) { r.count += v.count; });
return r;
}
'),
'out' => ['inline' => 1]
]);
echo json_encode($result, JSON_PRETTY_PRINT);
ここでは、コレクション全体で map 関数を実行し、o
処理するオブジェクト内の一意のキーをそれぞれ発行しています。放出ごとに、放出する初期値は です{count: 1}
。{count: 1}
マッピング後、キーとペアで構成される大量のエミッションが得られます。次に、reduce メソッドを呼び出して、これらの中間結果を個別のキーに対して処理します。引数の各オブジェクトvalues
は同じ構造 (つまり、前に発行したのと同じ値) に従い、同じ構造の単一の縮小値を返すことが期待されます。
このスクリプトは、次の出力を生成します。
$ php mr.php
{
"results": [
{
"_id": "12",
"value": {
"count": 1
}
},
{
"_id": "36",
"value": {
"count": 2
}
},
{
"_id": "63",
"value": {
"count": 1
}
},
{
"_id": "64",
"value": {
"count": 1
}
},
{
"_id": "97",
"value": {
"count": 1
}
}
],
"timeMillis": 17,
"counts": {
"input": 2,
"emit": 6,
"reduce": 1,
"output": 5
},
"ok": 1
}
objectives
ネストされたオブジェクトの配列になるようにスキーマを作り直した場合は、MongoDB 2.2+の集約フレームワークを利用して、より少ない労力で同じ結果を計算できます。
<?php
$m = new Mongo();
$db = $m->test;
$db->foo->drop();
$db->foo->insert(['o' => [['id' => 36], ['id' => 63], ['id' => 64]]]);
$db->foo->insert(['o' => [['id' => 12], ['id' => 36], ['id' => 97]]]);
$result = $db->command([
'aggregate' => 'foo',
'pipeline' => [
['$project' => ['o' => 1]],
['$unwind' => '$o'],
['$group' => ['_id' => '$o.id', 'count' => ['$sum' => 1]]],
],
]);
echo json_encode($result, JSON_PRETTY_PRINT);
ここでは、集計パイプラインを使用して 3 つの操作を順番に実行します。
o
検索クエリで特定の出力フィールドを選択するのと同様に、スキャンしたドキュメントのフィールドを投影します。
o
遭遇する各配列をほどきます。パイプラインをドキュメントのストリームと考えると、このステップには 2 つのドキュメントがあり、それぞれo
のフィールドに 3 つのネストされたオブジェクトがあります。巻き戻しにより、6 つのドキュメントが次のステップに進み、各o
配列フィールドが配列の要素に置き換えられます。
- この時点で、
o.id
フィールドをグループ化識別子として使用し、合計を各要素のグループ値として使用して、ストリーム内のすべてのドキュメントをグループ化します。グループ化された要素ごとに、count
値フィールドが 1 ずつ増えます。
このスクリプトは次の出力を生成します。
$ php af.php
{
"result": [
{
"_id": 12,
"count": 1
},
{
"_id": 63,
"count": 1
},
{
"_id": 97,
"count": 1
},
{
"_id": 64,
"count": 1
},
{
"_id": 36,
"count": 2
}
],
"ok": 1
}