Simple Search and Rails - ruby-on-rails

I have a simple search in Rails:
def self.search(search)
# if search is not empty
if search
find(:all, :conditions => ["name LIKE ?", "%#{search}%"])
# if search is empty return all
else
find(:all)
end
The view:
<% if #registries.empty? %>
Can't find the registry. Please try a differnet name.
<% else %>
<% #registries.each do |registry| %>
......etc.
How can I code it to show "Nothing Found" instead of find(:all) if it could not find a query?
I tries a few things, but nothing works. Even if I take out the else it still shows all queries if it can't find the one searching for.
Thanks in advance

When you mean "not empty" you must test against that specifically. Remember in Ruby that there are only two values that evaluate as false: false and nil.
You probably intended:
if (search.present?)
where("name like ?", "%#{search}%").all
else
all
end
Use of the Rails 3 style where clause makes your methods a lot easier to understand. Using find with :conditions is the old Rails 1 and 2 style.

Related

Ruby on Rails - Implementing Simple Search with scopes

I followed the Railscasts #37, very interesting. I tried to apply it to a search that already implies a scope and pagination. But it failed unsuccessfully. Being quite new at rails, I wonder if a better solution exists.
Here is my original controller, that works:
def index
#business_rules = BusinessRule.pgnd(current_playground).order("hierarchy ASC").paginate(page: params[:page], :per_page => paginate_lines)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #business_rules }
end
end
But trying to add the search function as defined in the RailsCast fails:
#business_rules = BusinessRule.pgnd(current_playground).search(params[:search]).order("hierarchy ASC").paginate(page: params[:page], :per_page => paginate_lines)
Error message is: undefined method order for #<Array:0x007fbf8cc62ca0>
Does your search function look like this, as in the railscast?
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
If so, I'm guessing your getting back an array of results, which cannot be chained with additional query methods.
So when you use the query methods (where, order, limit, etc), it returns an ActiveRelation object, which is basically a proxy for your eventual result set. It won't actually hit your DB until you try to use the result set by calling .all, .first, .each, something like that.
You could write your search method like this instead:
def self.search(search)
if search
where('name LIKE ?', "%#{search}%")
end
end
So if search is present, you'll scope your AR object down to the matching results. Otherwise you won't, which will have the same result as returning everything. Then you can still order those results, limit them, paginate them, whatever.
The final script for the search function created in the controller is:
def self.search(search)
if not search.empty?
where('name like ?', "%#{search}%")
else
where('1=1')
end
end
Then it is possible to cahin the query methods as this:
#business_rules=BusinessRule.pgnd(current_playground).search(params[:search]).limit(10)
For more explanations, please refer to RailsCast #37, and note that the syntax for inserting a from_tag is <%= form_tag ...
Thanks to all for your help,
Best regards,
Fred

How to use sunspot_rails gem to search for related articles

I have a mini blog app and i would like user to view articles that relates to what they are reading in the article show page. without the sunspot_rails gem i would do something like this
in my model
def self.related_search(query, join = "AND")
find(:all, :conditions => related_search_conditions(query, join))
end
def self.related_search_conditions(query, join)
query.split(/\s+/).map do |word|
'(' + %w[name description notes].map { |col| "#{col} LIKE #{sanitize('%' + word.to_s + '%')}" }.join(' OR ') + ')'
end.join(" #{join} ")
end
then in my view it would be like this
#article.related_search
but i want to use the sunspot_rails gem to make this way easy. Any help. Thanks
As RocketR mentions, this is a trivial use case for Sunspot.
First, use Sunspot to specify that you have three fields to be indexed as text.
class Article < ActiveRecord::Base
searchable do
text :name
text :description
text :notes
end
end
Then issue a search, likely from within a controller action. The #search object below contains metadata about the search response, including the matching objects under its results method.
#search = Article.search do
keywords query
end
#results = #search.results
To find other documents that are similar to an object you already have loaded, say in a show action, you can call the more_like_this instance method. This is a special kind of search, which uses Solr's "More Like This" functionality, and which returns a search object similar to the above full-text search. You can use its results method to render the results of that search.
<%= render #article.more_like_this.results %>
The more_like_this method also accepts a block with similar options to the search block, so you can have more control over how you're judging similarity.
Hope that helps!

How can I convert that to MetaWhere or Arel?

Consider a City Model having:
def self.search(field, search)
if search
where("#{field} LIKE ?", "%#{search}%")
else
scoped
end
end
How can I use Arel or Metawhere in that situation knowing that field is a String can have anything like:
"name"
"residents.name"
"state.name"
I want to do something like that (will not work):
def self.search(field, search)
if search
where(field =~ "%#{search}%")
else
scoped
end
end
So, what are your thoughts?
The real question is, how can I convert that:
"residents.name LIKE '#{value}%'"
To that:
:residents => { :name =~ "#{value}%" }
You should be able to use Arel like this.
def self.search(field, search)
if search
if field =~ /\./ # table and field
table, field = field.split('.')
arel_join = table.singularize.camelize.constantize.arel_table
joins(table.to_sym).where(arel_join[field].matches("%#{search}%"))
else
where(Resource.arel_table[field].matches("%#{search}%"))
end
else
scoped
end
end
There's a Railscast that does a good job of explaining the basics of using Arel in Rails 3.

Rails doing a FIND with Conditions?

In Rails 3, I created a Search Form that does a FIND with conditions in the Models file.
#projects = find(:all,
:select => 'projects.*',
:conditions => ['name = ?', search_name]
).first
This works great if a name is provided in the searchform (search_name). Problem is if search_name is blank, Rails Errors (can't say I blame it)...
What is the smart way to handle this situation? I'd like, if search_name is blank, to not error but return everything.
Suggestions? Thanks!
You can create a scope to handle this. In your Project model, add something like:
scope :search_by(name), lambda{|name| first.where(:name => name) unless name.blank?}
then in your controller, simply call:
Project.search_by(params[:search])
EDIT:
If you need to serach for multiple fields you can adapt the scope:
scope :search_by(name), lambda{|name| first.includes(:owner).where("projects.name LIKE ? OR owners.name LIKE ?", name, name) unless name.blank?}
if search_name.blank?
#projects = Project.order(:name)
else
#projects = Project.where(:name => search_name)
end
The cleanest way is using lazy loading with the new ActiveRecord functionalities like this:
#projects = Project.order(:name)
#projects = #projects.where(:name => search_name) if search_name
You can add as many conditions as you like this way. They won't be executed until you need the results anyway (with #projects.all or #projects.each, etc...)

DRYing Search Logic in Rails

I am using search logic to filter results on company listing page. The user is able to specify any number of parameters using a variety of named URLs. For example:
/location/mexico
/sector/technology
/sector/financial/location/argentina
Results in the following respectively:
params[:location] == 'mexico'
params[:sector] == 'technology'
params[:sector] == 'financial' and params[:location] == 'argentina'
I am now trying to cleanup or 'DRY' my model code. Currently I have:
def self.search(params)
...
if params[:location]
results = results.location_permalink_equals params[:location] if results
results = Company.location_permalink_equals params[:location] unless results
end
if params[:sector]
results = results.location_permalink_equals params[:sector] if results
results = Company.location_permalink_equals params[:sector] unless results
end
...
end
I don't like repeating the searchs. Any suggestions? Thanks.
This is how I would write it:
[params[:location], params[:sector]].reject(&:nil?).each do |q|
results = (results ? results : Company).location_permalink_equals q
end
There's plenty of other ways, just an idea. Has the benefit of making it easy to add say params[:street] or something.
I don't think you can really DRY that up much when sticking to SearchLogic... I'd suggest to refine your routes to directly emit *_permalink as parameter names and do something like this:
Company.all :conditions => params.slice(:location_permalink, :sector_permalink)
or
Company.find :all, :conditions => params.slice(:location_permalink, :sector_permalink)
Documentation link: http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Slice.html

Resources