SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

「Jaql」を使ってMapReduceをより簡単に

Jaql(a query language desired for JSON)の使い方

件数や人数を数えてみる

 すべての件数を数えるには、いったんグループ化することでできます。そのため、すべてのレコードに「id:1」という属性を追加し、それをグループ化して集計します(MapReduceと同じですね)。

jaql> $rank
 -> transform {id:1, $.head-count}
 -> group by $id_group = $.id
     into {$id_group, head-count: count($[*].head-count)};
[
{
"id_group": 1,
"head-count": 530250
}
]

 この結果「530,250」はレコードの件数です。すなわち、530,250位までデータがある、ということです。しかし、「だれだれちゃんは何人いる」のように、レコードには「head-count」という人数が含まれていて、これを全部足し算すると人数が分かります。先ほどと似たことをやって、人数を「sum」ビルトイン関数を使って集計するには、以下のように記述します。

jaql> $rank
 -> transform {id:1, $.head-count}
 -> group by $id_group = $.id
     into {$id_group, head-count: sum($[*].head-count)};
[
{
"id_group": 1,
"head-count": 651777
}
]

 この結果で、530,250位まで存在するが、総計651,777人いることが分かりました。

名前だけで集計し、ソートする

 元データを見ていただくと、「鈴木 健太」くんだけで78人いるわけですが、これでは「健太」くんが何人いるか分かりません。そこで、苗字と名前を分離し、名前だけで集計した結果を配列にし、ソートしてランクを見てみようと思います。幸いなことに、苗字と名前の間には、全角のスペースが入っていますので、これで分けられそうです。

 コマンドは以下のようになります。

jaql> $rank
 -> transform
     { firstname : strSplit($.name, " ")[1],
       head-count: $.head-count
     }
 -> group by $firstname_group = $.firstname
     into {$firstname_group, total: sum($[*].head-count)}
 -> sort by [$.total];
:
:
:
途中省略
:
{
"firstname_group": "美咲",
"total": 2499
},
{
"firstname_group": "翔太",
"total": 2506
},
{
"firstname_group": "拓也",
"total": 2521
}
]

 これで、拓也くんが一番多い名前で、2,521人いたことが分かりました。

 このコマンドは長いので、途中のJSONがどうなっているか、順を追って確認しておきましょう。

strSplit($.name, " ")[1]

 まず、strSplitビルトイン関数です。これは、引数を2つ持っており、2つ目の文字列をデリミッタ(区切り文字)として1つ目の文字列を分解する関数です。結果はJSON配列となります。そこに添え字[1]を付けることによって、2つ目を取り出しています(JSON配列の添え字は0から)。つまり、全角スペース" "で苗字と名前に分け、2つ目である名前を取り出しています。

transform

 transform演算子によって、新しいJSON配列が作られます。さきほど取り出した名前と、その同じレコードにあるhead-countを{ "firstname": "拓也", "head-count": 27}というようなJSONレコードにします。拓也くんは、別の姓にもいることでしょうから、何度も違うhead-countを持って出現する可能性があります。

 transformが終わった時点で、JSONは以下のようなものになります。

[
   {"firstname": "健太","head-count": 78},
   {"firstname": "匠","head-count": 73},
   {"firstname": "里奈", "head-count": 67},
   {"firstname": "翔太", "head-count": 67},
   {"firstname": "里奈", "head-count": 62},
   :
   :
以下同様
]

group by

 group by は、MapReduceでいうところのシャッフルとReduce処理に相当します。前半の「$firstnem_group = $.firstname」によって、以下のような状態になります(数値はデタラメです)。

$firstname_group: "健太"

$:[
   {"firstname": "健太","head-count": 1},
   {"firstname": "健太","head-count": 78},
   {"firstname": "健太","head-count": 51},
   {"firstname": "健太","head-count": 29}
]

 各行のhead-count属性の配列の数値を合計するために、「sum($[*].head-count)」を使います。$変数の配列のすべてのエントリーの「head-count」属性の値を合計する、という意味です。これをtotalという名前を付けてつなぎ合わせ、「into {$firstname_group, total: sum($[*].head-count)}」によって、最終的な形状のJSONができ上がります。あとは、totalフィールドでソートし、名前だけのランキングを出しているわけです。

まとめと期待

 Jaqlは、MapReduceを簡単に記述するためのとても便利な言語です。シェルから試す場合、試行錯誤しながらMapReduceジョブを作っていくことが可能ですし、Javaで記述するような型の宣言などの整合性などで悩むことは少ないと考えられます。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

米持 幸寿(ヨネモチ ユキヒサ)

日本アイ・ビー・エム公認 ソフトウェア・エバンジェリスト。alphaWorks、developerWorks、インキュベーション系製品、アセットなどのテクノロジーの推進をしつつ、テクノロジー戦略、エバンジェリストチームをリードしている。講演や執筆も多数。主な著書に「かんたんサーバーサイドJava」(翔泳社)、「Webサービス完全解説」(翔泳社)がある。developerWorks Japan ブログ 「米持幸寿のブログ

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5646 2010/12/24 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー