最近はレコメンドウィジェットTAXELの開発をやっているCTO室のHadoopエンジニアのJ.Nです。
HBaseで日本語を取り扱う時、日本語データはバイト列で格納されるためHBase Shellでデータを確認したい時はバイト列がprintされます。
このままだと、HBaseのデータを確認したい時に実際に何が入ってるのかが不明なため非常に困ります。
1 2 3 4 5 6 7 8 |
hbase(main):023:0> scan 'noda_test' ROW COLUMN+CELL 1 column=keihintouhoku:sn, timestamp=1454471594989, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 1 column=soubusen:sn, timestamp=1454471595769, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 1 column=yamanote:sn, timestamp=1454471564495, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 2 column=keihintouhoku:sn, timestamp=1454471721932, value=\xE7\xA5\x9E\xE7\x94\xB0 2 column=yamanote:sn, timestamp=1454471721202, value=\xE7\xA5\x9E\xE7\x94\xB0 2 row(s) in 0.0300 seconds |
HBaseにはJRubyが内包されており、HBase ShellもJRubyで書かれてるため、JRubyでバイト列を文字列にするスクリプトを書けばとりあえず確認できるので書いてみました。
テストデータの準備
テーブルを作成
1 2 |
HBase shell create 'noda_test', {NAME => 'yamanote'}, {NAME => 'keihintouhoku'}, {NAME => 'soubusen'} |
HBaseに日本語付のデータを投入
1 2 3 4 5 6 |
put 'noda_test', '1', 'yamanote:sn','秋葉原' put 'noda_test', '1', 'keihintouhoku:sn','秋葉原' put 'noda_test', '1', 'soubusen:sn','秋葉原' put 'noda_test', '2', 'yamanote:sn','神田' put 'noda_test', '2', 'keihintouhoku:sn','神田' |
scan でデータ表示
1 2 3 4 5 6 7 8 |
hbase(main):023:0> scan 'noda_test' ROW COLUMN+CELL 1 column=keihintouhoku:sn, timestamp=1454471594989, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 1 column=soubusen:sn, timestamp=1454471595769, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 1 column=yamanote:sn, timestamp=1454471564495, value=\xE7\xA7\x8B\xE8\x91\x89\xE5\x8E\x9F 2 column=keihintouhoku:sn, timestamp=1454471721932, value=\xE7\xA5\x9E\xE7\x94\xB0 2 column=yamanote:sn, timestamp=1454471721202, value=\xE7\xA5\x9E\xE7\x94\xB0 2 row(s) in 0.0300 seconds |
バイト列でデータが入っている
JRubyで日本語表示できるプログラムを書く
print_jr.rbというファイル名で保存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
include Java import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.client.Put import org.apache.hadoop.hbase.client.Get import org.apache.hadoop.hbase.client.Scan import org.apache.hadoop.hbase.client.HTable import org.apache.hadoop.hbase.util.Bytes conf = HBaseConfiguration.create conf.add_resource "/etc/hbase/conf/hbase.xml" table = HTable.new(conf, "noda_test") scan = Scan.new scanner = table.get_scanner(scan) begin while (row = scanner.next()) do yamanote_station_name = row.getValue("yamanote".unpack("C*"), "sn".unpack("C*")) puts "山手線->" + Bytes.toString(yamanote_station_name) keihin_station_name = row.getValue("keihintouhoku".unpack("C*"), "sn".unpack("C*")) puts "京浜東北線->" + Bytes.toString(keihin_station_name) soubu_station_name = row.getValue("soubusen".unpack("C*"), "sn".unpack("C*")) puts "総武線->" + Bytes.toString(soubu_station_name) if soubu_station_name != nil end ensure scanner.close end |
実行(コンソール表示)
1 |
hbase org.jruby.Main print_jr.rb |
結果
1 2 3 4 5 |
山手線->秋葉原 京浜東北線->秋葉原 総武線->秋葉原 山手線->神田 京浜東北線->神田 |
実行(ファイル保存)
1 2 3 4 5 6 7 |
hbase org.jruby.Main print_jr.rb > station_list.txt cat station_list.txt 山手線->秋葉原 京浜東北線->秋葉原 総武線->秋葉原 山手線->神田 京浜東北線->神田 |
汎用的な日本語表示プログラム
先ほどのプログラムを改良し、汎用的なものを作りました。とりあえず日本語を表示したい、またはパイプラインでファイルに保存したいといった時に以下のプログラムが使えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
include Java import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.client.Put import org.apache.hadoop.hbase.client.Get import org.apache.hadoop.hbase.client.Scan import org.apache.hadoop.hbase.client.HTable import org.apache.hadoop.hbase.util.Bytes conf = HBaseConfiguration.create conf.add_resource "/etc/hbase/conf/hbase.xml" if ARGV.length < 3 puts "ARGV ERROR !!!! (;><)" puts "-----------------------------------------------------------------------------" puts "hbase org.jruby.Main hbase_ja_print.rb [TableName] [ColumnFamily] [qualifier]" puts "hbase org.jruby.Main hbase_ja_print.rb noda_test soubusen sn" puts "-----------------------------------------------------------------------------" puts "ARGV ERROR !!!! (;><)" exit end table_name = ARGV[0] column_family = ARGV[1] qualifier = ARGV[2] table = HTable.new(conf, table_name) scan = Scan.new scanner = table.get_scanner(scan) begin while (row = scanner.next()) do v = row.getValue(column_family.unpack("C*"), qualifier.unpack("C*")) puts Bytes.toString(v) if v != nil end ensure scanner.close end |
使い方
引数が足りない場合(HELPを表示)
1 2 3 4 5 6 7 8 9 10 11 12 |
[hbase@dev-s1 jruby_script]$ hbase org.jruby.Main hbase_ja_print.rb noda_test ARGV ERROR !!!! (;><) ----------------------------------------------------------------------------- hbase org.jruby.Main hbase_ja_print.rb [TableName] [ColumnFamily] [qualifier] hbase org.jruby.Main hbase_ja_print.rb noda_test soubusen sn ----------------------------------------------------------------------------- ARGV ERROR !!!! (;><) SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/usr/hdp/2.3.2.0-2950/hadoop/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/usr/hdp/2.3.2.0-2950/zookeeper/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory] |
引数がある場合
1 2 3 4 |
hbase org.jruby.Main hbase_ja_print.rb noda_test yamanote sn 秋葉原 神田 |
他に、日本語表示する冴えたやり方があったら是非教えてください(;><)!