Edited at

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

More than 3 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に置き換わりました