How can I search by a range of dates in Rails? - ruby-on-rails

I've written a Rails controller function that queries a MySQL database looking for entries that were added within a certain date range. Something like the following, for instance:
#foos = FooTableModel.where(created > ? AND created <= ?', DateTime.new(2011, 1, 26), DateTime.new(2011, 1, 29))
This query works great for my purposes. What I would like to do, however, is to allow the user to set their desired date range rather than have it explicitly set. I've poked around with erb files somewhat and I've had great success accessing the data, I'm exclusively interested in altering what data I call for to display.
Are there any examples that are similar to what I'm trying to achieve? I assume this is a fairly common thing Rails developers would like to enable their users to do but I haven't had much luck with leads.

Where is your form code?
Here is a standard REST form:
<% form_tag(foo_path) do %>
<% select_date :start_date %>
<% select_date :end_date %>
<%= submit_tag "Submit", :disable_with => "Submitting..." %>
<% end %>
Then in your controller you can use the parameters to search by those dates.
start_date = params[:start_date]
end_date = params[:end_date]
Keep in mind foo_path needs to be restful route. If it is not then you need to pass a hash {:controller => 'foo', :action => "find"}
In your controller you can do something like this:
def index
if params[:start_date] && params[:end_date]
start_date = params[:start_date]
end_date = params[:end_date]
#foos = Foo.find(:all, :conditions => ['created_at BETWEEN ? AND ?,' start_date, end_date
end
responds_to do |format|
format.html
end
end

Related

Ruby on Rails - Search through records with multiple attributes

I have written some code to search via a couple of attributes in all my Recipe records. The code works, but I would like some input on if it's ok, or how to make it better/faster.
I have a Recipe model with various attributes including name:string and ingredients:[array of integers] (postgres database). The ingredients are the ID's of a separate model Ingredient. This is a learning experience, I don't want to use any gems.
My form
index.html.erb
<%= form_tag recipes_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= collection_select( :ingredients, :ingredient_ids, Ingredient.all, :id, :name, {:include_blank => false, include_hidden: false}, { :multiple => true } ) -%>
<%= submit_tag "Search" %>
</p>
<% end %>
recipes_controller.rb
def index
#recipes = Recipe.search(params[:search], params[:ingredients])
end
recipe.rb
def self.search(search, ids)
array = []
if search && !search.empty?
meals = where('name ILIKE ?', "%#{search}%")
meals.each do |meal|
array.push(meal)
end
if ids && !ids.empty?
ingredients(array, ids)
else
return array
end
elsif ids && !ids.empty?
ingredients(all, ids)
else
all
end
end
def self.ingredients(meals, ids)
newarray = []
if ids
meals.each do |me|
a = me.ingredients
b = ids[:ingredient_ids].map(&:to_i)
if (b - a).empty?
newarray.push(me)
end
end
return newarray
else
return meals
end
end
This works fine at the moment as I don't have many records, but I don't trust that it'll be very fast if I had hundreds or thousands of records. Any advice on improving things?
If you know you're going to be searching on one or more columns frequently, try adding a database-level index for those columns. Without an index, any search will be O(n) time where n is the number of records. However, if you use an index, a search will be O(log(n)) time, because with the data ordered by your search column, you can binary search through it.
Take a look at http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/ for more information at how to check if your query performance can be improved.
Two other things to consider performance-wise:
1) For your all condition, you might be returning waaaay more records than you want. You may want to consider using pagination (I know you mentioned no other gems, but there are some great gems for pagination out there).
2) If you do actually want to return a ton of records at once, consider using ActiveRecord's batching (I usually use #find_each) to ensure you don't load everything into memory at once and end up OOM-ing.

Searching for two arguments Ruby on Rails

I am working on a search with two text fields. It searches for two things (right now location and description) and only shows the entries (listings) that match both of them.
I would like the second text field to also search for title, so it should look for description and title. How would that work?
This is what I have right now
listing.rb
def self.locsearch(search_location, search_description)
return scoped unless search_location.present? || search_description.present?
where(['location LIKE? AND description LIKE?', "%#{search_location}%", "%#{search_description}%"])
end
home.html.erb
<%= form_tag findjobs_path, :controller => 'listings', :action => 'locsearch', method: :get do %>
<%= text_field_tag :location, params[:location] %>
<%= text_field_tag :descripiton, params[:descripiton] %>
<%= submit_tag "Search", name: nil %>
<% end %
listings_controller.rb
def index
#listings = #listings.locsearch(params[:location], params[:description])
end
Also, my locsearch method right now uses the or || condition. How would I implement the "and" condition? (If I change the || to && I get the error" undefined method or variable scroped)
Are you sure you want to use unless
unless is used only if you want to execute a specified code if the condition(s) is false.
And scoped is used along with a model.
Model.scoped
You can refer the Apidock or Github
You can write return scoped only if u have defined scoped as a local variable or a method.
You can also see scope for databases

combine two search results ruby on rails

I am trying to have two search fields, that however end up combining the two results.
In one filed you can search for the location of a listing and in the other for a keyword, which will look for title and description of the listing.
When a location and keyword is entered, listings that match both the location and keyword should show. So if I enter San Francisco and Retail, only listings located in SF and with the description or title Retail should pop up.
Feng Chen suggested to do this in one query by the following:
self.where("location like ? and (title like ? or description like ?)", query, query)
However this does not show a result that matches both the location and the keyword(title, description).
Do I need to change something in my view or anywhere else?
Here is what I have listing.rb
def self.locsearch(search)
query = "%#{search}%"
if search
self.where("location like ? and (title like ? or description like ?)", query, query)
else
self.all
end
end
end
Static_pages_controller.rb
def home
#listings = #listings.locsearch(params[:search]) if params[:search].present?
home.html.erb
<%= form_tag findjobs_path, :controller => 'listings', :action => 'locsearch', :method => 'get' do %>
<p>
<%= text_field_tag :search, "location" %>
<%= text_field_tag :search, "keyword" %>
<%= submit_tag "search" %>
</p>
</div>
<% end %>
You need a descsearch method in your listing model and you need to do
# Right now you have #listings = #listings.locsearch(...)
# You need #listings = Listing.locsearch(...)
#listings = Listing.locsearch(params[:search][:location], params[:search][:keyword])
And in your listing model
def self.locsearch(location, keyword)
location = "%#{location}%"
keyword = "%#{keyword}%"
if !location.bllank? && !keyword.blank?
self.where("location like ? and (title like ? or description like ?)", location, keyword)
else
self.all
end
end
end
If you are using a postgres database, you could try to use the full text search feature to do the above. There is gem pg_search enter link description herewhich will allow you to do so easily. The overhead incurred would be trivial and would provide you greater flexibility in this search problem and any such other ones you might have to solve later.

Rails 3 - Loop through to get a collection?

I'm implementing search and am having some difficulties with my sql/finding.
Basically, I have a favorites page, that gets a collection of favorites with:
#favorites = current_user.votes
In the view, I then loop through my favorites, and can call .voteable on them to get the actual object that was voted on. This is making search very difficult for me to write.
I was wondering if it was possible to change my original collection, so that I'm actually getting the .voteable objects each time to dry up my view/help me write my search. I cannot called current_user.votes.voteables but individually can do something like current_user.votes.first.voteable
What I've attempted is a loop like so:
#favorites = current_user.votes.each {|vote| vote.voteable }
Which is wrong, and I'm just getting my votes again, and not the actual voteable object. I was just wondering if there was a way to get these voteables from looping through my votes like this.
Any pointers would help, thanks.
EDIT:
Expansion what I mean by search:
I'm building a method in the model that searches self, here is an example:
def self.search(search)
if search
where('title LIKE ?', "%#{search}%")
else
scoped
end
end
I pass in search from the view, with a form like:
<div id="search_form">
<%= form_tag topic_links_path, :method => 'get', :id => "links_search" do %>
<%= text_field_tag :search, params[:search], :size => "35" %>
<%= submit_tag "Search", :name => nil %>
<% end %>
</div>
That way, when I make my collection in the controller, I can call .search(params[:search]) to get only the items that are like whatever the user entered in the form. I'm using the vote_fu gem for handling the votes/voteables.
You need to use map instead of each:
#favorites = current_user.votes.map {|vote| vote.voteable }
each simply loops through the elements and performs the operation on them, but it doesn't return the result in an array format. That's what map does.
On a side note, you can use a scope for search instead of a function. It might be a little cleaner:
scope :search, lambda{ |title| where('title LIKE ?', "%#{title}%") unless title.blank? }

Using Websolr with Heroku to do full text search

Hi I am having trouble trying to figure out how to implement a search form globally across my application. I have a series of posts that need to be searchable by users that are signed in and not signed in. I have added this code in my post model:
searchable do
text :content, :default_boost => 2
text :body, :default_boost => 1.5
end
However, I do not know where to go from there to create a search field across all pages and make it show the results I need. I am new to rails and would be happy to post more information if someone is willing to help me out.
First, you should add your search field like explained in this railscast: http://railscasts.com/episodes/37-simple-search-form
Since your search isn't specific to a particular model, use a generic controller name instead of ProjectsController though.
Then, you should replace the ActiveRecord finder by the use of the Sunspot DSL.
Here is an sample code to help get you started:
page = #page = params[:page] && params[:page].to_i || 1
#search = Sunspot.search(Realty) do # search_ids
per_page = params[:per_page] && params[:per_page].to_i || 10
# not adapted to your case
with(:equipments).all_of params['equip'].split(' ') if params['equip']
case params[:sort]
when "average_rating"
order_by :average_rating, :desc
when "type"
order_by :type, :asc
end
paginate :page => page, :per_page => per_page
# other criteria...
end
In your view, you can then iterate through #search.results
<%= will_paginate #search.results %>
<% #search.results.each do |hit| %>
<%# 'path' contains the stored polymorphic_path of each model object #%>
<% link_to hit.stored('path') do %>
<p><%= hit.stored('content') %></p>
<% end %>
<% end %>
Last, using WebSolR instead of a standard SolR server is quite simple, you can follow the setup instructions at https://github.com/onemorecloud/websolr-rails.
Edit:
As Nick commented, you should totally go to http://docs.heroku.com/websolr.
Thanks Nick !

Resources