めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Jaql入門(4) - さまざまなサンプル

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