TÌM HIỂU RANSACK, GEM TÌM KIẾM CHO ỨNG DỤNG RAILS

Giới thiệu

Gem ransack cho phép người dùng xây dựng tìm kiếm cho ứng dụng của bạn. Tích hợp cả 2 hình thức tìm kiếm đơn giản và nâng cao, đồng thời còn giúp người dùng sắp xếp kết quả tìm kiếm.

Cài đặt

Thêm gem ransack vào trong gemfile, chạy bundle.

gem "ransack"

Sử dụng

Sử dụng ransack cùng với dữ liệu mẫu products

E370I01 (1).png

Trong controller

Tìm kiếm đối tượng bằng cách sử dụng Product.search với tham số:q là một hash bao gồm các cột trong products model mà bạn muốn tìm kiếm products theo nó, gọi hàm result để trả về kết quả result để trả về kết quả.

app/controller/products_controller.rb

 class ProductsController < ApplicationController
  def index
    @search = Product.search(params[:q])
    @products = @search.result
  end
end

Trong view

Sử dụng search_form_for thay cho form_for, @search để lấy dữ liệu

app/views/products/index.html.erb

<%= search_form_for @search do |f| %>
  <div class="field">
    <%= f.label :name_cont, "Name contains" %>
    <%= f.text_field :name_cont %>
  </div>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

Nhập bất kì thông tin bạn muốn tìm kiếm, ransack sẽ trả về kết quả cho bạn

E370I02.png

Ngoài ra, có thể thêm nhiều option tìm kiếm, ví dụ theo khoảng giá của products

<%= search_form_for @search do |f| %>
  <div class="field">
    <%= f.label :name_cont, "Name contains" %>
    <%= f.text_field :name_cont %>
  </div>
  <div class="field">
    <%= f.label :price_gteq, "Price between" %>
    <%= f.text_field :price_gteq %>
    <%= f.label :price_lteq, "and" %>
    <%= f.text_field :price_lteq %>
  </div>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

E370I03.png

Giống như trong hệ quản trị cơ sở dữ liệu sql, ransack cho phép bạn sắp xếp kết quả tìm kiếm theo tên trường bằng cách click vào tên tiêu đề trường.

app/views/products/index.html.erb

<tr>
  <th><%= sort_link(@search, :name, "Product Name") %></th>
  <th><%= sort_link(@search, :released_on, "Release Date") %></th>
  <th><%= sort_link(@search, :price, "Price") %></th>
</tr>

Tìm kiếm động

Ransack còn cung cấp cho người dùng khả năng tìm kiếm động, cho phép select các trường mà họ muốn tìm kiếm, sử dụng condition_fields method để làm điều này app/views/products/index.html.erb

 <%= search_form_for @search do |f| %>
  <%= f.condition_fields do |c| %>
  <div class="field">
    <%= c.attribute_fields do |a| %>
      <%= a.attribute_select %>
    <% end %>
    <%= c.predicate_select %>
    <%= c.value_fields do |v| %>
      <%= v.text_field :value %>
    <% end %>
    </div>
  <% end %>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

Đồng thời trong controller tương ứng, thêm @searchbuild_condition để thực hiện tìm kiếm động

Kết quả

E370I05.png

Ransack còn cho phép bạn tìm kiếm kết hợp dựa trên has_many, belongs_to. app/views/products/index.html.erb

<%= a.attribute_select associations: [:category] %>

E370I06.png

Thêm xóa các điều kiện tìm kiếm

Tạo partial form, add link remove search

app/views/products/_condition_fields.html.erb

<div class="field">
  <%= f.attribute_fields do |a| %>
    <%= a.attribute_select associations: [:category] %>
  <% end %>
  <%= f.predicate_select %>
  <%= f.value_fields do |v| %>
    <%= v.text_field :value %>
  <% end %>
  <%= link_to "remove", '#', class: "remove_fields" %>
</div>

Viết hàm link_to_add_fields trong ApplicationHelper. app/helper/application_helper.rb

module ApplicationHelper
  def link_to_add_fields(name, f, type)
    new_object = f.object.send "build_#{type}"
    id = "new_#{type}"
    fields = f.send("#{type}_fields", new_object, child_index: id) do |builder|
      render(type.to_s + "_fields", f: builder)
    end
    link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
  end
end

thêm add, remove link vào indexhtml.erb

<%= search_form_for @search do |f| %>
  <%= f.condition_fields do |c| %>
    <%= render "condition_fields", f: c%>
  <% end %>
  <p><%= link_to_add_fields "Add Conditions", f, :condition %>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

Sử dụng đoạn javascript để thực hiện thêm, xóa link trong index views app/assets/javascript/products.jscoffee

jQuery ->
  $('form').on 'click', '.remove_fields', (event) ->
    $(this).closest('.field').remove()
    event.preventDefault()

  $('form').on 'click', '.add_fields', (event) ->
    time = new Date().getTime()
    regexp = new RegExp($(this).data('id'), 'g')
    $(this).before($(this).data('fields').replace(regexp, time))
    event.preventDefault()

Khi có quá nhiều condition được dùng để search, dữ liệu truyền trên get requset bị giới hạn, vì vậy ,thay vì sử dụng get request ta sử dụng pót request , add search routes, action index trong routes.

config/routesrb

Store::Application.routes.draw do
  resources :products do
    collection { post :search, to: 'products#index' }
  end
  root to: 'products#index'
end

Thêm url, post method trong form search

<%= search_form_for @search, url: search_products_path, method: :post do |f| %>
  <%= f.condition_fields do |c| %>
    <%= render "condition_fields", f: c %>
  <% end %>
  <p><%= link_to_add_fields "Add Conditions", f, :condition %>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>

Một số tính năng nâng cao khác

Ransack còn cung cấp cho người dùng môt số tính năng nâng cao khác như

  • Authorization, dựa trên whitelist, blacklist

Người dùng chỉ có thể searching, sorting trên một số cột nhật định trong whitelists.

  • Sử dụng or thay vì and

Mặc định của việc tìm kiếm trên nhiều trường trong ransack là and, thay vì vậy bạn có thể sử dụng or, thay đổi diều này trong params[:q].

Kết luận

Ransack là một công cụ tìm kiếm tuyệt, rất đơn giản, dễ sử dụng và hiệu quả. Tuy nhiên vẫn có những hạn chế nhất đinh như việc chưa thể phân trang với kết quả tìm kiếm mà phải thực hiện bằng javascript.