「やりますか?どうしますか?とりあえずカレンダー作りますか?」っとか言いながら気づいたら12月になってしまいました。
MapRエンジニア勢が年末にかけて超絶忙しくなったため、もはやアドベントではなく正月の宿題、いや2018年中に終わればいいな(;´∀`)くらいになりそうですが、初日くらいは間に合わせたい!という気持ちで書いてみます。
ということで、MapR Advent Calendar 2017 の初日になります。
MapR-DB JSONとは
MapR-DBはMapR社が提供するNoSQLデータベースとなります。
MapR-DBはMapR-DB BinaryとMapR-DB JSONがあり、MapR-DB BinaryのほうはHBase APIを利用してアクセス出来ます。
MapR-DB JSONはMapR社がメインで開発しているOJAI(Open JSON Application Interface : オージェーエーアイ、オーハイと呼ばれることが多い)というAPIでアクセス出来ます。
両者の比較やMapR-DB BinaryとHBaseの比較については別の日にエントリーにしたいと思います。
今回はこちらの記事を参考にしながら、MapR-DB JSONにRubyからアクセスしてみます。
準備
Rubyからアクセスといっても、MapR-DB JSON用のバインディングはJava, C, Pythonしか提供されておらず、Rubyからアクセスしたい場合はJRubyを利用し、Javaのライブラリをインクルードする必要があります。
まずはご利用の環境でJRubyのインストールを行って下さい。
私の環境ではrvmをインストールしていたので、こちらで一発です。
# rvm install jruby
# rvm use jruby
# ruby -v
jruby 9.1.13.0 (2.3.3) 2017-09-06 8e1c115 OpenJDK 64-Bit Server VM 25.151-b12 on 1.8.0_151-b12 +jit [linux-x86_64]
Let's play
OJAIのAPIのドキュメントはこちらになります。
これを使って、まずはテーブルを作成し、データを突っ込んだり、編集したりしてみましょう。
include Java
Dir["/opt/mapr/lib/\*.jar"].each { |jar| require jar }
java_import com.mapr.db.MapRDB
java_import com.mapr.db.Table
java_import org.ojai.Document
java_import org.ojai.DocumentStream
java_import org.ojai.store.DocumentMutation
java_import org.ojai.store.QueryCondition
java_import org.ojai.store.exceptions.DocumentExistsException
java_import org.ojai.types.ODate
java_import java.io.IOException
java_import java.util.Arrays
java_import java.util.Iterator
java_import org.ojai.store.QueryCondition
tableName = "/user/mapr/db/testtable"
table = nil
if (!MapRDB.table_exists?(tableName))
puts "Table Doesn't exist, creating"
table = MapRDB.create_table(tableName)
else
puts "Table exists"
table = MapRDB.get_table(tableName)
end
これを実行するとパス /user/mapr/db/testdb にMapR-DB JSONテーブルが作成されます。
$ ruby test.rb
Table Doesn't exist, creating
$ maprcli table info -path /user/mapr/db/testtable -json
{
"timestamp":1512113101782,
"timeofday":"2017-12-01 02:25:01.782 GMT-0500",
"status":"OK",
"total":1,
"data":[
{
"path":"/user/mapr/db/testtable",
"numregions":1,
"totallogicalsize":0,
"totalphysicalsize":0,
"totalcopypendingsize":0,
"totalrows":0,
"totalnumberofspills":0,
"totalnumberofsegments":0,
"autosplit":true,
"bulkload":false,
"tabletype":"json",
"regionsizemb":4096,
"audit":false,
"maxvalueszinmemindex":100,
"adminaccessperm":"u:mapr",
"createrenamefamilyperm":"u:mapr",
"bulkloadperm":"u:mapr",
"indexperm":"u:mapr",
"packperm":"u:mapr",
"deletefamilyperm":"u:mapr",
"replperm":"u:mapr",
"splitmergeperm":"u:mapr",
"defaultcompressionperm":"u:mapr",
"defaultmemoryperm":"u:mapr",
"defaultreadperm":"u:mapr",
"defaulttraverseperm":"u:mapr",
"defaultwriteperm":"u:mapr",
"uuid":"fd99673e-4a43-1425-3851-0db603215a00"
}
]
}
テーブルが出来ました。次に何かデータを入れて削除してみましょう
MapR-DB JSONではJSON型のドキュメントを作成し、このドキュメントをテーブルに登録します。
削除の際にはドキュメントやrowkeyを指定して削除するrowを選択出来ますが、ここではrowkeyを使ってみます。
# 上のコードに続けて
(10..20).each {|id|
document = MapRDB.new_document
.set("_id", "jdoe#{id}")
.set("first_name", "John#{id}")
.set("last_name", "Doe#{id}")
.set("dob", ODate.parse("2016-06-23"))
.set("drink", ["beer", "apple cider", "coffee"])
.set("level", id)
table.insert_or_replace(document)
}
# 検証
record = table.find_by_id("jdoe")
puts "jdoe's info #{record.inspect}"
record = table.find_by_id("jdoe15")
puts "jdoe15's info #{record.inspect}"
puts "jdoe15's drink info #{record["drink"]}"
table.delete("jdoe20")
record = table.find_by_id("jdoe20")
puts "jdoe20's info #{record.inspect}"
実行してみます。
$ ruby test.rb
Table exists
jdoe's info nil
jdoe15's info {"_id"=>"jdoe15", "dob"=>#<Java::OrgOjaiTypes::ODate:0x1e4d3ce5>, "drink"=>#<Java::ComMaprDbRowcol::DBList:0x4b5189ac>, "first_name"=>"John15", "last_name"=>"Doe15", "level"=>15}
jdoe15's drink info ["beer", "apple cider", "coffee"]
jdoe20's info nil
期待通り動いていますね。
おわかりのように格納するデータ型にはStringだけでなくさまざまな型が使えます。
ちなみにODateはOJAIで定義されているデータ型となります。
更新の際にもtableに対してreplace()やupdate()メソッドを利用することが出来ます。
ここではDocumentMutationクラスを利用した更新についてご紹介します。
DocumentMutationではドキュメントに対する更新をオブジェクトで持ち、これをテーブルのupdate()に渡してテーブルを更新することが出来ます。
この時、append() や merge()、increment()といったメソッドにより、型を意識した変更を表現することが出来ます。
試してみましょう。
# 上のコードに続けて
mutation = MapRDB.new_mutation
# 新規カラム追加
mutation.set("father's_name", "Andre")
# 既存カラムへの追記。同一のカラムに対しては最終の更新のみ反映
mutation.append("first_name", " Jonny")
mutation.append("first_name", " Jonathan")
# 既存のカラムでない場合は追加扱い
mutation.append("middle_name", "Smith")
# Array要素の追加
mutation.append("drink", ["fanta"])
# 削除
mutation.delete("last_name")
# Map型のマージ
mutation.merge("other_info", {"car" => "Mustang"})
# 数値のインクリメント
mutation.increment("level", 10)
table.update("jdoe15", mutation)
record = table.find_by_id("jdoe15")
puts record.inspect
puts record["drink"]
もはやRubyっぽさが全くないコードになってきましたが、気にしないでいきましょう。
実行してみます。
$ ruby test.rb
Table exists
jdoe's info nil
jdoe15's info {"_id"=>"jdoe15", "dob"=>#<Java::OrgOjaiTypes::ODate:0xa307a8c>, "drink"=>#<Java::ComMaprDbRowcol::DBList:0x6986852>, "first_name"=>"John15", "last_name"=>"Doe15", "level"=>15, "other_info"=>{"city"=>"LA"}}
jdoe15's drink info ["beer", "apple cider", "coffee"]
jdoe20's info nil
#<Java::ComMaprDbRowcol::MutationImpl:0x73a8da0f>
{"_id"=>"jdoe15", "dob"=>#<Java::OrgOjaiTypes::ODate:0x7d898981>, "drink"=>#<Java::ComMaprDbRowcol::DBList:0x21129f1f>, "father's_name"=>"Andre", "first_name"=>"John15 Jonathan", "level"=>25, "middle_name"=>"Smith", "other_info"=>{"car"=>"Mustang", "city"=>"LA"}}
["beer", "apple cider", "coffee", "fanta"]
期待通りですね。
遊び終わったらテーブルを掃除しておきましょう。
# 上のコードに続けて
MapRDB.deleteTable(tableName)
確認です。
$ ruby test.rb
...省略...
$ maprcli table info -path /user/mapr/db/testtable -json
{
"timestamp":1512117965049,
"timeofday":"2017-12-01 03:46:05.049 GMT-0500",
"status":"ERROR",
"errors":[
{
"id":22,
"desc":"Requested file /user/mapr/db/testtable does not exist."
}
]
}
掃除されていました。
さて、今回はRubyからMapR-DB JSONにアクセスしてみました。
次回はPythonからアクセスしてみる、予定です。