ex search subquery - maxivak/simple_search_filter GitHub Wiki
Search by a field in another model
If you need to search by a field from another model then you can use options condition: :custom
, condition_scope: :_your_scope_name_
and define scope in the main model.
search_filter :index, {save_session: true, search_method: :post_and_redirect, url: :orders_path, search_url: :search_orders_path, search_action: :search} do
default_order "id", 'desc'
# custom scope
field :city, :string, :text, {label: 'City', default_value: '', ignore_value: '', search_by: :id, condition: :custom, condition_scope: :of_client_city}
end
Example. Search by field in another model
In our example we have orders and clients. Client has a city field of type string. We want to find orders by clients from a certain city.
Models:
# app/models/order.rb
class Order < ActiveRecord::Base
belongs_to :client
end
# app/models/client.rb
class Client < ActiveRecord::Base
has_many :orders
end
Routes:
Rails.application.routes.draw do
...
resources :orders do
collection do
post :search
end
end
end
Define filter in controller app/controllers/orders_controller.rb
:
class OrdersController < ApplicationController
search_filter :index, {save_session: true, search_method: :post_and_redirect, url: :orders_path, search_url: :search_orders_path, search_action: :search} do
default_order "id", 'desc'
# custom scope
field :city, :string, :text, {label: 'City', default_value: '', ignore_value: '', search_by: :id, condition: :custom, condition_scope: :of_client_city}
end
def index
@items = Order.includes(:client).by_filter(@filter)
end
end
Orders will be searched by city of client.
Because we search orders and city
field belongs to client, then we need to use a custom search method condition: :custom
and specify a scope condition_scope: :of_client_city
.
Define scope :of_client_city
in Order model:
class Order < ActiveRecord::Base
belongs_to :client
# search
searchable_by_simple_filter
scope :of_client_city, lambda { |city| where_client_city(city) }
def self.where_client_city(v)
if v.present?
where(client_id: Client.select("id").where(:city => v))
else
where("1=1")
end
end
That's it. The rest is done by the simple_search_filter gem.
View app/views/orders/index.html.haml
:
%h1 Orders
Filter:
= inline_filter_form_for @filter
%br
%table.table.table-bordered
%tr
%th= link_to_sortable_column :id, 'ID'
%th Date
%th Client name
%th Client city
- @items.each do |item|
%tr
%td=item.created_at
%td=item.id
%td= item.client.name
%td= item.client.city
= paginate @items
%br
Found: #{@items.total_count}
SQL query
It will search orders using a subquery in where clause. i.e. having city = 'london' the query to the database would be like:
SELECT `orders`.*
FROM `orders`
WHERE `orders`.`client_id` IN (
SELECT `clients`.`id` FROM `clients` WHERE `clients`.`city` = 'london'
) AND (1=1 )
ORDER BY id desc LIMIT 25 OFFSET 0