wordcount
jaql> splitArr = builtin("com.acme.extensions.expr.SplitIterExpr$Descriptor"); // 組み込みの split 関数 jaql> read(lines("Karamazov.txt")) -> expand splitArr( $, "[^\\w]+" ) -> group by $word = $ into { $word, num: count($) } -> filter $.word != "" -> sort by [ $.num desc ] -> top 10; [ { "word": "the", "num": 14222 }, { "word": "and", "num": 10159 }, { "word": "to", "num": 9462 }, { "word": "I", "num": 8007 }, { "word": "of", "num": 7273 }, { "word": "a", "num": 6653 }, { "word": "he", "num": 6106 }, { "word": "that", "num": 5712 }, { "word": "you", "num": 5409 }, { "word": "in", "num": 5308 } ]
「カラマーゾフの兄弟」に含まれる単語の個数のトップ10です。
read(lines("Karamazov.txt"))は、HDFSのテキストファイルを読んで、1行が1要素のストリング配列に変換します。次に、splitArrで各行を単語が要素の配列にしたものをexpandで、全体として1つのフラットな配列に変換します。その他の処理は、関数名から想像がつくと思います。
MapReduceを意識せずに、自然な手続きで処理が書けることがよく分かります。
複数データの出力
MapReduce プログラミングでは、Map or Reduce 関数の中で、1つのレコードに対して複数のレコードを出力することがあります。同じことを Jaql でやる場合は、次のようにします。
jaql> $input = [ { number: 1 }, { number: 2 }, { number: 3 } ]; jaql> $input -> transform [ { number: $.number }, { number: -($.number) } ]; [ [ { "number": 1 }, { "number": -1 } ], [ { "number": 2 }, { "number": -2 } ], [ { "number": 3 }, { "number": -3 } ] ] jaql> $input -> transform [ { number: $.number }, { number: -($.number) } ] -> expand; [ { "number": 1 }, { "number": -1 }, { "number": 2 }, { "number": -2 }, { "number": 3 }, { "number": -3 } ]
transform でレコードを出力する時に、配列を使って、複数レコードを出力します。それを expand することで、配列をはずして、複数レコードに展開します。