I'm using Ransack for search, which is working fine. But I would like the user to be able to search for multiple keywords in the same search input field.
For example if I have blog "I live in Ohio", I would like the user to be able to search "I Ohio", and it would find the blog. I tried the following which doesn't work:
<%= search_form_for #search do |f| %>
<%= f.search_field :title_or_description_cont_any %>
<% f.submit %>
<% end %>
And here is my controller:
def index
#search = Blog.ransack(params[:q])
#blogs = #search.result
end
I would have thought cont_any would work, but for some reason it doesn't?
UPDATE: I'm using also active_admin, I don't know if that matters?
I too had this question (also Rails 5). What finally worked for me was using *_cont_all and passing it an array of search terms.
View: (generic Rails form_with so I can just send params, because our use cases are too long/wordy/complex for the ransack search form to be easily readable!)
= form_with url: book_search_path, method: :get do |f|
.input-label Book Title
= f.text_field :title_terms, value: #title_terms
. . .
Then in my controller, I used *_cont_all, but passed it an array of terms split on spaces.
(Note that I'm not using params[:q], I just create an empty hash and call it 'q', then pass q to ransack -- Book.ransack(q))
# make an array of terms, split on spaces
q[:book_titles_cont_all] = params[:title_terms].split(' ')
Finally, I pass q to Book.ransack(q) and send the results to my view.
It seems that you have to pass to the predicate an array of words, _cont_any or cont_all or other forms of them.
I had fixed it using split string like the code shown below.
if params[:q].present? and params[:q][:name_cont_all].present?
words = params[:q][:name_cont_all].split(" ")
params[:q][:name_cont_all] = words
end
this worked for me
I think what Shuaib did was the right way to go, a more complete answer would be:
def index
q = params[:q]
if q&.key?(:title_or_description_cont_any) && q[:title_or_description_cont_any].is_a?(String)
q[:title_or_description_cont_any] = q[:title_or_description_cont_any].split
end
#search = Blog.ransack(params[:q])
#blogs = #search.result
end
This checks the existence of the field in question and split it if it is a String.
I think it should be like the following
def index
#search = Blog.ransack(params[:q])
#blogs = #search.result(distinct: true)
end
Your form should look like this,
<%= search_form_for #q do |f| %>
<%= f.search_field :title_or_description_cont %>
<% end %>
Title and description need to be actual fields in your database. For example if you created a Post model with fields in it containing header, subtitle and shortDescription, you will need to use these fields instead of title and description.
<%= f.input :title_or_description_cont_any %>
<%= f.input :title_or_description_cont %>
something like this:
<%= f.input :header_or_shortDescription_cont_any %>
I have a Rails 5.1 application and a Ransack search form with a dropdownlist containing some database columns. One of the column is finish_date and has values in format eg "2007-12-31". In the input field I have to type exactly the full date to get a match but I want to change the behaviour that it accepts only the year as input value.
How can I pass in a custom predicate that transforms the input value from full date eg. 2007-12-31 to only year?
What I'm looking for is a Ransack equivalent for the sql query:
SELECT *
FROM imagecapturing
WHERE YEAR(finish_date) = 2007
My Ransack form:
<%= search_form_for #search do |f| %>
<%= f.grouping_fields(f.object.new_grouping) do |g| %>
<%= g.condition_fields(g.object.new_condition) do |c| %>
<%= c.predicate_select only: %i(cont not_eq eq) %>
<%= c.value_fields do |v| %>
<%= v.text_field :value %>
<% end %>
<% end %>
<% end %>
<%= f.submit "filter" %>
<% end %>
in my controller:
#search = Imagecapturing.ransack params[:q]
#imagecapturings = #search.result.page params[:page]
Use Ransacker:
You can write something like this in your model:
ransacker :custom_years do |args|
query = <<-SQL
Year(finish_date)
SQL
Arel.sql(query)
end
See also using Ransacker on Ransack Wiki
It looks like you can accomplish the results you need using scopes. You can see the test project I created here:
https://github.com/wmavis/so_rails_ransack
The documentation has an example of how to use scopes here:
https://github.com/activerecord-hackery/ransack/#using-scopesclass-methods
I added the following to my Imagecapturing model:
def self.finish_year(year)
where("strftime('%Y', finish_date) = ?", year)
end
def self.ransackable_scopes(auth_object = nil)
%i(finish_year)
end
(I had to use strftime because my test project was using sqlite, but you can change that to YEAR(finish_date) like you wanted)
In the view, I just use the search field named finish_year:
<%= f.label :finish_year %>
<%= f.search_field :finish_year %>
Let me know if you have any issues getting it working.
I am facing this difficulty. This works for me :
<% cat_st = 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29 %>
<%= Categories.where(cat_id: [cat_st]).distinct.count(:af_id) %>
However, I need to create the cat_st string from my GET parameters. When I create it as a string, it gets only the first integer. Actually, I really don't know what the cat_st variable is. I tried to create an array, but it doesn't work. Any ideas?
( it doesn't work)
<% cat_st = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29' %>
<%= Categories.where(cat_id: [cat_st]).distinct.count(:af_id) %>
cat_st = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29'
cat_array = cat_st.split(',')
Categories.where(cat_id: cat_array).distinct.count(:af_id)
There may be a better solution for what you're trying to achieve if you give some additional details about what you're trying to do, seems like it should be some sort of search form that passes multiple ids. Something like this for example:
class Search
include ActiveModel::Model
attr_accessor :category_ids
end
<%= form_for Search.new, url: 'whatever_the_url_is' do |f| %>
<div class="field">
<%= f.label :category_ids %>
<%= f.collection_check_boxes :category_ids, Category.all, :id, :name %>
</div>
<% end %>
search = Search.new(search_params)
Categories.where(cat_id: search.category_ids).distinct.count(:af_id)
You can pass just a range to the where method so:
Categories.where(cat_id: (1..29)).distinct.count(:af_id)
and to pass data via GET request just use a limitation:
http://host.domain/controller/action?begin=1&end=29
controller:
def action
#cat = Categories.where(cat_id: (params[:begin].to_i..params[:end].to_i)).distinct.count(:af_id)
end
then render the view using #cat variable.
If you add square brackets to the param name in the URL query, Rails (specfically Rack) will interpret that as an array:
http://some.route.url/path?foo[]=bar&foo[]=buzz
The controller will read params[:foo] as ['bar', buzz']
However, I have a feeling that use case problem you are attempting to solve might have a much simpler solution. Care to go into any more detail?
Ive got a select_tag field at the top of a page and trying to get the selected option to change the content on the page based on the users selection.
Im a learner and have found pieces of information around but without detailed examples and good explanations on how to best approach and implement.
The scenario is as follows:
I have a belongs_to association between a project and documents and in one of my views which lists documents, I want to only show all the documents that belong to the currently selected project in the select tag.
Passing the selected project's id to the documents index action which only shows documents for a specified project id via a link_to tag came to mind. This would thus refresh the page with the correct documents in the view but I believe that is not the correct way to do it and that I cant use link_to tags as options in a select_tag. Can anyone help and offer an example?
I would suggest using the form.select method and options_for_select as in
f.select :attribute, options_for_select(#array, default_value)
and in your controller you should create or update using the submitted parameter
n = record.new(:attribute => params[:attribute])
have fun
In your controller:
def index
if params[:project]
#documents = Document.where(:project => params[:project]
else
#projects = Project.all
end
end
In your form/view:
<%= form_tag 'projects', :method => :get do %>
<%= options_from_collection_for_select(#projects, :id, :name)
<%= submit_tag %>
<% end %>
<% if #documents %>
<%= #documents.each do |d| %>
....
<% end >
<% end %>
I currently have a basic search that will allow me to type in a word and it will return back all records that are like that search. It is as followed:
application.html.erb
<%= form_tag games_path, :method => 'get' do %>
<%= hidden_field_tag :direction, params[:direction] %>
<%= hidden_field_tag :sort, params[:sort] %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag t('.searchb'), :game_name => nil %>
<% end %>
controller and Model
#games = Game.search(params[:search])
def self.search(search)
search = search.to_s.strip.split
search.inject(scoped) do |combined_scope, search|
combined_scope.where(['game_name LIKE ? OR genre LIKE ? OR console LIKE ?', "%#{search}%", "%#{search}%", "%#{search}%"])
end
end
What I now wish to do though is the ability to enter multiple words in to the search bar and have it return all related records to those words rather than any record that has them all.
e.g. If I type fighting, action
I want it to return all fighting games and all action games.
How would I go about implementing this?
Model:
EDIT
You should use a fulltext search engine like Sphinx for this! There is a gem called thinking-sphinx wich offers an interface between ruby and the Sphinx server! The only downside is that you need to update the database periodly. It doesnt support live updates.
You have to take the key words, loop them and create queries to achieve this. You can use inject to make it easy. This might help.