Post

Full text search with Searchkick

Getting Started

ติดตั้ง Elasticsearch หรือ OpenSearch สำหรับคนที่ใช้ macOS สามารถติดตั้งโดยใช้ brew ได้เลย:

1
2
3
4
5
brew install elastic/tap/elasticsearch-full
brew services start elasticsearch-full
# or
brew install opensearch
brew services start opensearch

จากนั้นเพิ่มบรรทัดเหล่านี้เข้าไปใน Gemfile:

1
2
3
4
gem "searchkick"

gem "elasticsearch"   # select one
gem "opensearch-ruby" # select one

และเพิ่ม searchkick เข้าไปใน model ที่ต้องการจะค้นหา:

1
2
3
class Product < ActiveRecord::Base
  searchkick
end

เข้าไปอ่าน Searchkick เพิ่มเติม ดูวิธีการใช้งานและ feature ที่มีให้ใช้

Demo

ต่อไปเราจะไปลองทำ demo ง่าย ๆ กัน:

1
rails new store

จากนั้นก็สร้างโมเดล product ซึ่งขอลัด ๆ ใช้ scaffold ไปก่อน:

1
2
3
rails g scaffold Product name detail:text
rails db:migrate
rails s

สร้างตัวอย่างข้อมูลใน:

1
Product.create(name: '', detail: '')

จากนั้นก็รันคำสั่งเพื่อเพิ่มข้อมูลเข้าไป:

1
rails db:seed

เปิด http://localhost:3000/products:

16988928511916

หลังจากนี้จะ implement search ให้กับ Product ก่อนอื่นก็เพิ่ม routes search:

1
2
3
4
5
6
7
Rails.application.routes.draw do
  resources :products do
    collection do
      get 'search'
    end
  end
end

เพิ่ม form การ search:

1
2
3
4
<%= form_tag search_products_path, method: :get do %>
  <%= text_field_tag :q %>
  <%= submit_tag :search %>
<% end %>

จะได้ form มาแบบนี้:

16988929644838

แล้วก็ไป implement search method:

1
2
3
4
5
6
7
8
class ProductsController < ApplicationController
  ...
  def search
    @products = Product.search(params[:q])
    render :index
  end
  ...
end

ตอนนี้ทำง่าย ๆ ไปก่อน แล้วไปลองดูผลงานกัน:

16988930135463

มาถึงขั้นนี้แล้วก็เหมือนว่าทุกอย่างจะใช้ได้ดี ที่นี้ลองใส่ข้อมูลที่เป็นภาษาไทยลงไป:

16988931025125

แล้วก็ลองค้นหาดู ก็จะเห็นว่าได้ผลออกมา:

16988931625161

เกือบดีละ ที่นี้ลองค้นหาแค่บางส่วนของคำ:

16988932201872

หาไม่เจอ 😓 มันเป็นปัญหาอย่างเดียวกับที่เคยเจอตอนใช้ Thinking Sphinx เป็นเรื่องการตัดคำภาษาไทยอีกแล้ว

อัพเดท 2020: จากนั้นแก้ไข searchkick ใน model โดยการเพิ่ม mapping สำหรับภาษาไทยเข้าไป ซึ่งจะต้องกำหนดว่าจะให้ค้นหาใน column ไหนได้บ้าง อย่างในตัวอย่างคือให้ค้นหาที่ name และ detail ต่างจากเมื่อก่อนที่กำหนดแต่ teken เป็น thai ก็พอ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Product < ApplicationRecord
  searchkick merge_mappings: true,
             settings: {
               analysis: {
                 analyzer: {
                   thai_analyzer: {
                     tokenizer: 'thai'
                   }
                 }
               }
             },
             mappings: {
               properties: {
                 name: {
                   type: 'keyword',
                   fields: {
                     analyzed: {
                       type: 'text',
                       analyzer: 'thai_analyzer'
                     }
                   }
                 },
                 detail: {
                   type: 'keyword',
                   fields: {
                     analyzed: {
                       type: 'text',
                       analyzer: 'thai_analyzer'
                     }
                   }
                 }
               }
             }
end

จากนั้น reindex อีกครั้ง:

1
rails searchkick:reindex CLASS=Product

ลองค้นหาผลลัพท์ดู:

16988932763830

เป็นอันเรียบร้อย ทำให้ค้นการแบบตัดคำภาษาไทยได้แล้ว 😉

Conclusion

Elasticsearch เป็น search engine ที่ดี และ Searchkick ก็ทำให้มันใช้ง่าย ๆ กับ rails model และตอนนี้ก็หาวิธีการให้ค้นหาภาษาไทยได้อย่างมีประสิทธิภาพได้แล้ว เพราะงั้นงานต่อไปได้ใช้อย่างแน่นอน

อัพเดท 2020: Elasticsearch ไม่สามารถค้นหาภาษาไทยที่เป็นทับศัพท์ได้ น่าจะเพราะการตัดคำนั่นแหละ หากจะเลือกใช้ก็อย่าลืมดูเรื่องนี้ด้วย

This post is licensed under CC BY 4.0 by the author.