Sử dụng gem pg_search để xây dựng chức năng tìm kiếm trong PostgreSQL
Bài đăng này đã không được cập nhật trong 3 năm
Tìm kiếm là một trong những chức năng phổ biến nhất mà bất kỳ trang web nào cũng được tích hợp. Có rất nhiều giải pháp đã được đưa ra để giải bài toán tìm kiếm trong ứng dụng của bạn.
Sau thời gian dài làm việc với MySQL
, gần đây mình làm quen với PostgreSQL
để đổi gió và mình thấy có gem pg_search
có khả năng hỗ trợ tìm kiếm rất hay trên PostgreSQL
.
Trong bài viết này mình sẽ xây dựng một ứng dụng đơn giản và sử dụng gem pg_search
để làm chức năng tìm kiếm cho ứng dụng đó.
Khởi tạo
Môi trường làm việc :
- Rails 5.0.1
- PostgreSQL (nếu chưa cài đặt thì bạn có thể xem hướng dẫn)
Hãy cùng thực hiện nhanh các bước chuẩn bị này vì nó rất quen thuộc.
Trước tiên, tạo một ứng dụng Rails mới sử dụng tìm kiếm trong Postgres
:
rails new autocomplete --database=postgresql
Chuẩn bị cấu hình trong file config/database.yml
để kết nối tới DB.
development:
adapter: postgresql
database: autocomplete
host: localhost
user: username
password: password
Tạo một bảng đơn giản để tiến hành việc tìm kiếm với 2 trường name
, surname
rails g model User name:string surname:string
rails db:migrate
Tạo một vài dữ liệu mẫu cho bảng users
, tốt nhất là nên tạo ra các use có trường name
là phân biệt. Bạn có thể dùng gem Faker
tạo ra file seed đơn giản như sau :
50.times do
User.create({name: Faker::Name.first_name, surname: Faker::Name.last_name})
end
Cuối cùng, tạo controller, view và trỏ router đến controller
users_controller.rb
class UsersController < ApplicationController
def index
@users = User.all
end
end
views/users/index.html.erb
<ul>
<% @users.each do |user| %>
<li><%= user.name %> <%= user.surname %></li>
</ul>
config/routes.rb
resources :users, only: [:index]
root to: "users#index"
Như vậy là chúng ta đã chuẩn bị xong môi trường. Hãy tiến hành thêm chức năng tìm kiếm vào nhé.
Tìm kiếm users
Trước tiên, hãy thêm một box search vào view như sau :
<%= form_tag users_path, method: :get do %>
<%= text_field_tag "term", params[:term], placeholder: "Enter user's name or surname" %>
<%= submit_tag "Search!"" %>
<% end
Bây giờ là lúc cần cài đặt gem pg_search
cho ứng dụng.
Gem pg_search
hỗ trợ chế độ pg_search_scope
(tương tự như scope trong ActiveRecord) với cú pháp pg_search_scope SCOPE_NAME, against: OPTIONS
trong đó :
OPTIONS
là danh sách các trường mà chúng ta cần tìm kiếm
Áp dụng vào model User
cho việc tìm kiếm với cả 2 trường name
và surname
như sau :
# ...
include PgSearch
pg_search_scope :search_by_full_name, against: [:name, :surname]
Sửa lại action index trong controller để sử dụng scope vừa khai báo như sau :
def index
if params[:term]
@users = User.search_by_full_name(params[:term])
else
@users = User.all
end
end
Đến đây thì bạn hãy chạy server lên để xem kết quả đầu tiên nhé.
Tìm kiếm với options nâng cao
Chức năng tìm kiếm của chúng ta đã làm việc, tuy nhiên hiện tại, nó chỉ đơn thuần là so sánh =
giữa text mình truyền vào box search với trường name
hoặc surname
của các bản ghi trong BD.
Hãy nâng cấp khả năng tìm kiếm cho ứng dụng bằng việc sử dụng các kỹ thuật tìm kiếm mà pg_search
cung cấp :
- tsearch - full text search, được tích hợp vào
PostgreSQL
- trigram - yêu cầu phải có phần mở rộng trigram
- dmetaphone - yêu cầu phái có phần mở rộng fuzzystrmatch
Sẽ được khai báo trong param using
của scope tìm kiếm đã được định nghĩa ở trên. Trong bài viết này mình chỉ trình bày về tsearch
, hai kỹ thuật còn lại bạn có thể tham khảo thêm tại đây
PostgreSQL
tích hợp full text search với các hỗ trợ : trọng số, tìm kiếm tiền tố,...
- Trọng số
Với các trường cho phép tìm kiếm thì sẽ được gán các trọng số A
> B
> C
> D
, ưu tiên theo thứ tự trên. Trọng số có thể được khai báo trong key against
dưới dạng Hash
, Array
như sau :
class Article < ActiveRecord::Base
include PgSearch
pg_search_scope :search_full_text1, against: {
title: "A",
subtitle: "B",
content: "C"
}
pg_search_scope :search_full_text2, against: [
[:title, "A"],
[:subtitle, "B"],
[:content, "C"]
]
pg_search_scope :search_full_text3, against: [
[:title, "A"],
{subtitle: "B"},
:content
]
end
Như ở trên thì thứ tự ưu tiên khi tìm kiếm sẽ là title
> subtitle
> content
- :prefix
Chỉ hỗ trợ cho PostgreSQL
8.4 trở lên.
Mặc định thì full text search sẽ tìm kiếm trong toàn bộ từ, nếu bạn chỉ muốn tìm kiếm một phần thì bạn sẽ thêm key prefix: true
vào trong tsearch
như sau :
class Superhero < ActiveRecord::Base
include PgSearch
pg_search_scope :whose_name_starts_with,
against: :name,
using {
tsearch: { prefix: true }
}
end
Khi đó ta sẽ tìm kiếm được như sau :
batman = Superhero.create name: "Batman"
batgirl = Superhero.create name: "Batgirl"
robin = Superhero.create name: "Robin"
Superhero.whose_name_starts_with("Bat") # => [batman, batgirl]
- :negation
Mặc định thì full text search sẽ tìm kiếm với tất cả các cụm từ đầu vào, nếu muốn tìm kiếm loại trừ một số cụm từ thì bạn có thể khai báo negation: true
bên trong tsearch
. Khi tìm kiếm thì đầu vào của bạn chỉ cần thêm ký tự !
trước cụm từ bạn muốn loại bỏ khi tìm kiếm :
class Animal < ActiveRecord::Base
include PgSearch
pg_search_scope :with_name_matching,
against: :name,
using: {
tsearch: { negation: true }
}
end
one_fish = Animal.create(:name => "one fish")
two_fish = Animal.create(:name => "two fish")
red_fish = Animal.create(:name => "red fish")
blue_fish = Animal.create(:name => "blue fish")
Animal.with_name_matching("fish !red !blue") # => [one_fish, two_fish]
Ngoài ra còn có một số option khác cũng khá hay nữa như dictionary
, normalization
, highlight
... mà việc kết hợp sử dụng linh hoạt giữa chúng sẽ giúp ta xây dựng một ứng dụng tìm kiếm rất hiệu quả.
Tham khảo
Hi vọng bài viết sẽ giúp bạn có thêm lựa chọn để xây xựng tính năng tìm kiếm cho ứng dụng của mình.
Cám ơn bạn đã theo dõi bài viết
tribeo.
All rights reserved