I have a search functionality in my app which works as the following
Homepage: user selects location from a drop-down and then enters a search key-word and searches to get a set of results
Search Page (REfine Search Options): Once the user hits this page from the above he will be provided with more options to refine the search and narrow the results.
Right now we are implementing as follows but i am assuming as the paramenters increase over 5-7 the number of combinations will increase the number of if-else-elseif statement as well.
#refine search
#search = params[:search]
if params[:city].present? && params[:location_ids].present? && params[:search].blank?
#blood_banks = BloodBank.where(
{ :city_id => "#{#city}" }).where(
{ :location_id.in => params[:location_ids] })
elsif params[:search].present? && params[:location_ids].blank?
#blood_banks = BloodBank.where(
{ :bb_name => /##search/i })
elsif params[:search].present? && params[:city].present? && params[:location_ids].present?
#blood_banks = BloodBank.where(
{ :city_id => "#{#city}" }).where(
{ :location_id.in => params[:location_ids] }).where(
{ :bb_name => /##search/i })
end
Which is the best way to implement the same.
How do you achieve below code,
if params[:gender].present?
if params[:gender] == "male"
#doctors = Doctor.where( :gender => "Male")
end
if params[:gender] == "female"
#doctors = Doctor.where( :gender => "Female")
end
if params[:gender] == "any"
#doctors = Doctor.where( :gender => "Male") || Doctor.where( :gender => "Female")
end
end
Mongoid's where returns a Mongoid::Criteria and Mongoid::Criteria responds to where by returning another Mongoid::Criteria. This means that you can build your query piece by piece:
#blood_banks = BloodBank.all
if params[:city].present?
#blood_banks = #blood_banks.where(:city_id => params[:city])
end
if params[:location_ids].present?
#blood_banks = #blood_banks.where(:location_id.in => params[:location_ids])
end
...
As far as the second part goes, if you're searching for any gender then just leave it out entirely, then you can do things like:
#doctors = Doctor.all
genders = { 'male' => 'Male', 'female' => 'Female' }
if genders.has_key? params[:gender]
#doctors = #doctors.where(:gender => genders[params[:gender]]
end
Searching for any gender is the same not filtering on gender at all so the nil and 'all' cases are the same. Then you can handle the input and :gender values with a simple lookup table.
Related
I am creating a form for my posts search. I am doing like this ....
erb form code...
<%= form_tag '/posts/search-post', :remote=> "true" do %>
<p>
<%= text_field_tag :search, params[:search], :placeholder => "Search Posts..." %><br/>
<%= radio_button_tag :day, 1, params[:day] %>None
<%= radio_button_tag :day, 2, params[:day] %>Last Week
<%= radio_button_tag :day, 3, params[:day] %>Last Month<br/>
<%= submit_tag "Search", :onclick => "document.getElementById('spinner').style.visibility='visible';document.getElementById('postlist').style.visibility='hidden'" %>
</p>
<% end %>
root.rb
match 'posts/search-post', to: 'posts#search_post'
posts_controller.rb
def search_post
if !params[:search].blank? && params[:day].blank?
#posts = Post.paginate(page: params[:page],:per_page => 5).search(params[:search])
elsif params[:search].blank? && !params[:day].blank?
#posts = Post.paginate(page: params[:page],:per_page => 5).all if params[:day] == "1"
#posts = Post.paginate(page: params[:page],:per_page => 5).where("created_at >= ?", 1.week.ago.utc) if params[:day] == "2"
#posts = Post.paginate(page: params[:page],:per_page => 5).where("created_at >= ?", 1.month.ago.utc) if params[:day] == "3"
elsif !params[:search].blank? && !params[:day].blank?
#posts = Post.paginate(page: params[:page],:per_page => 5).search(params[:search]) if params[:day] == "1"
#posts = Post.paginate(page: params[:page],:per_page => 5).search(params[:search]).where("created_at >= ?", 1.week.ago.utc) if params[:day] == "2"
#posts = Post.paginate(page: params[:page],:per_page => 5).search(params[:search]).where("created_at >= ?", 1.month.ago.utc) if params[:day] == "3"
else
end
end
Post.rb model
def self.search(search)
search_condition = "%" + search + "%"
if search
find(:all, :conditions => ['lower(content) LIKE ? OR lower(title) LIKE ?', search_condition.downcase,search_condition.downcase])
else
find(:all)
end
end
search-post.js.erb
$("#posts_list").html("<%= escape_javascript( render(:partial => "posts") ) %>");
When I search by both keyword and day type then searching is not working (Getting all post list-items). I don't know where i am wrong. Please help.
I am not sure if you've done this intentionally, but in both your elseif and else sections in your controller, you're overwriting your search results.
For example, in your else section, you first do this:
#posts = Post.paginate(page: params[:page],:per_page => 5).search(params[:search]) if params[:day] == "1"
and then you do this:
#posts = Post.paginate(page: params[:page],:per_page => 5).where("created_at >= ?", 1.week.ago.utc) if params[:day] == "2"
Which means that the second set of results that are saved in #posts will overwrite your first set of results (what was saved in #posts in your first line).
Since you're doing an "&&" operation, then you should include your result set from your first line into the second.
One solution to your problem might be to change your Post.rb model to something like this:
def self.search(search, previous_results_set)
search_condition = "%" + search + "%"
if search
if previous_result_set.nil?
find(:all, :conditions => ['lower(content) LIKE ? OR lower(title) LIKE ?', search_condition.downcase,search_condition.downcase])
else
previous_result_set.find(:all, :conditions => ['lower(content) LIKE ? OR lower(title) LIKE ?', search_condition.downcase,search_condition.downcase])
else
find(:all)
end
end
My code might not be perfect and you can probably find a more efficient way of doing it in your code, but you get the idea. Even when you user the .where, you need to perform the .where on the previous result set, not on the Post model as a whole again. That way you will be filtering your previously filtered results.
Hope this helps.
I am really new to Ruby and Rails and need to know how to check if a string contains a dash before applying titlelize.
#city = City.first :conditions => { :title => params[:city].titleize }
What I need to do is:
#city = City.first :conditions => { :title => params[:city] }
and then write something that will apply titleize ONLY if the #city variable doesn't contain a dash.
I like this solution added by zachrose a couple of weeks ago: https://gist.github.com/varyonic/ccda540c417a6bd49aec
def nice_title(phrase)
return phrase if phrase =~ /^-+$/
phrase.split('-').map { |part|
if part.chars.count == part.bytes.count
part.titleize
else
part.split(' ').map { |word| word.mb_chars.titleize }.join(' ')
end
}.join('-')
end
if params[:city] =~ /-/
#city = City.first :conditions => { :title => params[:city] }
else
#city = City.first :conditions => { :title => params[:city].titleize }
end
I do not know why you are using this, but I believe it will not work for all cases. There should be a better approach.
After updating to rails 3.2.11, I noticed a problem with a search form in my app. After seeing the "We're sorry, but something went wrong." message on Heroku, I checked the logs and noticed the error message below:
NoMethodError (undefined method `to_i' for ["2010"]:Array):
app/controllers/skis_controller.rb:29:in `index'
This is referring to the index controller, where my search results are displayed. Im using a search form partial for the actual search form, which contains several collection_selects.
Line 29 in the index action refers to the model_year ("2010" in the search above). When I remove the model_year field from the collection_select everything works fine (no error message after searching). This is what the model_year collection_select looks like:
<%= collection_select(:ski, :model_year, #model_years.sort_by(&:model_year).reverse, :model_year, :model_year, {}, :multiple => true, :class => "chzn-select", :'data-placeholder' => "Enter Model Year") %>
This is bugging me because it has been working fine for the last three weeks. I recently updated to rails 3.2.11 but I'm not positive that is when the problem started (I know I did not notice it before). I checked out the collection_select documentation and everything seems fine.
model_year is stored in the database as an integer and I've confirmed this with rails console.
Any help or ideas would be very much appreciated! Thanks
EDIT - UPDATED to add controller code:
def index
#companies = Brand.scoped
#ski_types = Ski.find(:all, :select => "DISTINCT ski_type")
#genders = Ski.find(:all, :select => "DISTINCT gender")
#names = Ski.find(:all, :select => "DISTINCT name")
#price_ranges = PriceRange.scoped
#model_years = Ski.find(:all, :select => "DISTINCT model_year")
#sorts = ["Price", "Rating", "Availability"]
if params[:ski].present? || params[:brand].present? || params[:price_range].present?
# Creates references for collection select
ski_type = params[:ski][:ski_type].reject(&:blank?)
gender = params[:ski][:gender].reject(&:blank?)
company = params[:brand][:company].reject(&:blank?)
name = params[:ski][:name].reject(&:blank?)
price_range = params[:price_range][:price_range]
model_year = params[:ski][:model_year].reject(&:blank?)
# raise ski_type.any?.inspect
#ski = Ski.new
#ski.ski_type = ski_type
#ski.gender = gender
#ski.name = name
#ski.model_year = model_year
#price_range = PriceRange.new
#price_range.price_range = price_range
#brand = Brand.new
#brand.company = company
skis = Inventory.search_price(price_range)
skis_refined = Ski.search_characteristics(ski_type, gender, company, name, model_year)
ski_ids2 = skis.map(&:id) & skis_refined.map(&:id)
#all_skis = Ski.where(:id => ski_ids2)
if params[:sort_by] == "Price Low to High"
#overlapping_skis = Kaminari.paginate_array(#all_skis.joins(:inventories).order("inventories.price ASC").uniq_by(&:id)).page(params[:page]).per(30)
elsif params[:sort_by] == "Price High to Low"
#overlapping_skis = Kaminari.paginate_array(#all_skis.joins(:inventories).order("inventories.price DESC").uniq_by(&:id)).page(params[:page]).per(30)
elsif params[:sort_by] == "Rating"
#overlapping_skis = Kaminari.paginate_array(#all_skis.joins(:reviews).order("reviews.average_review DESC").uniq_by(&:id)).page(params[:page]).per(30)
else
#overlapping_skis = #all_skis.joins(:brand).order("brands.company ASC, skis.model_year DESC, skis.name ASC").page(params[:page])
end
else
#overlapping_skis = Ski.joins(:brand).order("brands.company ASC, skis.model_year DESC, skis.name ASC").page(params[:page])
end
respond_to do |format|
format.html # index.html.erb
format.json { render json: #skis }
end
end
Updated for search method:
def self.search_characteristics(ski_type, gender, company, name, model_year)
#skis = Ski.scoped
if ski_type.any?
#skis = #skis.where(:ski_type => ski_type)
end
if gender.any?
#skis = #skis.where(:gender => gender)
end
if company.any?
brand_object = Brand.where(:company => company)
#id_array = brand_object.map(&:id)
#skis = #skis.where(:brand_id => #id_array)
end
if name.any?
#skis = #skis.where(:name => name)
end
if model_year.any?
#skis = #skis.where(:model_year => model_year)
end
return #skis
end
You have :multiple => true, meaning there can be multiple selected values for model_year, which is why it's an array. You'll have to handle those multiple values some way or remove :multiple => true.
I have a simple search action that has 3 parameters and a where method to search a model. If I search and some of the parameters are nil, it will not return the records I want. I want it to search the database by only using the parameters that are not nil/blank. So if only one category is entered and sent in the parameters, I want my controller to ignore the other two parameters. However, if the other parameters are present, I want them to be included in the search.
I've tried many approaches but I can't get it to work properly. Here's my code.
hash = []
cat = :category_id => params[:category_id]
col = :color_id => params[:color_id]
brand = :brand_id => params[:brand_id]
if params[:category_id].present?
hash += cat
end
if params[:color_id].present?
hash += col
end
if params[:brand_id].present?
hash += brand
end
#results = Piece.where(hash).preload(:item).preload(:user).group(:item_id).paginate(:page => params[:page], :per_page => 9)
I've put the variables into strings and hashs, called to_a, joined them with (","). Nothing works.
Thanks
Try this code.
criteria = { :category_id => params[:category_id], :color_id => params[:color_id],
:brand_id => params[:brand_id] }.select { |key,value| value.present? }
#results = Piece.where(criteria).preload(:item).preload(:user).group(:item_id).
paginate(:page => params[:page], :per_page => 9)
So I am building an application that matches users. User models have 3 attributes (that are relevant to my question anyways: gender:string, looking_for_men:boolean, looking_for_women:boolean.
currently I've got a method in my model like so:
def browse
if self.looking_for_men == true && self.looking_for_women == true
if self.sex == "Male"
User.where("looking_for_men = ?", true)
elsif self.sex == "Female"
User.where("looking_for_women = ?", true)
end
elsif self.sex == "Male" && self.looking_for_men == true
User.where("looking_for_men = ? AND sex = ?", true, "Male")
elsif self.sex == "Female" && self.looking_for_women == true
User.where("looking_for_women = ? AND sex = ?", true, "Female")
else
if self.sex == "Male"
User.where("looking_for_men = ? AND sex = ?", true, "Female")
elsif self.sex == "Female"
User.where("looking_for_women = ? AND sex = ?", true, "Male")
end
end
end
This is pretty messy, as you can tell. Is there anyway to clean this up and make it into a scope, so that say for example I am a male user, and I am looking for women that it returns only women who are looking for men when I do a query like so:
#users = User.all.browse
I would just do the code below, to make it more readable. But somehow,I'm not totally comfortable with this solution. Still lot of code:
class User < ActiveRecord::Base
scope :male, where(:gender => "Male")
scope :female, where(:gender => "Female")
scope :looking_for_men, where(:looking_for_men => true)
scope :looking_for_women, where(:looking_for_women => true)
def browse
#women = #men = []
#women = self.interested_females if self.looking_for_women
#men = self.interested_males if self.looking_for_men
#result = #women.concat(#men)
#result.delete(self) #removes the user itself from the result-set
return #result
end
def interested_females
return User.female.looking_for_men if self.male?
return User.female.looking_for_women if self.female?
end
def interested_males
return User.male.looking_for_men if self.male?
return User.male.looking_for_women if self.female?
end
def male?
return (self.gender == "Male")
end
def female?
return (self.gender == "Female")
end
end
Just from a scope point of view, you could move that logic into a scope fairly easily just by passing it to a proc.
class User
scope :browse_for, lambda { |user|
user.looking_for_men == true && user.looking_for_women == true
...
}
end
#users = User.browse_for(#single_male)
and you could also chain scopes together to clean up the logic: http://edgerails.info/articles/what-s-new-in-edge-rails/2010/02/23/the-skinny-on-scopes-formerly-named-scope/index.html.
I'm not sure if that quite answers your question?