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