Full-text search sử dụng gem search_cop
Giới thiệu Về cơ bản thìfull-text search là một cách để tăng tốc độ tìm kiếm và chất lượng kết quả. Bạn có thể tìm hiểu thêm qua loạt bài viết sau: https://kipalog.com/posts/Full-Text-Search--Tu-Khai-Niem-den-Thuc-Tien--Phan-1. Trong bài viết này mình sẽ tập trung vào công dụng của full-text ...
Giới thiệu
Về cơ bản thìfull-text search là một cách để tăng tốc độ tìm kiếm và chất lượng kết quả. Bạn có thể tìm hiểu thêm qua loạt bài viết sau: https://kipalog.com/posts/Full-Text-Search--Tu-Khai-Niem-den-Thuc-Tien--Phan-1.
Trong bài viết này mình sẽ tập trung vào công dụng của full-text search bằng cách xây dựng một demo dựa trên framework Ruby On Rails và gem search_cop.
Về gem search_cop, nó là một thư viện hỗ trợ việc sử dụng full-text search cho ứng dụng Rails mà không cần quan tâm đến database sử dụng là gì (MySQL, PostgreSQL) hay bạn sẽ không cần phải sử dụng search server của bên thứ 3 (Elasticsearch, Solr). Khi sử dụng, ta chỉ việc viết những câu query string hoặc query dạng hash đơn giản và gem sẽ tự động sinh và tối ưu query tương ứng với database.
Xây dựng demo
Để bắt đầu, chúng ta thêm vào Gemfile các dòng sau
gem 'search_cop' gem "faker" //để tạo dữ liệu mẫu
Tiếp theo chúng ta tạo một model Article chính là đối tượng search bằng câu lệnh
rails generate model Article title:string content:string author:string
Câu lệnh trên sẽ sinh ra một file migration, thêm dòng sau
add_index :articles, [:title, :content, :author], type: :fulltext // đánh index tương ứng cho các trường
Tiếp theo là thực hiện fake dữ liệu
// tạo 10000 bản ghi có dữ liệu ngẫu nhiên require 'faker' 100000.times.each do Article.create!( title: Faker::Lorem.sentence, content: Faker::Lorem.paragraph, author: Faker::Lorem.word ) end
Cuối cùng chạy các lệnh sau để tạo cơ sở dữ liệu
rails db:migrate rails db:seed
Thêm scope search cho model
class Article < ApplicationRecord include SearchCop //để sử dụng các phương thức trong gem // khai báo scope search search_scope :search do attributes all: [:title, :content, :author] //khai báo tập các field muốn search options :all, type: :fulltext, default: true //định nghĩa chúng ta muốn sử dụng full-text search end end
Tạo một search controller
class SearchController < ApplicationController before_action :get_q, only: [:search_by_full_text, :search_by_like] def index;end def search_by_full_text @articles = Article.search(q) // gọi tới scope search ta định nghĩa ở trên @found = @articles.count // số bản ghi tìm được @all = Article.count // tổng số bản ghi render :search_result end def search_by_like @articles = Article.where("title LIKE ? OR content LIKE ? OR author LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%") // tìm kiếm bằng LIKE @found = @articles.count @all = Article.count render :search_result end private attr_reader :articles, :q def get_q @q = params[:q] end end
Định nghĩa các routes
Rails.application.routes.draw do get "/search", to: 'search#index' post "/search_by_full_text", to: 'search#search_by_full_text' post "/search_by_like", to: "search#search_by_like" root 'search#index' end
Tạo các view tương ứng
index.html.erb
<div class="form-group col-md-offset-4" style="margin-top: 120px; margin-bottom: 120px;"> <%= form_tag "/search_by_full_text", class: "form-inline" do %> <%= text_field_tag :q, nil, placeholder: 'Search Full-text', class: "form-control input-lg" %> <%= submit_tag :search, class: "btn btn-lg btn-primary" %> <% end %> </div> <div class="form-group col-md-offset-4"> <%= form_tag "/search_by_like", class: "form-inline" do %> <%= text_field_tag :q, nil, placeholder: 'Search LIKE', class: "form-control input-lg" %> <%= submit_tag :search, class: "btn btn-lg btn-primary" %> <% end %> </div>
searchresult.html.erb
<div class="container"> <h2><%= @found %> / <%= @all %></h2> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>TITLE</th> <th>CONTENT</th> <th>AUTHOR</th> </tr> </thead> <tbody> <% @articles.each do |article| %> <tr> <td><%= article.id %></td> <td><%= article.title %></td> <td><%= article.content %></td> <td><%= article.author %></td> </tr> <% end %> </tbody> </table> </div>
Chạy server và ta có kết quả như sau:
Thử submit từng form và so sánh kết quả. Với từ khóa là 'quis'
Kết quả sử dụng full-text search
Kết quả sử dụng LIKE search
Đầu tiên, ta thấy được số kết quả sử dụng full-text search ít hơn (25103 so với 35545 trong 117665 bản ghi), đó là do cơ chế tính điểm cho kết quả từ đó lọc ra được các kết quả phù hợp nhất. Cũng chính vì vậy mà thứ tự kết quả cũng đã được sắp xếp theo độ liên quan khác với kết quả của search LIKE cho kết quả theo thứ tự.
Tiếp đó là về hiệu năng
Câu truy vấn Article Load đầu tiên chính là query full-text search được sinh ra trong MySQL với thời gian thực hiện chỉ bằng một nửa so với query bằng LIKE.
Khi truy vấn với từ khóa mà số kết quả thỏa mãn ít (chỉ có 4 bản ghi) thì sự chêch lệch còn lớn hơn!!!
Qua bài viết, mình đã nêu ra ưu điểm vượt trội của full-text search trong trường hợp số lượng bản ghi lớn và chứa nhiều text, cùng với đó là cách sử dụng cơ bản của gem search_cop trong ứng dụng Rails. Để tìm hiểu sâu hơn bạn có thể tham khảo các tài liệu sau:
https://github.com/mrkamel/search_cop
https://kipalog.com/posts/Full-Text-Search--Tu-Khai-Niem-den-Thuc-Tien--Phan-1
Cảm ơn các bạn đã theo dõi