Ransack, find a record that has all related records

I have a recipe model in which there are ingredients, and each ingredient relates to an item. In my advanced search form, I would like the user to select multiple ingredients and allow Ransack to find a recipe containing all the ingredients selected by the user.

I tried the following search box:

= f.collection_select(:ingredients_item_id_in, Item.all, :id, :name, {}, {multiple: true}) 

But logically, this leads to the display of all recipes that contain any of the selected ingredients.

changing :ingredients_item_id_in to :ingredients_item_id_in_all results in an invalid request, because one record cannot contain multiple item_id values.

Any ideas on creating this search parameter in Ransack, or should I create a subquery for this?

Upon request, my controller method for searching is:

  def search @q = Recipe.ransack(params[:q]) @recipes = @q.result(distinct: true).include_related_models.published end 
+8
ruby-on-rails ransack
source share
1 answer

I recently faced a similar task for my project ( Rails 4.2.4 / Ruby 2.3.1 ).

We have many amenities. I need to get all the estates that include all the selected amenities.

This is how I resolved it with Ransack

In my case, I have a has_many :through relation.

estate.rb

 class Estate < ActiveRecord::Base has_many :estate_comforts has_many :comforts, through: :estate_comforts end 

comfort.rb

 class Comfort < ActiveRecord::Base has_many :estate_comforts has_many :estates, through: :estate_comforts end 

estate_comfort.rb

 class EstateComfort < ActiveRecord::Base belongs_to :estate belongs_to :comfort end 

For complex queries, you need to search through post . To do this, you need to edit routes like this. And add the search action to estates_controlle.rb . Read the Ransack wiki for more information.

routes.rb

 ... resources :estates collection do match 'search' => 'estates#search', via: %i[get post], as: :search end end 

estates_controller.rb

 class EstatesController < ApplicationController ... def index @q = Estate.ransack(params[:q]) @estates = if params[:q]&.has_key?(:estate_comforts_comfort_id_eq_any) # Store checked comforts session[:estate_comforts_comfort_id_eq_any] = params[:q][:estate_comforts_comfort_id_eq_any] comforts_count = params[:q][:estate_comforts_comfort_id_eq_any].count ids = @q.result.includes(:estate_comforts).group_by(&:id).select { |_, v| v.count == comforts_count}.keys Estate.where(id: ids) else @q.result(distinct: true) end end def search index render :index end end 

And finally, part of the template ...

Estates / index.haml

 = search_form_for @q, url: search_estates_path, html: { method: :post } do |f| # here goes the form inputs # Polulate checkboxes with previously checked comforts - Comfort.find_each do |comfort| # Was checked previously? - checked = comfort.id.to_s.in?(session[:estate_comforts_comfort_id_eq_any].to_a) %div %input{ name: 'q[estate_comforts_comfort_id_eq_any][]', type: "checkbox", id: "checkbox#{comfort.id}", value: comfort.id, checked: checked } %label{for: "checkbox#{comfort.id}"}= comfort.name 

Will generate the following html

 <form class="estate_search" id="estate_search" action="/estates/search" accept-charset="UTF-8" method="post"> <div> <input checked="" id="checkbox1" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="1"> <label for="checkbox1">Comfort Name 1</label> </div> <div> <input id="checkbox2" name="q[estate_comforts_comfort_id_eq_any][]" type="checkbox" value="2"> <label for="checkbox2">Comfort Name 2</label> </div> </form> 
+1
source share

All Articles