LoginSignup
2
2

More than 5 years have passed since last update.

Rails + PostgreSQLでIndexを利用した中間一致検索する

Posted at

ショッピンサイトなどを開発していると中間一致で検索したくなりますよね。

でも、Elasticsearch入れるほどでも無いし...という場合の簡単な方法を紹介します。

試した環境は

  • Ubuntu 16.04
  • Ruby 2.3.3
  • Rails 5.0.0
  • PostgreSQL 9.5.5 (apt-get install)

です。

pg_trgmの有効化

まずはサクッとpg_trgmを有効にしましょう。PostgreSQLに接続するユーザはSUPERUSER権限を持つ必要があります。

なければ、

psql -U postgres -c 'ALTER USER USER_FROM_RAILS WITH SUPERUSER'

こんな感じで権限与えます。

Railsでpg_trgmを有効にするにはrails g migration EnablePgTrgmExtensionして、

db/migrate/yyyyMMddhhmmss_enable_pg_trgm_extension.rb
class Hoge < ActiveRecord::Migration[5.0]
  def change
    enable_extension 'pg_trgm'
  end
end

からの、rake db:migrateください。

インデックス作成

これもrails g migration AddIndexProductsOnNameして、

db/migrate/yyyyMMddhhmmss_add_index_to_product_on_name.rb
class AddIndexProductsOnName < ActiveRecord::Migration[5.0]
  def up
    add_index :products,
              'name gist_trgm_ops',
              using: :gist,
              name: :index_products_on_name
  end

  def down
    remove_index :products,
                 name: :index_products_on_name
  end
end

からの、rake db:migrateください。

中間一致検索してみる

Likeで検索するだけです。サンプルでは小文字大文字無視したいのでiLikeで検索します。rails cで試してみて下さい。

Product.where("name ilike ?", "%silk%")

like検索なんだからあたりまえ?そうですね、index使っているか確認しましょう。

Product.where("name ilike ?", "%silk%").explain
  Product Load (0.5ms)  SELECT "products".* FROM "products" WHERE (name ilike '%silk%')
=> EXPLAIN for: SELECT "products".* FROM "products" WHERE (name ilike '%silk%')            
                                        QUERY PLAN                                                   
------------------------------------------------------------------------------------------           
 Bitmap Heap Scan on products  (cost=4.17..12.63 rows=4 width=152)                              
   Recheck Cond: ((name)::text ~~* '%silk%'::text)                                                   
   ->  Bitmap Index Scan on index_products_on_name  (cost=0.00..4.17 rows=4 width=0)            
         Index Cond: ((name)::text ~~* '%silk%'::text)                                               
(4 rows)                                                                                             

Bitmap Index ScanなのでIndex検索できてますね!

注意

簡単にできそうと紹介してましたが、pg_trgmで英数字以外をIndexするには

のいずれかが必要です。

2
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2