113
112

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2014-01-08

##説明
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に置き換わりました

113
112
0

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
113
112

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?