CakePHP MapReduceとは

CakePHPにはデータ構造を変更するための関数として MapReduce という関数が用意されています。 公式サイト以外にあまり説明しているサイトが無いので簡単に説明したいと思います。

MapReduce

データベースから取り出したデータを事後処理して構造を変更したいときに使うことが出来ます。例としてユーザーデータから、合格者と不合格の数を取得してみたいと思います。

$passedCount = $users->find()
 ->where(['group_id' => $group_id])
 ->mapReduce($mapper, $reducer)
 ->toArray();

MapReduce関数は2つの関数$mapperと$reducerを引数に取ります。 $mapperは第1引数としてデータベースから現在の結果を受け取り、第2引数としてイテレーションのキーを受け取ります。第三引数にはMapReduceルーチンのインスタンスを受け取ります。

$mapper = function ($user, $key, $mapReduce) {
    $passed = 'true';
    if ($user->countAttendance() < 10 || $user->getScore() < 30) {
        $passed = 'false';
    }
    $mapReduce->emitIntermediate($user->id, $passed);
};

上記の例では$mapperでuserが合格したかどうかを出席数とテストの点から計算しています。 MapReduceインスタンスの関数emitIntermediate()は第2引数をキーとして第1引数の値を登録します。今回は$user->idと$passedを渡しているので,$passedをキーとしてラベル付けされたリストが生成されます。idが2,4,5,6の人が合格,1,3の人が不合格だった場合次のようになります

[
   'true' => [2,4,5,6]
   'false' => [1,3]
]

次にemitIntermediate()の第2引数で指定したキーの種類ごとにreducer関数が呼ばれます。今回の例では$passedはtrueとfalseの二種類だけなので二回だけ呼ばれます。reducer関数の第2引数には各キーの値、第1 引数には先程生成した配列の第2引数をキーとした値が入ります。第三引数はmapper関数と同様にMapReduceのインスタンスになります。

$reducer = function ($user_ids, $passed, $mapReduce) {
    $mapReduce->emit(count($user_ids), $passed);
}

上記のようにすることで$passedの値ごとにuser_idの数を数えます。つまり合格者、不合格者が何人なのかを数え上げる処理になります。こうして生成されたリストは次のようになります

[
   'true' => 4
   'false' => 2
]

このようにあるグループのユーザーの合格者と不合格の数をMapReduceによって取得できました。