Advance search with Ransack Gem
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 sử dụng để tìm kiếm dữ liệu, 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.
Trong bài viết Tìm hiểu về Ransack Gem và ứng dụng trong tìm kiếm đã hướng dẫn cách sử dụng Ransack
với hai phương thức tìm kiếm trên.
Bài viết này sẽ bổ sung thêm một vài cách sử dụng và tùy biến Ransack
cho các vấn đề tìm kiếm khác nhau.
II. Các vấn đề bổ sung
1. Các hàm search trong Ransack
Phương thức search cơ bản trong Ransack được biết đến như là các hậu tố (predicates
)
Các hậu tố được dùng trong các truy vấn tìm kiếm trong Ransack để xác định thông tin đối chiếu.
Dưới đây là một vài hậu tố của Ransack hay được dùng:
eq (equals)
Hậu tố eq trả về tất cả các record có một trường bằng đúng với gía trị tìm kiếm:
User.ransack(first_name_eq: "Ryan").result.to_sql
=> SELECT "users".* FROM "users" WHERE "users"."first_name" = "Ryan"
not_eq: thì ngược lại eq
matches
Trả về các record có trường giống với một gía trị tìm kiếm:
User.ransack(first_name_matches: "Ryan").result.to_sql
=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE "Ryan")
does_not_match ngược lại matches
lt(less than)
Trả về tất cả các record có trường nhỏ hơn gía trị tìm kiếm
User.ransack(age_lt: 25).result.to_sql
SELECT "users".* FROM "users" WHERE ("users"."age" < 25)
gt (greater than) ngược lại lt
lteq (less than equal to)
Trả về tất cả các record nhỏ hơn hoặc bằng gía trị tìm kiếm
User.ransack(age_lteq: 25).result.to_sql
=> SELECT "users".* FROM "users" WHERE ("users"."age" <= 25)
gteq (greater than or equal to) ngược lại với lteq
in
Hậu tố in
trả về các record có trường nằm trong một danh sách có sẵn:
User.ransack(age_in: 20..25).result.to_sql
=> SELECT "users".* FROM "users" WHERE "users"."age" IN (20, 21, 22, 23, 24, 25)
Ngược lại với in là not_in
cont
Hậu tố cont
trả về tất cả các record có trường chứa gía trị tìm kiếm
User.ransack(first_name_cont: 'Rya').result.to_sql
=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%Rya%')
Ngược lại là not_cont
start (start with)
Trả về các record có trường bắt đầu bằng gía trị tìm kiếm
User.ransack(first_name_start: 'Rya').result.to_sql
=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE 'Rya%')
Ngược lại là not_start
end (ends with) Trả về các record có trường kết thúc bằng gía trị tìm kiếm
User.ransack(first_name_end: 'yan').result.to_sql
=> SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%yan')
Ngược lại là not_end
2. Sắp xếp với Ransack
Trong Ransack sử dụng helper sort_link
để tạo bảng có các header được gẵn với link sort
<%= sort_link(@q, :name) %>
Có thể bổ sung thêm tên cột hay thứ tự sắp xếp mặc định:
<%= sort_link(@q, :name, 'Last Name', default_order: :desc) %>
Với quan hệ polymorphic, cần chỉ rõ tên model:
<%= sort_link(@q, :xxxable_of_Ymodel_type_some_attribute, 'Attribute Name') %>
Có thể sort nhiều trường thông qua mảng thứ tự cụ thể:
<%= sort_link(@q, :last_name, [:last_name, 'first_name asc'], 'Last Name') %>
Ngoài ra, có thể định nghĩa một thứ tự sort mặc định trong controller:
@search = Post.ransack(params[:q])
@search.sorts = 'name asc' if @search.sorts.empty?
@posts = @search.result.paginate(page: params[:page], per_page: 20)
3 Trộn Search
Để tìm các record phù hợp với đa tìm kiếm, thì có thể trộn tất cả các điều kiện search vào quan hệ ActiveRecord để thực hiện một truy vấn đơn. Để tránh bị conflict giữa tên các bảng, cần tạo một bối cảnh chung lưu lại tên định danh bảng được dùng cho tất cả các điều kiện trước khi khởi tạo qúa trình search
shared_context = Ransack::Context.for(Person)
search_parents = Person.ransack(
{ parent_name_eq: "A" }, context: shared_context
)
search_children = Person.ransack(
{ children_name_eq: "B" }, context: shared_context
)
shared_conditions = [search_parents, search_children].map { |search|
Ransack::Visitor.new.accept(search.base)
}
Person.joins(shared_context.join_sources)
.where(shared_conditions.reduce(&:or))
.to_sql
từ đó sẽ sinh ra câu truy vấn
SELECT "people".*
FROM "people"
LEFT OUTER JOIN "people" "parents_people"
ON "parents_people"."id" = "people"."parent_id"
LEFT OUTER JOIN "people" "children_people"
ON "children_people"."parent_id" = "people"."id"
WHERE (
("parents_people"."name" = 'A' OR "children_people"."name" = 'B')
)
ORDER BY "people"."id" DESC
4 Tìm kiếm đa hình (Polymorphic searches)
Khi tạo ra các tìm kiếm từ các polymorphic models thì cần phải chỉ rõ kiểu model đang tìm
Vd: với 2 models:
class House < ActiveRecord::Base
has_one :location, as: :locatable
end
class Location < ActiveRecord::Base
belongs_to :locatable, polymorphic: true
end
Bình thường nếu không sử dụng quan hệ polymorphic, ta sẽ search theo:
Location.ransack(locatable_number_eq: 100).result
Tuy nhiên, dòng lệnh trên sẽ báo lỗi:
ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :locatable
Để có thể search location theo house number khi quan hệ là polymorphic thì phải sử dụng:
Location.ransack(locatable_of_House_type_number_eq: 100).result
với _of_House_type_
được thêm vào search key. Điều đó cho phép Ransack chỉ rõ tên bảng trong join querries.
III Lời kết
Ransack là một gem rất hữu ích, hỗ trợ đầy đủ để tùy biến search và sort dữ liệu dễ dàng.
Nguồn tham khảo:
All rights reserved