Help us understand the problem. What is going on with this article?

速攻で検索機能を実装できるsearchkickを調べた(Ruby)

More than 5 years have passed since last update.

説明

searchkickとは検索エンジンの一つであるelasticsearchを簡単に扱えるようにしたGemです。これを使うことにより

  • 高速検索
  • 誤字訂正してくれる検索
  • 部分合致検索 -自動補完

etc…

など様々な検索に関する機能を実装することが出来ます。
今回はこのsearchkickを使うために公式ドキュメントを和訳したのでそれを記しときます。

以下ドキュメントの和訳です

Searchkickとは

searchkickとはユーザーが検索をするときの方法です。これを使うことによりより多くの人が簡単により良く検索結果を得ることが出来るようになります。

Searchkickを利用する機会

  • 制御 -tomatoesをtomatoで合致するようにする
  • 固有のもの ―jalapenoがjalapeñoと合致するようにする
  • 余分な余白 - dishwasher が dish washer に合致するようにする
  • 誤字 -  zuchini が zucchini に合致するようにする
  • 習慣的異名 - qtip が cotton swab に合致する

利点

  • SQLの様にqueryを書けるので新しくqueryの文法を学習する必要がありません
  • システムを休止することなくindexをつくることが出来ます - 簡単に互いのUserを専用のものとして扱うことができます
  • 自動補完してくれます
  • 意味の提案をしてくれます
  • ActiveRecord や Mongoid と一緒に使うことができます

使用方法

homebrewを使いelasticsearchをインストールします

$ brew install elasticsearch

Gemfileに以下を追記します

gem
gem "searchkick"

検索を使いたいmodelにelasticsearchを追加します

model
class Product < ActiveRecord::Base
  searchkick #追記
end

indexを追加する場合は以下のようにします

Product.reindex

queryは以下のように書きます

products = Product.search "2% Milk"
products.each do |product|
  puts product.name
end

Queryについて

queryはSQLの様に書きます

Product.search "2% Milk", where: {in_stock: true}, limit: 10, offset: 50

具体的なfieldを検索する

fields: [:name, :brand]

whereの使い方

where: {
  expires_at: {gt: Time.now}, # lt, gte, lte also available
  orders_count: 1..10,        # equivalent to {gte: 1, lte: 10}
  aisle_id: [25, 30],         # in
  store_id: {not: 2},         # not
  aisle_id: {not: [25, 30]},  # not in
  or: [
    [{in_stock: true}, {backordered: true}]
  ]
}

Limit / offset の使い方

limit: 20, offset: 40

Boost by a field (フィールドによって押し上げる?)

boost: "orders_count" # give popular documents a little boost

ページ付け

ページ付けを正しく適用させる

# controller
@products = Product.search "milk", page: params[:page], per_page: 20

# view
<%= paginate @products %>

部分合致検索

defaultの設定では完全一致となっています

Product.search "fresh honey" # fresh AND honey

これを変えて使うには

Product.search "fresh honey", partial: true # fresh OR honey

*Synonyms

(*一つの実体に複数の名前や値などが割り当てられる機能や状態を指すこと)

class Product < ActiveRecord::Base
  searchkick synonyms: [["scallion", "green onion"], ["qtip", "cotton swab"]]
end

(注)必ずsynonymsを変更した後Product.reindexを呼んでください

誤字

Searchkickではdefaultで誤字の修正を取り扱っています。この機能をOFFにしたい場合は以下のようにします

Product.search "zuchini", misspellings: false # no zucchini

また、edit distanceの範囲を選択することが出来ます

Product.search "zucini", misspellings: {distance: 2} # zucchini

indexを作る

どのデータをindex化するかは search_data というメソッドを使って決めることができます。このメソッドを定義したあとに Product.reindex を呼んでください。

model
class Product < ActiveRecord::Base
  def search_data
    as_json only: [:name, :active]
    # or equivalently
    {
      name: name,
      active: active
    }
  end
end

Searchkickは find_in_batches を使って情報を付与することができます。associationを求める場合は search_import というスコープを使ってください。

class Product < ActiveRecord::Base
  scope :search_import, includes(:searches)
end

Reindexをする 又は Reindexしない 

Reindexが必要なとき

  • searchkickをインストールするとき、アップグレードするとき
  • search_data メソッド変更したとき
  • searchkick メソッドを変更したとき

Reindexが必要ないとき

  • アプリを開始するとき
  • データが追加されたり、変更されたり、削除された場合(自動的に同期されます)

より良くデータを保ち、受け取る

Searchkickは具体的なデータからuserを探すことが出来ます。もしuserがBemとjerryの大きな猿がカートを引くアイスクリームを探そうとしたら、それは簡単に見つかるでしょう。

この手順では会話の軌跡と距離から定義されます。このデータベースは少ない容量ですが他のデータベースよりも自由に感じられるでしょう

class Search < ActiveRecord::Base
  belongs_to :product
  # fields: id, query, searched_at, converted_at, product_id
end

search queryを整理する必要はありません。searchkickは自動的にappleとAPPLESを同じものとして扱ってくれます

次はindexを改変しましょう。使用しているバージョンを 0.2.0 と明示しなくてはなりません((・ ・?)よくわかんない)

class Product < ActiveRecord::Base
  has_many :searches

  searchkick conversions: "conversions" # name of field

  def search_data
    {
      name: name,
      conversions: searches.group("query").count
      # {"ice cream" => 234, "chocolate" => 67, "cream" => 2}
    }
  end
end

毎日最新のものに変更するためにReindexとcron jobをset upしなくてはなりません

rake searchkick:reindex CLASS=Product

専用の結果

userの結果ごとに並び替えることができます。例えば他の結果を出す前にユーザーが以前購入した商品を表示することができます。

class Product < ActiveRecord::Base
  searchkick personalize: "user_ids"

  def search_data
    {
      name: name,
      user_ids: orders.pluck(:user_id) # boost this product for these users
      # [4, 8, 15, 16, 23, 42]
    }
  end
end

Reindexとsearchはこのようにします

Product.search "milk", user_id: 8

自動補完

autocompleteはユーザーが何を打つのか予測して、より簡単で早い検索経験を作ります。

最初にどのfieldを主要にするか明示します。これはindexの数が増えるに連れて著しい意味を持ちます。しかしこれは酷く簡単なので心配はいりません。

class City < ActiveRecord::Base
  searchkick autocomplete: ["name"]
end

Reindexとsearchはこのようにします

City.search "san fr", autocomplete: true

典型的な typeahead.jsや jQuery UIといったjqueryライブラリを使うことが出来ます。

どのようにRailsで動かすのか

最初にcontrollerにアクションを追加します

# app/controllers/cities_controller.rb
class CitiesController < ApplicationController

  def autocomplete
    render json: City.search(params[:query], autocomplete: true, limit: 10).map(&:name)
  end

end

このときにViewにsearch boxとjavascriptのコードを追記します

<input type="text" id="query" name="query" />

<script src="jquery.js"></script>
<script src="typeahead.js"></script>
<script>
  $("#query").typeahead({
    name: "city",
    remote: "/cities/autocomplete?query=%QUERY"
  });
</script>

Facets(小枠)

Facetsは検索結果の集計を表示します
facets

products = Product.search "chuck taylor", facets: [:product_type, :gender, :brand]
p products.facets

さらに高度な集計結果を表示したい場合は

Product.search "2% Milk", facets: {store_id: {where: {in_stock: true}, limit: 10}}

強調

queryによる検索結果の強調をすることができます

bands = Band.search "cinema", fields: [:name], highlight: true

(注意)fieldsオプションが必須です

fieldsを用いて強調をみるには

bands.with_details.each do |band, details|
  puts details[:highlight][:name] # "Two Door <em>Cinema</em> Club"
end

使うタグを変える場合は

Band.search "cinema", fields: [:name], highlight: {tag: "<strong>"}

類似検索

似た要素を探すことができます

product = Product.first
product.similar(fields: ["name"])

土地検索

(注意) 0.3.0以前のバージョンではlocationが不正確です。アップグレードしてReindexしてください

class City < ActiveRecord::Base
  searchkick locations: ["location"]

  def search_data
    attributes.merge location: [latitude, longitude]
  end
end

Reindexとsearchのやりかた

City.search "san", where: {location: {near: [37, -114], within: "100mi"}} # or 160km

Boxで仕分ける

City.search "san", where: {location: {top_left: [38, -123], bottom_right: [37, -122]}}

Deployment

SearchkickはENV["ELASTICSEARCH_URL"]をサーバーとして使います。ただしコレが無い場合は
http://localhost:9200
がdefaultとして使用されます

Heroku

SearchboxBonsaiFoundを使用してください

# SearchBox
heroku addons:add searchbox:starter
heroku config:add ELASTICSEARCH_URL=`heroku config:get SEARCHBOX_URL`

# Bonsai
heroku addons:add bonsai
heroku config:add ELASTICSEARCH_URL=`heroku config:get BONSAI_URL`

# Found
heroku addons:add foundelasticsearch
heroku config:add ELASTICSEARCH_URL=`heroku config:get FOUNDELASTICSEARCH_URL`

このときにdeployとReindexをしてください

heroku run rake searchkick:reindex CLASS=Product

その他

作成時にはconfig/initializers/elasticsearch.rbを初期化してください

ENV["ELASTICSEARCH_URL"] = "http://username:password@api.searchbox.io"

このときにdeployとreindexをして下さい

rake searchkick:reindex CLASS=Product

継承

searchkickは一つのテーブルを受け続ことが出来ます

class Dog < Animal
end

このとき親と子の要素は両方Reindexしてください

Animal.reindex
Dog.reindex # equivalent

検索するためには

Animal.search "*" # all animals
Dog.search "*"    # just dogs

参照

Searchkickは0.90.0より高いelasticsearchを必要とします
ReindexはRecordの一つです

product = Product.find 10
product.reindex

古いindexの削除は

Product.clean_indices

異なったindex名を使うには

class Product < ActiveRecord::Base
  searchkick index_name: "products_v2"
end

Prefixはindexの名前です

class Product < ActiveRecord::Base
  searchkick index_prefix: "datakick"
end

callback機能をOFFにするには

class Product < ActiveRecord::Base
  searchkick callbacks: false
end

associationの利用を望むなら

Product.search "milk", include: [:brand, :stores]

modelのロードをOFFにするには

Product.search "milk", load: false

特徴を消すには

class Product < ActiveRecord::Base
  # A will not match Ä
  searchkick special_characters: false
end

すべてのモデルをReindexする(Railsだけしか使えません)

rake searchkick:reindex:all

Tireから移動するには

1,searchメソッドをtire.searchに変えて、既存のindexを呼べるようにします

Product.search "fruit"

置き換えるには

Product.tire.search "fruit", index: "products"

2.tire mapping wをsearchkickメソッドに置き換えます

class Product < ActiveRecord::Base
  searchkick
end

deployとreindexをします

rake searchkick:reindex CLASS=Product # or Product.reindex in the console

4,これでsearch calls wはsearchkick callsに置き換わりました

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした