以前試しで Redis を使ってたのですが、メモリ上限が気になって本番環境ではどうだろう、とちょっと躊躇してましたが、「Riakとかどうだろう?」って教えてもらったので、さっそく使ってみました。
インストール
インストールを試みましたが、環境が古くてできませんでした。。。
なので、すでにインストール済みの環境を借りることができたので、それを使います。
# たぶん apt とかでインストールできるかと。。。
# version は 2.0.0 でした。
Rubyから使う
gem install 'riak-client'
でライブラリをインストールします。
っとその前に Riak について
Riak は Erlang で記述された Key-Value Store です。
NoSQLは最近流行ってますね。
データの構造とかは特に考えずに、どかーっとデータを格納して利用したい場面で使います(^^;
あとは色々特徴がありますが、公式を参照してみてください。
基本
Riak は bucket と呼ばれる バケツ にデータを ガーっ と入れます。
バケツ に入れるデータは Key で一意にわかるようなデータで、なんでも入ります。
基本的にはバケツ毎管理なので、バケツが違えば同じ Key でも問題ないみたいです。
ただし、bucket_type というものを使ってくるめることもできるみたいです。
接続
require "riak"
client = Riak::Client.new(:nodes => [{:host => "localhost", :protocol => "pbc", :pb_port => 8087}])
ノードを複数設定することも可能です。
バケツの生成
bucket = client.bucket("users")
users バケツを作りました。
この中に色々入れていきます。
が、このままでは入れて出すだけしかできないので、検索できるように設定します。
検索設定
client.create_search_index("users")
client.set_bucket_props(bucket, { :search_index => "users" })
users に検索インデックスを作成しました。これで検索ができるようになりました。
データ登録
ポイントは、検索対象の項目には _s とか目印をつけなければならないところです。
検索対象が文字列の場合は、 _s (string)
検索対象が数値の場合は、 _i (int)、_l (long)、_d (double)
検索対象がブール値の場合は、 _b (boolean)
などです。
user = bucket.new("Manaka Laala")
user.data = {:birthday_s => "11/20", :bloodtype_s => "O", :favorite_s => "pizza", :charm_s => "lovly", :team_s => "SoLaMiSmile"}
user.store
user = bucket.new("Minami Mirei")
user.data = {:birthday_s => "10/01", :bloodtype_s => "A", :favorite_s => "sweets", :charm_s => "pop", :team_s => "SoLaMiSmile"}
user.store
user = bucket.new("Hojo Sophy")
user.data = {:birthday_s => "07/30", :bloodtype_s => "AB", :favorite_s => "red-flush", :charm_s => "cool", :team_s => "SoLaMiSmile"}
user = bucket.new("Todo Shion")
user.data = {:birthday_s => "01/05", :bloodtype_s => "B", :favorite_s => "daifuku", :charm_s => "cool", :team_s => "DressingPafe"}
user.store
user = bucket.new("Dorothy West")
user.data = {:birthday_s => "02/05", :bloodtype_s => "A", :favorite_s => "monja-yaki", :charm_s => "pop", :team_s => "DressingPafe"}
user.store
user = bucket.new("Reona West")
user.data = {:birthday_s => "02/05", :bloodtype_s => "A", :favorite_s => "strawberry pafe", :charm_s => "pop", :team_s => "DressingPafe"}
user.store
bucket.new の引数が キー になります。これが一意の値でないといけません。
同じ場合は、上書きされるので注意しましょう。
# 実際に使用する場合は、Key は 一意の ID になるのでしょう。
呼び出し、上書き、削除
user = bucket.get("Minami Mirei")
Riak::RObject で帰ってきます。
中身は user.data で見ることができます。
キーが無い場合はエラーになります。
user = bucket.get("Shiratama Mikan")
#> Expected success from Riak but received not_found. The requested object was not found.(Riak::ProtobuffsFailedRequest)
上書きは先ほども触れましたが、同じキーで get して store するだけです。
user = bucket.get("Manaka Laala")
user.data[:favorite_s] = "pizza, da-gashi"
user.store
削除は delete です。キーを指定して削除します。
bucket.delete("Dorothy West")
検索
検索設定をしていれば案外簡単です。
result = client.search("users", "charm_s:cool")
これで charm_s が cool のものを取ってきます。
上の例では Sophy と Shion が返ってきます。
ヒットした数は result["num_found"] に格納されています。
実際の値は result["docs"] に配列として格納されていますので、取り出して利用します。
p result["num_found"]
#> 2
p result["docs"]
#> [{"score"=>"1.83000e+00", "_yz_rb"=>"users", "_yz_rt"=>"default", "_yz_rk"=>"Hojo Sophy", "_yz_id"=>"1*default*users*Hojo Sophy*6", "birthday_s"=>"07/30", "bloodtype_s"=>"AB", "favorite_s"=>"red-flush", "charm_s"=>"cool", "team_s"=>"SoLaMiSmile"}, {...}]
が、戻りは普通のハッシュデータなので、キーを取得して、バケツから取り出した方が素直かもしれません。
キーは _yz_rk
で、バケツは _yz_rb
です。
result["docs"].each do |doc|
key = doc["_yz_rk"]
user = bucket.get(key)
...
end
検索文字列
上記の例では charm_s:cool
がこれに該当します。
これは、 charm_s が cool のもの
で SQL文では charm_s = 'cool'
にあたります。
同様に、
charm_s like '%c%' は charm_s:*c* になります。
charm_s = 'pop' and team_s like '%So%' の場合は charm_s:pop AND team_s:*So* になります。
AND は大文字でないと認識しないので注意しましょう。
数値の場合は
age = 10 は age_i:10
age > 10 and age =< 15 は age_i:[11 TO 15]
です。
とりあえず、以上な感じですかね。
普通に使う感じではこれくらいあればいけるかなと思います。