Tính năng tìm kiếm và Autocomplete trong Rails
Bài đăng này đã không được cập nhật trong 7 năm
Tìm kiếm là một trong những tính năng phổ biến nhất được tìm thấy trên hầu như bất kỳ trang web nào. Có rất nhiều giải pháp dễ dàng cho phép kết hợp việc tìm kiếm vào ứng dụng của bạn, nhưng trong bài này tôi sẽ thảo luận về tìm kiếm trong các ứng dụng Rails được cung cấp bởi gem pg_search
. Trên hết, tôi sẽ chỉ cho bạn cách thêm tính năng autocomplete
với sự trợ giúp của plugin select2
.
Tôi sẽ có 3 ví dụ về việc sử dụng tính năng tìm kiếm và autocomplete trong các ứng dụng Rails. Cụ thể, bài viết này đề cập đến:
- xây dựng một tính năng tìm kiếm cơ bản
- thảo luận các tùy chọn bổ sung được hỗ trợ bởi pg_search
- xây dựng tính năng autocomplete để tự động hiển thị tên người dùng
Chuẩn bị
Đầu tiên tạo một ứng dụng Rails mới. Tôi sẽ sử dụng Rails 5.0.1, nhưng hầu hết các khái niệm được giải thích trong bài viết này đều áp dụng tốt với các phiên bản cũ hơn. Miễn là chúng ta sẽ sử dụng tìm kiếm của Postgres, ứng dụng cần được khởi tạo với cơ sở dữ liệu PostgreSQL:
rails new Autocomplete --database=postgresql
Tạo một cơ sở dữ liệu PG mới và thiết lập config/database.yml
. Để loại trừ username và password trong Postgres khỏi version hệ thống kiểm soát, tôi sử dụng gem dotenv-rails:
Gemfile
# ...
group :development do
gem 'dotenv-rails'
end
Để cài đặt nó tôi chạy câu lệnh:
$ bundle install
và tạo một file trong folder chính của ứng dụng:
.env
PG_USER: 'user'
PG_PASS: 'password'
Loại trừ file này khỏi sự điều khiển:
.gitignore
.env
File database sẽ thiết lập như sau:
config/database.yml
development:
adapter: postgresql
database: autocomplete
host: localhost
user: < %= ENV['PG_USER'] %>
password: < %= ENV['PG_PASS'] %>
Bây giờ chúng ta tạo một bảng dữ liệu và thêm vào dữ liệu mẫu. Ở đây, tôi chỉ đơn giản sẽ tạo ra một bảng users với name và surname:
$ rails g model User name:string surname:string
$ rails db:migrate
Các mẫu user phải có tên riêng biệt để chúng tôi có thể test tính năng tìm kiếm. Vì vậy, tôi sẽ sử dụng gem Faker :
Gemfile
# ...
group :development do
gem 'faker'
end
Cài gem:
$ bundle install
end
Chỉnh sửa file seeds.rb để tạo 50 user với name và surname ngẫu nhiên:
db/seeds.rb
50.times do
User.create({name: Faker::Name.first_name,
surname: Faker::Name.last_name})
end
Chạy script:
$ rails db:seed
Cuối cùng, thêm một route root, controller với action tương ứng và view. Hiện tại, nó sẽ hiển thị tất cả user:
config/routes.rb
# ...
resources :users, only: [:index]
root to: 'users#index'
users_controller.rb
class UsersController < ApplicationController
def index
@users = User.all
end
end
views/users/index.html.erb
<ul>
< %= render @users %>
</ul>
views/users/_user.html.erb
<li>
< %= user.name %> < %= user.surname %>
</li>
Mọi việc chuẩn bị đã hoàn tất! Bây giờ chúng ta có thể tiến hành phần tiếp theo và thêm một tính năng tìm kiếm vào ứng dụng.
Tìm kiếm User
Những gì tôi muốn làm là hiển thị một trường tìm kiếm ở trên cùng của trang chủ nói rằng: "Enter user’s name or surname" với một nút "Search!". Khi form được gửi, chỉ những user phù hợp với cụm từ được nhập sẽ được hiển thị.
Bắt đầu với form:
views/users/index.html.erb
< %= 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ờ chúng ta cần lấy cụm từ được nhập và thực hiện tìm kiếm. Đó là khi gem pg_search xuất hiện:
Gemfile
# ...
gem 'pg_search'
Đừng quên cài đặt:
$ bundle install
Pg_search
hỗ trợ 2 mode: multisearchable
và pg_search_scope
. Trong bài viết này, tôi sẽ sử dụng tùy chọn thứ 2 (multisearchable hỗ trợ tương tự, nhưng cho phép tìm kiếm toàn cầu được tạo trên nhiều model). Ý tưởng khá đơn giản: chúng ta tạo một phạm vi tìm kiếm. Nó có một tên và một loạt các lựa chọn. Trước khi làm điều đó, module PgSearch phải được include trong model:
models/user.rb
# ...
include PgSearch
pg_search_scope :search_by_full_name, against: [:name, :surname]
Trong ví dụ này, cột name và surname sẽ được sử dụng để tìm kiếm.
Bây giờ tuỳ chỉnh action trong controller sử dụng phạm vi tìm kiếm mới`
users_controller.rb
# ...
if params[:term]
@users = User.search_by_full_name(params[:term])
else
@users = User.all
end
Bây giờ bạn có thể khởi động máy chủ, truy cập trang chủ và nhập name hoặc surname của một số user và mọi thứ cần hoạt động bình thường.
Bổ sung các tuỳ chọn
Việc tìm kiếm đã làm việc, nhưng nó không được thuận tiện. Ví dụ: nếu tôi chỉ nhập một phần của name hoặc surname, nó sẽ không trả lại bất kỳ kết quả nào. Điều này có thể dễ dàng sửa chữa bằng cách cung cấp các thông số bổ sung.
Trước hết, bạn cần chọn một kỹ thuật tìm kiếm để sử dụng. Một là mặc định tìm kiếm full text của Postgres, mà tôi sẽ sử dụng trong bài báo này. Hai loại khác là tìm kiếm kiểu trigram và metaphone, yêu cầu phải cài đặt phần mở rộng PG. Để thiết lập kỹ thuật, sử dụng tuỳ chọn :using
:
pg_search_scope :search_by_full_name, against: [:name, :surname], using: [:tsearch]
Mỗi một kỹ thuật có các lựa chọn khác nhau. Ví dụ: cho phép tìm kiếm một phần của từ:
models/user.rb
class User < ApplicationRecord
include PgSearch
pg_search_scope :search_by_full_name, against: [:name, :surname],
using: {
tsearch: {
prefix: true
}
}
Bây giờ nếu bạn nhập "jay" làm cụm từ tìm kiếm, bạn sẽ thấy tất cả người dùng có tên đầy đủ chứa "jay". Lưu ý rằng tính năng này chỉ được hỗ trợ bắt đầu từ phiên bản 8.4 của Postgres.
Một lựa chọn thú vị là :negation
, cho phép loại trừ từ được cung cấp bằng cách thêm ký tự "!""vào trước. Ví dụ: Bob !Jones. Bật tùy chọn này như sau:
models/user.rb
class User < ApplicationRecord
include PgSearch
pg_search_scope :search_by_full_name, against: [:name, :surname],
using: {
tsearch: {
prefix: true,
negation: true
}
}
Tuy nhiên, hãy lưu ý rằng đôi khi tính năng này có thể cung cấp kết quả không mong muốn.
Nó cũng sẽ làm nổi bật các từ tìm thấy. Điều này có thể thực hiện với tuỳ chọn :highlight
:
models/user.rb
# ...
include PgSearch
pg_search_scope :search_by_full_name, against: [:name, :surname],
using: {
tsearch: {
prefix: true,
negation: true,
highlight: {
start_sel: '<b>',
stop_sel: '',
}
}
}
Ở đây chúng tôi đang nói rằng selection được bọc bằng các thẻ b. Tính năng highlight có một số tùy chọn khác có sẵn nữa.
Để tính năng highlighting hoạt động, chúng ta cần phải thực hiện thêm hai thay đổi. Đầu tiên, thêm phương thức with_pg_search_highlight
như sau:
users_controller.rb
# ...
@users = User.search_by_full_name(params[:term]).with_pg_search_highlight
Thứ hai chỉnh sửa partial:
views/users/_user.html.erb
<li>
< % if user.respond_to?(:pg_search_highlight) %>
< %= user.pg_search_highlight.html_safe %>
< % else %>
< %= user.name %> < %= user.surname %>
< % end %>
</li>
Phương thức pg_search_highlight
trả về các giá trị được highlights các từ giống từ tìm kiếm và làm nổi bật kết quả tìm thấy.
Thêm tính năng Autocomplete
Một tính năng phổ biến khác được tìm thấy trên nhiều trang web là autocomplete. Hãy xem làm thế nào nó có thể được crafted với pg_search và plugin select2. Giả sử chúng ta có trang "Send message", nơi người dùng có thể chọn ai để ghi vào.
Thêm 2 gem Gemfile:
Gemfile
# ...
gem 'select2-rails'
gem 'underscore-rails'
javascripts/application.js
//= require underscore
//= require select2
//= require messages
File messages.coffee
sẽ được tạo ra trong giây lát. Select2 cũng requires một số styles:
stylesheets/application.scss
@import 'select2';
Bây giờ hãy nhanh chóng thêm một route, controller và view mới:
config/routes.rb
# ...
resources :messages, only: [:new]
messages_controller.rb
class MessagesController < ApplicationController
def new
end
end
views/messages/new.html.erb
<%= form_tag '' do %>
< %= label_tag 'to' %>
< %= select_tag 'to', nil, style: 'width: 100%' %>
< % end %>
Form trên chưa hoàn chỉnh, bởi vì nó sẽ không được gửi được. Lưu ý rằng ban đầu trường chọn không có bất kỳ giá trị, chúng sẽ được hiển thị động dựa trên đầu vào của người dùng.
CoffeeScript:
javascripts/messages.coffee
jQuery(document).on 'turbolinks:load', ->
$('#to').select2
ajax: {
url: '/users'
data: (params) ->
{
term: params.term
}
dataType: 'json'
delay: 500
processResults: (data, params) ->
{
results: _.map(data, (el) ->
{
id: el.id
name: "#{el.surname}, #{el.name}"
}
)
}
cache: true
}
escapeMarkup: (markup) -> markup
minimumInputLength: 2
templateResult: (item) -> item.name
templateSelection: (item) -> item.name
Vì bài viết này không dành cho Select2 và JavaScript. Tuy nhiên, chúng ta hãy nhanh chóng xem lại các phần chính:
- dataType trả về là JSON
- delay: 500 nghĩa là yêu cầu sẽ bị trì hoãn tới 0.5 giây sau khi người dùng nhập xong.
- processResults giải thích cách xử lý dữ liệu nhận được từ máy chủ. Tôi đang xây dựng một mảng của các đối tượng có id người dùng và tên đầy đủ. Lưu ý rằng thuộc tính id là bắt buộc.
- escapeMarkup ghi đè lên chức năng escapeMarkup mặc định để tránh bất kỳ sự thoát ra.
- minimumInputLength quy định người dùng nên nhập ít nhất 2 ký tự.
- templateResult định nghĩa giao diện của các tùy chọn được hiển thị trong trình đơn thả xuống.
- templateSelection xác định giao diện của tùy chọn đã chọn.
Bước tiếp theo là tuỳ chỉnh users#index cho phép nó phản hồi với dạng JSON:
users_controller.rb
# ...
def index
respond_to do |format|
if params[:term]
@users = User.search_by_full_name(params[:term]).with_pg_search_highlight
else
@users = User.all
end
format.json
format.html
end
end
Cuối cùng là view:
views/users/index.json.jbuilder
json.array! @users do |user|
json.id user.id
json.name user.name
json.surname user.surname
end
Lưu ý rằng để làm việc này phải có gem jBuilder. Đối với các ứng dụng Rails 5, gem này mặc định có trong Gemfile.
Tính năng highlight đã được thêm vào trong phần trước. Hãy sử dụng tốt nó ở đây! Những gì cần phải làm là làm nổi bật các từ tìm thấy trong danh sách dropdown, nhưng không phải khi lựa chọn đã được chọn. Do đó, chúng ta cần giữ name và surname trong phản hồi JSON và cũng thêm trường full_name:
views/users/index.json.jbuilder
json.array! @users do |user|
json.id user.id
json.full_name user.pg_search_highlight.html_safe
json.name user.name
json.surname user.surname
end
Chỉnh sửa CoffeeScript phần processResults:
processResults: (data, params) ->
{
results: _.map(data, (el) ->
{
name_highlight: el.full_name
id: el.id
name: "#{el.surname}, #{el.name}"
}
)
}
phần templateResult:
templateResult: (item) -> item.name_highlight
Kết luận
Trong bài này, chúng ta đã dùng gem pg_search và select2 plugins. Với sự hỗ trợ của chúng, chúng tôi đã xây dựng chức năng tìm kiếm và thêm một tính năng autocomplete vào ứng dụng, làm cho ứng dụng thêm thân thiện với người dùng. Pg_search có rất nhiều thứ thú vị hơn để cung cấp, vì vậy hãy tìm hiểu thêm tài liệu về nó.
Thank you!
Tài liệu dịch: https://www.sitepoint.com/search-autocomplete-rails-apps/
All rights reserved