Ludia ç¨ã® Rails ãã©ã°ã¤ã³ acts_as_ludia ãä½ãã¾ãã
第ååå å
使ãæ¹ãæ©è½ãªã©ï¼è©³ããã¯å¾ã§æ¸ãã¾ãæ¸ãã¾ããï¼
acts_as_ludiaの更新情報ãã覧ä¸ããï¼
acts_as_ludiaã®æ¦è¦ã¨æ©è½
Ludiaã«ããPostgreSQLã®é«éå
¨ææ¤ç´¢æ©è½ãRuby on Railsããå©ç¨ããããã®ãã©ã°ã¤ã³ã§ãï¼ååã¯acts_as_ludiaã¨ããã¾ãï¼ãã®ã¾ãã¾ã§ããã¾ããï¼ï¼
ä»ã¾ã§RailsããLudiaã使ãå ´åï¼findã®conditionsãªãã·ã§ã³ã«ã¯ã¨ãªãããããæ¸ãå¿
è¦ãããã¾ããï¼
Model.find(:all, :conditions => "body @@ '*D+ ruby rails'")
ãããï¼acts_as_ludiaãå©ç¨ããã¨ä»¥ä¸ã®ããã«ç°¡æ½ã«ã³ã¼ããæ¸ããã¨ãã§ãã¾ãï¼ãã¡ããï¼AND/ORæ¤ç´¢ãæ¥æ¬èªã§ã®æ¤ç´¢ãå¯è½ã§ãï¼
Model.find_fulltext(:body => "ruby rails")
ã¾ãï¼Ludia 1.0.0ãã追å ãããæ¤ç´¢åèªå¨è¾ºã®æç« ï¼ããããã¹ãããã(snippet)ï¼ãç°¡åãªæ¸å¼ã§æ¤ç´¢çµæã¨åæã«åå¾ãããã¨ãã§ãã¾ãï¼
Model.find_fulltext({:body => "ruby rails"}, :snippet => true)
ãã®ã¨ã³ããªã®ç¶ãã«ã¯ï¼acts_as_ludiaã®å°å ¥æ¹æ³ï¼ä½¿ãæ¹ã«ã¤ãã¦æ¸ãã¦ããã¾ãï¼
ãã¢ãã¼ã¸
acts_as_ludiaã使ã£ã¦ããã£ã¨ã¯ã¦ãå
¨ææ¤ç´¢ãä½ã£ã¦ã¿ã¾ããï¼
acts_as_ludia を使ってLudiaのデモWebアプリケーションを作りました - のほほん徒然
acts_as_ludiaã®å°å ¥æ¹æ³
acts_as_ludiaã®å°å ¥ã¯ä»¥ä¸ã®æé ã§è¡ãã¾ãï¼ãªãï¼Ludiaã®å°å ¥ã¯ãã§ã«æ¸ãã§ãã¦ï¼æ£å¸¸ã«ç¨¼åãã¦ããã¨ãã¾ãï¼
- Railsã¢ããªã±ã¼ã·ã§ã³ã®ä½æ
- ãã¦ã³ãã¼ãã¨é ç½®
- Modelã¸ã®è¿½è¨
é©å½ãªRailsã¢ããªã±ã¼ã·ã§ã³ã®ä½æ
acts_as_ludiaãã©ã°ã¤ã³ãå©ç¨ããããã®Railsã¢ããªã±ã¼ã·ã§ã³ãPostgreSQLããã¼ã¿ãã¼ã¹ã«å©ç¨ãã¦ä½æãã¾ãï¼ãã§ã«PostgreSQLãå©ç¨ãã¦ããã¢ããªã±ã¼ã·ã§ã³ãããï¼ããã«å°å
¥ããå ´åï¼ãã®é
ã¯èªã¿é£ã°ãã¦ããã ãã¦ãã¾ãã¾ããï¼
ããã§ã¯ï¼ã¢ããªã±ã¼ã·ã§ã³åãludiaã¨ãï¼ä»¥ä¸ã®æé ã§ä½æãã¾ãï¼
- railsã³ãã³ãã«ããéå½¢ä½æ
- config/database.ymlä¿®æ£
- config/environment.rbã¸ã®è¿½è¨
- ãã¼ã¿ãã¼ã¹ã¨ãã¼ãã«ã®ä½æ
railsã³ãã³ãã«ããéå½¢ä½æ
PostgreSQLãå©ç¨ããããæ示çã«æå®ãã¦ãã¾ãï¼$ rails ludia --database=postgresql
config/database.ymlä¿®æ£
以ä¸ã®ããã«database.ymlãä¿®æ£ãã¾ãï¼ã³ã¡ã³ãé¨åã¯åã£ã¦ããã¾ãï¼ï¼ãªãï¼PostgreSQLã®ã¦ã¼ã¶ã¨ãã¦railsããã¹ã¯ã¼ãrailsã§ä½ã£ã¦ãããã®ã¨ãã¾ãï¼login: &login adapter: postgresql username: rails password: rails host: localhost development: database: ludia_development <<: *login test: database: ludia_test <<: *login production: database: ludia_production <<: *login
config/environment.rbã¸ã®è¿½è¨
ãªãã®ãã¨ã¯ããã¾ããï¼æ¥æ¬èªãæ±ãããã®è¨è¿°ãå é ã«è¿½å ããã ãã§ãï¼# 以ä¸ã®2è¡ãenvironment.rbã®å é ã«è¿½å $KCODE = 'u' require 'jcode'
ãã¼ã¿ãã¼ã¹ã¨ãã¼ãã«ã®ä½æ
PostgreSQLã«ãã¼ã¿ãã¼ã¹ã¨ãã¼ãã«ãä½æãã¾ãï¼ä»åã¯ã¾ãï¼ãã¼ã¿ãã¼ã¹ã®ä½æã¨Ludiaç¨ã®SQLãæµãè¾¼ã¿ã¾ãï¼
$ createdb ludia_development -U rails Password for user rails: CREATE DATABASE $ psql -f vendor/plugin/acts_as_ludia/share/pgsenna2.sql ludia_development -U rails Password for user rails: CREATE FUNCTION CREATE FUNCTION ï¼ä¸ç¥ï¼ CREATE OPERATOR CLASS CREATE OPERATOR CLASS
次ã«ï¼Migrationãå©ç¨ãã¦ãã¼ãã«ãä½æãã¾ãï¼ã¤ãã§ã«Modelãä½ã£ã¡ããã¾ãããï¼
$ ruby script/generate model Test
é©å½ãªã¨ãã£ã¿ã§ãã¤ã°ã¬ã¼ã·ã§ã³ãã¡ã¤ã«ï¼#{RAILS_ROOT}/db/migrate/001_create_tests.rbï¼ãéãï¼ä»¥ä¸ã®ããã«è¨è¿°ãã¾ãï¼
class CreateTests < ActiveRecord::Migration def self.up create_table :tests do |t| t.column :col1, :text t.column :col2, :string end execute(%|CREATE INDEX index1 ON tests USING fulltext(col1);|) execute(%|CREATE INDEX index2 ON tests USING fulltextb((col2::text));|) end def self.down execute(%|DROP INDEX index1;|) execute(%|DROP INDEX index2;|) drop_table :tests execute(%|SELECT pgs2destroy();|) end end
rakeãå®è¡ãã¦ãã¼ãã«ãä½æãã¾ãï¼
$ rake migrate
ãã¦ã³ãã¼ãã¨é ç½®
script/pluginãå©ç¨ããã¤ã³ã¹ãã¼ã«ãå¯è½ã§ãï¼ä¸è¨è¿½è¨é¨åãã覧ãã ããï¼
wgetãcurlãªã©ãå©ç¨ãã¦ä»¥ä¸ã®URLããacts_as_ludiaããã¦ã³ãã¼ããã¦ï¼è§£åå¾ã¢ããªã±ã¼ã·ã§ã³ã®ãã©ã°ã¤ã³ãã£ã¬ã¯ããªã«é
ç½®ãã¦ãã ããï¼
acts_as_ludia-0.2.0.tar.gz
â»ããã¯robyforgeã§ããã¸ã§ã¯ããéå§ããï¼script/pluginã§ã¤ã³ã¹ãã¼ã«ã§ããããã«ãªãã¾ã§ã®éå®æªç½®ã§ãï¼ãªã³ã¯å
ããªããªã£ããï¼ææ°çã§ã¯ãªããªãå¯è½æ§ãããã¾ãï¼
追è¨(2007/03/19 21:30)
rubyforgeã¸ã®ããã¸ã§ã¯ãç»é²ãå®äºããã®ã§ï¼script/pluginãå©ç¨ããã¤ã³ã¹ãã¼ã«ãå¯è½ã«ãªãã¾ããï¼#{RAILS_ROOT}ã§ä»¥ä¸ã®ã³ãã³ããå®è¡ãããã¨ã§ãã¦ã³ãã¼ãã¨ã¤ã³ã¹ãã¼ã«ãå¯è½ã§ãï¼$ ruby script/plugin install svn://rubyforge.org/var/svn/actsasludia
Modelã¸ã®è¿½è¨
Modelã¸acts_as_ludiaãå©ç¨ããããã®è¨è¿°ã追å ãã¾ãï¼
#{RAILS_ROOT}/app/models/your_model.rb
class YourModel < ActiveRecord::Base acts_as_ludia end
以ä¸ã§ï¼acts_as_ludiaã®å°å ¥ã¯å®äºã§ãï¼ãããã¨æéããããããã«æãã¾ããï¼Railsã¢ããªã±ã¼ã·ã§ã³ã¨ãã¼ã¿ãã¼ã¹ã®åæè¨å®ãæ¸ããã®ã§é·ããªã£ã¦ãã¾ãã¾ããï¼ãã©ã°ã¤ã³ã ããªãã¤ã³ã¹ãã¼ã«ï¼ãã¦ã³ãã¼ãã¨é ç½®ï¼ãã¦ã¢ãã«ã«ä¸è¡æ¸ã足ãã ãã§ãï¼
acts_as_ludiaã®ä½¿ãæ¹
acts_as_ludiaã®ä½¿ãæ¹ã¯ããã£ã¦ç°¡åã§ãï¼ActiveRecord::Baseã«find_fulltextã¡ã½ããã追å ããã¦ãã¾ãã®ã§ï¼é常ã®findã®ããã«å¼ã³åºããã¨ã§Ludiaã®å
¨ææ¤ç´¢ãå©ç¨ãããã¨ãã§ãã¾ãï¼
ããã§ã¯ï¼å®éã®å©ç¨ä¾ã¨ã¨ãã«ä½¿ãæ¹ã説æãã¾ãï¼
ãã¹ããã¼ã¿ä½æ
ã¾ãï¼ãã¹ããã¼ã¿ãæµãè¾¼ãã§ããã¾ãï¼åè¿°ã®acts_as_ludiaã®å°å ¥æ¹æ³ã«ãããã£ã¦Railsã¢ããªã±ã¼ã·ã§ã³ï¼ludiaï¼ãä½æãããæ¹ã¯Testã¨ããã¢ãã«ãããï¼Ludiaã®ã¤ã³ããã¯ã¹ãå¼µã£ã¦ããã¨æãã¾ãã®ã§ï¼ããã«ãã¼ã¿ãä½ã£ã¦ã¿ã¾ãï¼script/consoleãã以ä¸ã®ã³ã¼ã*1ãå®è¡ãã¦ãã ããï¼
Test.create(:col1=>"ãããããããããã®ãã¡",:col2=>"ãã®å£ºã¯ãããã®ã ") Test.create(:col1=>"ããããçã¾ããæ¡å¤ªé",:col2=>"ãã®å£·ã¯ãããã®ã ") Test.create(:col1=>"ããããé£ãã ãæ¨ãæºãã",:col2=>"ãã®å£ºã¯æªããã®ã ") Test.create(:col1=>"ããªããããã話ãèãããã®",:col2=>"ãã®å£·ã¯æªããã®ã ") Test.create(:col1=>"æ¨å¤ãããã®ç¼¶è©°ãéãã",:col2=>"ãã®å£·ã¯ãããã®ã ") Test.create(:col1=>"ã«ãã«ã¯ã«ãã«ãã¨ãããã",:col2=>"ãã®å£ºã¯ãããã®ã ")
find_fulltextãå©ç¨ããå ¨ææ¤ç´¢
acts_as_ludiaãå©ç¨ããã¨ActiveRecord::Baseã«find_fulltextã¨ããã¡ã½ããã追å ããã¾ãï¼
find_fulltext(query, options={})
ä¸è¨ã®ããã«find_fulltextã¡ã½ããã¯å¼æ°ãä¸ã¤ï¼ã¨findãªãã·ã§ã³ï¼ãã¨ãã¾ãï¼queryã«ã¯ããã·ã¥ã§æ¤ç´¢å¯¾è±¡åã¨æ¤ç´¢èªã渡ãã¾ãï¼
ä¾ãã°ï¼col1åã«ããããã¨ããèªãå«ãè¡ãæ¤ç´¢ããå ´åï¼ä»¥ä¸ã®ããã«å®è¡ãã¾ãï¼è¦ãããããã«ppãå©ç¨ãã¦ãã¾ãï¼ï¼
>> pp Test.find_fulltext(:col1 => "ãã") [#<Test:0xb768c880 @attributes= {"id"=>"1", "col1"=>"ãããããããããã®ãã¡", "col2"=>"ãã®å£ºã¯ãããã®ã "}>, #<Test:0xb768c650 @attributes= {"id"=>"5", "col1"=>"æ¨å¤ãããã®ç¼¶è©°ãéãã", "col2"=>"ãã®å£·ã¯ãããã®ã "}>, #<Test:0xb768c614 @attributes= {"id"=>"2", "col1"=>"ããããçã¾ããæ¡å¤ªé", "col2"=>"ãã®å£·ã¯ãããã®ã "}>] => nil
ã¾ãï¼ã¯ã¨ãªããã·ã¥ã®å¤ã«åè§ã®ç©ºç½ã§åºåã£ã¦èªãå ¥åããã¨ANDæ¤ç´¢ã«ãªãã¾ãï¼
>> pp Test.find_fulltext(:col1 => "ãã æ¡å¤ªé") [#<Test:0xb6c9d680 @attributes= {"id"=>"2", "col1"=>"ããããçã¾ããæ¡å¤ªé", "col2"=>"ãã®å£·ã¯ãããã®ã "}>] => nil
ããã«ï¼ã¯ã¨ãªããã·ã¥ã®å¤ã«é åã渡ãã¨é åã®åè¦ç´ ã®ORæ¤ç´¢ã«ãªãã¾ãï¼ä»¥ä¸ã§ã¯ANDæ¤ç´¢ã¨çµã¿åãããä¾ã示ãã¾ãï¼
>> pp Test.find_fulltext(:col2 => ["ã㮠壺", "æªã"]) [#<Test:0xb6c652e4 @attributes= {"id"=>"3", "col1"=>"ããããé£ãã ãæ¨ãæºãã", "col2"=>"ãã®å£ºã¯æªããã®ã "}>, #<Test:0xb6c652a8 @attributes= {"id"=>"4", "col1"=>"ããªããããã話ãèãããã®", "col2"=>"ãã®å£·ã¯æªããã®ã "}>] => nil
ããã«ï¼è¤æ°åã対象ã¨ããå ¨ææ¤ç´¢ãå¯è½ã§ãï¼ããã¯ï¼ã¯ã¨ãªããã·ã¥ã«ãã¼ã¨å¤ã追å ãã¾ãï¼ä»¥ä¸ã«col1ã«ãããããå«ã¿ï¼ãã¤col2ã«ã壺ããå«ãè¡ãæ¤ç´¢ããä¾ã示ãã¾ãï¼
>> pp Test.find_fulltext(:col1 => "ãã", :col2 => "壺") [#<Test:0xb6c5c554 @attributes= {"id"=>"1", "col1"=>"ãããããããããã®ãã¡", "col2"=>"ãã®å£ºã¯ãããã®ã "}>, #<Test:0xb6c5c518 @attributes= {"id"=>"3", "col1"=>"ããããé£ãã ãæ¨ãæºãã", "col2"=>"ãã®å£ºã¯æªããã®ã "}>] => nil
col1ã«ãããããå«ããï¼ã¾ãã¯col2ã«ã壺ããå«ãè¡ãæ¤ç´¢ãããã¨ãã§ãã¾ãï¼ãã®å ´åï¼ãªãã·ã§ã³ã«:all => trueãæå®ãã¾ãï¼
â»ã¯ã¨ãªé¨åãåä¸ã®ããã·ã¥ã¨ãããã{ã¨}ã§å²ãã§ãããã¨ã«æ³¨æãã¦ãã ããï¼
>> pp Test.find_fulltext({:col1 => "ãã", :col2 => "壺"}, :all => true) [#<Test:0xb6c4bd58 @attributes= {"id"=>"1", "col1"=>"ãããããããããã®ãã¡", "col2"=>"ãã®å£ºã¯ãããã®ã "}>, #<Test:0xb6c4bd1c @attributes= {"id"=>"2", "col1"=>"ããããçã¾ããæ¡å¤ªé", "col2"=>"ãã®å£·ã¯ãããã®ã "}>, #<Test:0xb6c4bce0 @attributes= {"id"=>"3", "col1"=>"ããããé£ãã ãæ¨ãæºãã", "col2"=>"ãã®å£ºã¯æªããã®ã "}>, #<Test:0xb6c4bca4 @attributes= {"id"=>"5", "col1"=>"æ¨å¤ãããã®ç¼¶è©°ãéãã", "col2"=>"ãã®å£·ã¯ãããã®ã "}>, #<Test:0xb6c4bc68 @attributes= {"id"=>"6", "col1"=>"ã«ãã«ã¯ã«ãã«ãã¨ãããã", "col2"=>"ãã®å£ºã¯ãããã®ã "}>] => nil
ã¹ãããããä¸ç·ã«åå¾ãã
find_fulltextã¡ã½ããã«ã¯æ¤ç´¢èªå¨è¾ºã®æç« ï¼ã¹ããããï¼ãåå¾ãããªãã·ã§ã³ãããã¾ãï¼
å©ç¨æ¹æ³ã¯å¼æ°ã«:snippet => trueã追å ãã¾ãï¼
ï¼çµæ§é大ãªï¼æ³¨æç¹
Ludiaã®ã¹ããããåå¾é¢æ°ã®ä»æ§(?)ã«ããï¼ã¹ããããåå¾ãå©ç¨ããå ´åã¯æ¤ç´¢ã§ããåãä¸ã¤ã«éå®ããã¾ãï¼:snippet => true ã®ããã©ã«ãåä½ï¼è¦ç´ï¼
åç´ã«find_fulltextã¡ã½ããã«:snippet => trueãªãã·ã§ã³ã追å ããå ´åï¼ä»¥ä¸ã®ã¹ãããããªãã·ã§ã³ã®defaultå¤ãé©ç¨ããã¾ãï¼- :length => 60
- ã¹ããããã®é·ã(byte)ãæå®ãã¾ãï¼default㯠60byte ã§ãï¼
- :decorations => ['<strong>', '</strong>']
- ã¹ããããå ã§æ¤ç´¢èªã®åå¾ã«è¿½å ããæåãããããæå®ãã¾ãï¼defaultã¯<strong>ã¿ã°ã§å²ã¿ã¾ãï¼
- :label => 'l_snippet'
- å¾ããããªãã¸ã§ã¯ãã«å¯¾ãã¦ã¹ããããç¨ã®ã©ãã«ãæå®ãã¾ãï¼defaultã¯"l_snippet"ã§ãï¼
>> pp Test.find_fulltext({:col1 => "æ¡å¤ªé"}, :snippet => true) [#<Test:0xb7643540 @attributes= {"l_snippet"=>"ããããçã¾ãã<strong>æ¡å¤ªé</strong>", "id"=>"2", "col1"=>"ããããçã¾ããæ¡å¤ªé", "col2"=>"ãã®å£·ã¯ãããã®ã "}>] => nil
:snippetã¸ãªãã·ã§ã³ã追å ãã
find_fulltextã¡ã½ããã®:snippetãªãã·ã§ã³ã«æ示çã«ã¹ãããããªãã·ã§ã³*2ãæå®ãããã¨ãã§ãã¾ãï¼ããã¯:snippetã«ããã·ã¥ã§æ¸¡ãã¾ãï¼ä»¥ä¸ã«å ¨é¨æå®ããä¾ã示ãã¾ãï¼>> pp Test.find_fulltext({:col1 => "æ¡å¤ªé"}, :snippet => {:length => 20, :decorations => ["<em>", "</em>"], :label => "other_ludia_snippet_label"}) [#<Test:0xb75b51f0 @attributes= {"other_ludia_snippet_label"=>"ã¾ãã<em>æ¡å¤ªé</em>", "id"=>"2", "col1"=>"ããããçã¾ããæ¡å¤ªé", "col2"=>"ãã®å£·ã¯ãããã®ã "}>] => nil
ã¨ããããï¼ä»ã®ã¨ãããããªæãã§ãï¼ãããããæ©è½è¿½å ãªã©ãã¦ããã¾ãï¼ãæè¦ã»ãè¦æãªã©ããã°æ°è»½ã«ã³ã¡ã³ããã¦ãã ããï¼
ãã¢ãã¼ã¸
ã¨ããããï¼ãããªãã®ãç°¡åã«ä½ãããã¨ãããã¨ã§ã¯ã¦ãå
¨ææ¤ç´¢ãä½ã£ã¦ã¿ã¾ããï¼
acts_as_ludia を使ってLudiaのデモWebアプリケーションを作りました - のほほん徒然
åèãã¼ã¸
ã½ã¼ã¹ã³ã¼ã
æ±ãã½ã¼ã¹ï¼acts_as_ludia-0.2.0æç¹ï¼ã§ããã¨ããããç½®ãã¦ããã¾ãã®ã§ï¼ä½¿ã£ã¦ã¿ããï¼ã¾ãã¯åèã«ãããï¼ã¨ããæ¹ã¯ã©ããï¼
#{RAILS_ROOT}/vendor/plugins/acts_as_ludia/init.rb
require File.dirname(__FILE__) + '/lib/ludia/acts_as_ludia' ActiveRecord::Base.send :include, Ludia::ActsAsLudia
#{RAILS_ROOT}/vendor/plugins/acts_as_ludia/lib/ludia/acts_as_ludia.rb
# ActsAsLudia module Ludia module ActsAsLudia def self.included(base) base.extend ClassMethods end module ClassMethods def acts_as_ludia(options = {}) return if self.included_modules.include?(Ludia::ActsAsLudia::InstanceMethods) send :include, Ludia::ActsAsLudia::InstanceMethods end end module InstanceMethods def self.included(base) base.extend SingletonMethods end module SingletonMethods # Model.find_fulltext({:col1 => ["hoge moge", "hoho"]}) # => SELECT * FROM models WHERE col1 @@ '*D+ hoge moge OR hoho'; # Model.find_fulltext({:col1 => "hoge", :col2 => ["moge", "hoho"]}, :all => true) # => SELECT * FROM models WHERE col1 @@ '*D+ hoge' OR col2 @@ '*D+ moge OR hoho'; # Model.find_fulltext({:col1 => "hoge moge"}, :snippet => true) # Model.find_fulltext({:col1 => "hoge moge"}, :snippet => {:length => 45, :decorations => ['<em>', '</em>']) def find_fulltext(query, options={}) raise ArgumentError, "wrong argument class" unless query.is_a? Hash raise ArgumentError, "one query is available, if use SNIPPET." if options[:snippet] && query.length > 1 conditions = [] query.each_pair do |column, string| string = string.join(' OR ') if string.is_a? Array conditions << %|#{column.to_s} @@ '*D+ #{string}'| end conjunction = options.delete(:all) ? ' OR ' : ' AND ' options[:conditions] = conditions.join(conjunction) option_select = snippet(query, options.delete(:snippet)) if options[:snippet] if option_select if options[:select] options[:select] += ", #{option_select}" else options[:select] = "*, #{option_select}" end end results = find(:all, options) end private def snippet(query, snippet_options) options = { :query => query.values.first.is_a?(Array) ? query.values.first.join(' ') : query.values.first, :length => 60, :column => query.keys.first.to_s, :escape => true, :decorations => ["<strong>", "</strong>"], :label => "l_snipett" } snippet_options = snippet_options.is_a?(Hash) ? snippet_options.reverse_merge(options) : options select_string = %|pgs2snippet1(1, #{snippet_options[:length]}, 1, | + %|'#{snippet_options[:decorations].first}', | + %|'#{snippet_options[:decorations][1]}', | + %|#{snippet_options[:escape] ? -1 : 0}, | + %|'#{snippet_options[:query]}', | + %|#{snippet_options[:column]}) | %|as #{snippet_options[:label]| end end end end end
*1:å 容ã¯READMEãåèã«é©å½ã«ä½ãã¾ãã
*2:ãªãã·ã§ã³ã«ã¹ãããããªãã·ã§ã³ãã»ã»ããããã