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 することで、配列をはずして、複数レコードに展開します。