Tìm hiểu về Ransack Gem và ứng dụng trong tìm kiếm
Bài đăng này đã không được cập nhật trong 9 năm
I.Tổng quan
Ransack là một gem được viết lại từ gem MetaSearch
được dùng để tìm kiếm dữ liệu. Nó hỗ trợ nhiều tính năng tương tự như MetaSearch
nhưng khác nhau khá nhiều so với MetaSearch
trong cách thức thực hiện tìm kiếm, và tính tương thích không phải là mục tiêu thiết kế của nó.
Ransack
cho phép tạo ra cả hai hình thức tìm kiếm đơn giản và tìm kiếm nâng cao tùy theo các mô hình ứng dụng trong chương trình.
II.Sử dụng Ransack (Getting started)
1. Cài đặt
Ransack khả dụng với Rails 3.x, 4.0, 4.1 và 4.2.
Thêm dòng sau vào Gemfile:
gem "ransack”
Hoặc nếu bạn muốn dùng bản mới nhất của Ransack:
gem "ransack", :git => "git://github.com/ernie/ransack.git"
2. Cách sử dụng (Usage)
Ransack có thể được sử dụng theo một trong hai chế độ, đơn giản hoặc nâng cao.
2.1. Simple Mode (Mô hình đơn giản)
Mô hình này hoạt động giống như Meta Search
, với những người đã quen thuộc với nó thì đòi hỏi những thiết lập rất nhỏ.
Nếu đã biết Meta Search
, những điều cần lưu ý :
-
Từ khóa mặc định cho
param
tìm kiếm bây giờ là:q
, thay vì:search
. Mục đích là để rút ngắn chuỗi truy vấnquery strings
trên trình duyệt. -
form_for
chuyển thànhsearch_form_for
, và xác nhận (chắc chắn) rằng mộtRansack::Search object
được truyền cho nó. -
Các method
Common ActiveRecord::Relation
không còn được phân cấp bởi các đối tượng tìm kiếm như trước. Thay vào đó, bạn sẽ nhận được kết quả tìm kiếm thông qua việc gọiSearch#result
. -
Nếu được thông qua (
distinct: true
),result
sẽ tạo ra một truy vấnSELECT DISTINCT
để tránh trả về các bản ghi trùng lặp ngay cả khi các điều kiện trong 1join
sẽ không dẫn đến trùng lặp.
Lưu ý đối với nhiều cơ sở dữ liệu, distinct: true
không khả dụng. Trong trường hợp đó sử dụng #to_a.uniq
với tập kết quả để lấy được các bản ghi duy nhất.
Controller:
def index
@q = Person.search(params[:q])
@people = @q.result(distinct: true)
end
//hoặc
def index
@q = Person.search(params[:q])
@people = @q.result.includes(:articles).page(params[:page])
# hoặc sử dụng `to_a.uniq` để xóa các bản ghi trùng lặp như dưới đây (có thể dùng cả trong view):
@people = @q.result.includes(:articles).page(params[:page]).to_a.uniq
end
View:
Sử dụng search_form_for
thay vì form_for
để tạo form search
trong view :
<%= search_form_for @q do |f| %>
# Tìm kiếm theo tên ...
<%= f.label :name_cont %>
<%= f.text_field :name_cont %>
# Search if an associated articles.title starts with...
<%= f.label :articles_title_start %>
<%= f.text_field :articles_title_start %>
# Tìm kiếm theo nhiều attr cho 1 giá trị ...
<%= f.label :name_or_description_or_email_or_articles_title_cont %>
<%= f.text_field :name_or_description_or_email_or_articles_title_cont %>
<%= f.submit %>
<% end %>
cont (contains)
và start (starts with)
là 2 phương thức tìm kiếm mặc định trong ransack
.Xem thêm thông tin có thể vào Constants hoặc wiki
.
Advanced Mode
Tìm kiếm Advanced
sử dụng thuộc tính lồng nhau của Rails
để tạo ra các truy vấn phức tạp với nhiều model khác nhau trong ứng dụng.
Thiết lập Routes:
resources :people do
collection do
match 'search' => 'people#search', :via => [:get, :post], :as => :search
end
end
… thêm action trong controller …
def search
index
render :index
end
… cập nhật search_form_for
trong view …
<%= search_form_for @q, :url => search_people_path,
:html => {:method => :post} do |f| %>
Ransack #search method
Ransack
sẽ tạo #search
phù hợp trong model
, trong trường hợp #search
đã được định nghĩa thì có thể sử dụng #ransack
. Ví dụ :
Article.search(params[:q])
Article.ransack(params[:q])
Associations
Có thể dễ dàng sử dụng Ransack để tìm kiếm các object liên kết với nhau bằng quan hệ has_many
và belongs_to
.
Giả sử có mối liên kết như sau:
class Employee < ActiveRecord::Base
belongs_to :supervisor
end
class Department < ActiveRecord::Base
has_many :supervisors
end
class Supervisor < ActiveRecord::Base
belongs_to :department
has_many :employees
end
… và controller
class SupervisorsController < ApplicationController
def index
@search = Supervisor.search(params[:q])
@supervisors = @search.result(:distinct => true)
end
end
… form view được thiết lập
<%= search_form_for @search do |f| %>
<%= f.label :last_name_cont %>
<%= f.text_field :last_name_cont %>
<%= f.label :department_title_cont %>
<%= f.text_field :department_title_cont %>
<%= f.label :employees_first_name_or_employees_last_name_cont %>
<%= f.text_field :employees_first_name_or_employees_last_name_cont %>
<%= f.submit "search" %>
<% end %>
...
<%= content_tag :table %>
<%= content_tag :th, sort_link(@q, :last_name) %>
<%= content_tag :th, sort_link(@q, "departments.title") %>
<%= content_tag :th, sort_link(@q, "employees.last_name") %>
<% end %>
Using Scopes/Class Methods
Tìm bởi scope
yêu cầu định nghĩa 1 whitelist
của ransackable_scopes
trong model class, whitelist
nên là 1 mảng các ký tự. Mặc định tất cả các class method
được bỏ qua,. Scope sẽ applied cho matching
các giá trị true
hoặc các giá trị mà scope chấp nhận :
class Employee < ActiveRecord::Base
scope :active, ->(boolean = true){(where active: boolean)}
scope :salary_gt, ->(amount){where('salary > ?', amount)}
# Scopes are just syntactical sugar for class methods, which may also be used:
def self.hired_since(date)
where('start_date >= ?', date)
end
private
def self.ransackable_scopes(auth_object = nil)
if auth_object.try(:admin?)
# allow admin users access to all three methods
%i(active hired_since salary_gt)
else
# allow other users to search on active and hired_since only
%i(active hired_since)
end
end
end
Employee.search({active: true, hired_since: '2013-01-01'})
Employee.search({salary_gt: 100_000 }, { auth_object: current_user})
3. Lời kết
Link trên github : https://github.com/nguyenhoa/brs_2/commit/aa71c0c3ba4f375b57510c2ad50fa3208e26d947
Link trên heroku : http://brs2.herokuapp.com/
admin account : admin1@example.com
user account : user1@example.com
password : foobar
Chỉ dẫn
: đăng nhập -> View -> Users (search form trong users index)
Nguồn tham khảo:
https://github.com/ernie/ransack
All rights reserved