query: tag:tokyotyrant
  • ActiveRecord の TC/TT アダプタ
  • AR 上で動作するので全てのAPIが利用可能

インストール

shell>>
% gem install activetokyocabinet
<<--

サーバの起動

shell>>
% ttserver -port 11114 db.tct &
<<--

  • ポート(11114)は何でもよい
  • ファイル名も何でもよい (拡張子はtct)

セットアップ

ruby>>
require 'active_tokyocabinet/tdb'

ActiveRecord::Base.establish_connection(
:adapter => 'tokyotyrant',
:database => {
:englishes => {:host => 'localhost', :port => 11114},
}
);
<<--

  • adapter 名は 'tokyotyrant'
  • database で利用するテーブル名と参照するTTの設定を指定する
  • 以下で Englishモデルを使うので "englishes" を定義している
  • 複数のモデルを使うときはやっぱりTTが複数必要?

使い方

ruby>>
class English < ActiveRecord::Base
include ActiveTokyoCabinet::TDB

string :word
int :length

validates_presence_of :word, :length

def validate
self[:length] ||= word.to_s.size
end
end
<<--

  • ARなので validate でも何でも思い通り
  • カラム定義は string, int, float (date/datetimeはない?)

ruby>>

/usr/share/dict/words を流し込み

buf = File.read("/usr/share/dict/words")
buf.scan(/^([a-z]+)$/) {
English.create!(:word=>$1)
}

English.count
=> 64024

English.all(:conditions=>["word regexp ? and length > ?", '^mai', 12])
[#<English id: 33247, word: "mainstreaming", length: "13">,
#<English id: 33250, word: "maintainability", length: "15">]
<<--

  • 正規表現の検索も可能

ruby>>
e = English.first
e.delete
ActiveRecord::StatementInvalid: NoMethodError: undefined method values_at' for 1:Fixnum: DELETE FROM englishes WHERE (id IN (1)) from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/connection_adapters/abstract_adapter.rb:219:in log'
<<--

あれ!?あれ?

まとめ

  • ARで稼動する上に正規表現による検索も可能
  • 削除(delete, destroy)ができないのはご愛嬌

で、一番驚いたのは、実装方法。
普通なら、ARの各メソッドをTT化していきそうなものだが、

  1. ARにSQLを作成させて
  2. そのSQLをパーズして
  3. TTに翻訳する

その発想はなかった。目から鱗です。
というか、SQLのparserは実用性が高いのでまず、
それをgem化希望!激しく希望!!

参考

posted by maiha maiha on Fri 26 Feb 2010 at 01:28 with 2 comments

TokyoTyrant を ActiveRecord 風のAPIで利用するライブラリ

インストール

shell>>
% gem install miyazakiresistance
<<--

セットアップ

適当なポートで TT を起動。(テーブルデータベースを利用するので *.tct)

shell>>
% ttserver -port 11114 services.tct &
<<--

使用例

ruby>>
require 'rubygems'
require 'miyazakiresistance'

class Service < MiyazakiResistance::Base
set_server "localhost", 11114, :write
set_timeout 1

set_column :name , :string
set_column :port , :integer
set_column :proto, :string
end

buf = File.read("/etc/services")
buf.scan(%r{^(\w+)\s+(\d+)/(udp|tcp)}) {
Service.create(:name=>$1, :port=>$2, :proto=>$3)
}

Service.count
=> 373

Service.first
=> #<Service:... @id=1, @port=1, @proto="tcp", @name="tcpmux">

Service.find_all_by_port(80)
=> [#<Service:... @id=40, @port=80, @proto="tcp", @name="www">,
#<Service:... @id=41, @port=80, @proto="udp", @name="www">]
<<--

  • データ型は :string, :integer, :date, :datetime
  • master/slave, dual master をサポート
  • ARのdynamic finderもサポート
  • (created|updated)_(on|at) は magic column
  • TTへの保存キーは id の値 (数字の連番が自動付与)

欠点

  • スキーマを途中で変更するとエラー (DB内は同じスキーマのデータが必要)
  • 1モデル毎にTTサーバが1つ必要になる?
  • :date, :datetime を空にできない (Time.at(0) になる)
  • "set_" prefix が冗長

という実装を見る限り、想定されたユースケースは、

  • 任意のドキュメントを格納

ではなく、

  • 固定されたスキーマ定義によってTTをRDB的に利用

のようだ。
ARもASも必要としないので、「TTを手軽に便利にCRUDしたい」
という用途にはピッタリだろう。

参考

posted by maiha maiha on Thu 25 Feb 2010 at 08:24 with 0 comments