Rails で Groonga をいい感じに扱える ActiveGroonga というものがあるようなので,2014-12-19 に出た Rails4.2 で試してみた.
2014-12-23 18:18:58 +0900 コメントを受けて内容を修正.ActiveGroonga4.2では全て意図通りに動作する
参考にしたサイト
Rails プロジェクトを作る
名前は active_record-playground とした.
ActiveRecord は使わないので --skip-active-record する.
$ rails new active_groonga_playground --skip-active-record
ActiveGroonga をインストールする
Gemfile に
# Gemfile
gem "activegroonga", require: "active_groonga"
を追加する.
次に
$ bin/bundle insall
する.
ActiveGroonga のライブラリを Rails 起動時に読み込む
# config/application.rb
# require "active_record/railtie"
require "active_groonga/railtie"
Groonga データベースを作成する
$ bin/rake groonga:create
User モデルを作成する
$ bin/rails generate model user name:short_text
$ bin/rake groonga:migrate
指定可能なカラム名は Schema::TableDefinition に書いてあるようだ.
Tweet モデルを作成する
$ bin/rails generate model tweet content:short_text user:reference
$ bin/rake groonga:migrate
検索用テーブルを作成する
$ bin/rails generate migration CreateFullTextSearchIndex
生成された migration ファイルを編集する
# db/groonga/migrate/20141221113954_create_full_text_search_index.rb
class CreateFullTextSearchIndex < ActiveGroonga::Migration
def up
create_table("bigram",
type: :patricia_trie,
key_type: "ShortText",
default_tokenizer: "TokenBigram",
key_normalize: true) do |table|
table.index("tweets.content")
end
end
def down
remove_table("bigram")
end
end
create_table に渡すオプションは Schema.create_table を参考のこと.
schema を定義したら migrate する.
$ bin/rake groonga:migrate
使ってみる
$ bin/rails console
Loading development environment (Rails 4.2.0)
irb(main):001:0> User.create(name: "北 太郎")
=> #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>
irb(main):002:0> User.create(name: "南 花子")
=> #<User id: 2, created_at: 2014-12-23 18:08:22 +0900, name: "南 花子", updated_at: 2014-12-23 18:08:22 +0900>
irb(main):003:0> Tweet.create(user: 1, content: "北風を吹かせます")
=> #<Tweet id: 1, content: "北風を吹かせます", created_at: 2014-12-23 18:08:32 +0900, updated_at: 2014-12-23 18:08:32 +0900, user: #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>>
irb(main):004:0> Tweet.create(user: 1, content: "冷たい空気を送ります")
=> #<Tweet id: 2, content: "冷たい空気を送ります", created_at: 2014-12-23 18:08:40 +0900, updated_at: 2014-12-23 18:08:40 +0900, user: #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>>
irb(main):005:0> Tweet.create(user: 2, content: "南風を吹かせます")
=> #<Tweet id: 3, content: "南風を吹かせます", created_at: 2014-12-23 18:08:47 +0900, updated_at: 2014-12-23 18:08:47 +0900, user: #<User id: 2, created_at: 2014-12-23 18:08:22 +0900, name: "南 花子", updated_at: 2014-12-23 18:08:22 +0900>>
irb(main):006:0> Tweet.create(user: 1, content: "暖かい空気を送ります")
=> #<Tweet id: 4, content: "暖かい空気を送ります", created_at: 2014-12-23 18:08:54 +0900, updated_at: 2014-12-23 18:08:54 +0900, user: #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>>
意図通りに動いていそうだ.
検索してみよう.検索は条件をブロックで指定する.
irb(main):007:0> set = Tweet.select { |record| record.content =~ "空気" }
=> #<ActiveGroonga::ResultSet:0x007ffec6ac7958 @records=#<Groonga::Hash id: <2147483649>, name: (anonymous), path: (temporary), domain: <tweets>, range: (nil), flags: <WITH_SUBREC>, size: <2>, encoding: <:utf8>, default_tokenizer: (nil), token_filters: [], normalizer: (nil)>, @klass=Tweet(content: ShortText, created_at: Time, updated_at: Time, user: users), @groups={}, @expression=#<Groonga::Expression #<expr
vars:{
$1:#<record:no_key:tweets id:0(nonexistent)>
},
codes:{
0:<push(), modify:1, value:#<record:no_key:tweets id:0(nonexistent)>>,
1:<get_value(), modify:2, value:#<column:var_size tweets.content range:ShortText type:scalar compress:none>>,
2:<push(), modify:0, value:"空気">,
3:<match(), modify:0, value:(NULL)>
}
>>, @n_records=2, @default_sort_keys=nil, @default_limit=nil, @n_key_nested=1>
irb(main):008:0> set.each { |e| p e }
#<Tweet id: 2, content: "冷たい空気を送ります", created_at: 2014-12-23 18:08:40 +0900, updated_at: 2014-12-23 18:08:40 +0900, user: #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>>
#<Tweet id: 4, content: "暖かい空気を送ります", created_at: 2014-12-23 18:08:54 +0900, updated_at: 2014-12-23 18:08:54 +0900, user: #<User id: 1, created_at: 2014-12-23 18:08:07 +0900, name: "北 太郎", updated_at: 2014-12-23 18:08:07 +0900>>
=> nil
うむ.うまくできている.