Heroku case sensitivity - ruby-on-rails

I have recently uploaded my application to heroku and due to the postgresql, the case sensitivity does not work, I am wondering how will I fix this? My code is as followed:
relation = Game.gamsearch(params[:gamsearch])
relation = Game.consearch(params[:consearch]) if params[:consearch].present?
relation = Game.gensearch(params[:gensearch]) if params[:gensearch].present?
relation = Game.where("game_name LIKE ?", "#{params[:game_name]}%") if params[:game_name].present?
relation = Game.where("console = ?", params[:console]) if params[:console].present?
view code:
<%= form_tag games_path, :controller => 'games', :action => 'gamsearch', :method => 'get' do %>
<%= text_field_tag :gamsearch, params[:gamsearch] %>
<%= submit_tag t('.searchb'), :game_name => nil %>
<% end %>
This is the model code:
def self.gensearch(*args)
#search for games by their genre
return [] if args.blank?
cond_text, cond_values = [], []
args.each do |str|
next if str.blank?
cond_text << "( %s )" % str.split.map{|w| "genre LIKE ? "}.join(" OR ")
cond_values.concat(str.split.map{|w| "%#{w}%"})
end
all :conditions => [cond_text.join(" AND "), *cond_values]
end

relation = Game.where("game_name ILIKE ?", "#{params[:game_name]}%") if params[:game_name].present?
notice the ILIKE instead of LIKE

ILIKE is the way to go for PG, but if you have another database (sqlite) in development, that might not work. One way to get around this is to make both values uppercase and then compare the uppercased version of both.

Related

Multi find search in ruby with date

I have created a multi find search, where I need to filter records by date / category / title. Searching by a category and/or title works, however, when date is typed it doesn't change anything (the results is the same like there was no date typed). I have no idea what else I could do to fix it, I am just a beginner in Ruby. Any idea?
Model:
def self.multi_find(cat_id, search, date_search)
search_condition = "%" + search + "%"
#date_condition = date_search
# test if cat_id is not blank
if not cat_id.blank?
# assign the value of cat_id to a ‘scope’ named :cat
scope :cat, -> { where('category_id = ?', cat_id) }
# using the ‘scope’ cat find where a search string is like a title or an author’s name
self.cat.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
else
# find where a search string is like a title or an author’s name
self.where("title LIKE ? or event_date = ?", search_condition, date_search.to_date)
end
end
Controller:
def multi_find
# call an Event class method, using two parameters; a category unique identifier and a search string (author or title)
events = Event.multi_find(params[:cat_id], params[:search_string], params[:event_date_search])
# use Kaminari pagination ...
#events = Kaminari.paginate_array(events.order :title).page(params[:page]).per(6)
# if no products have been found
if #events.empty?
# display a notice
flash.now[:alert] = "No events found - so displaying all events"
# then display all products
#events = Event.order(:title).page(params[:page]).per(6)
end
# use the index view
render :action => "index"
end
The console outputs the SQL Query
Event Load (0.0ms) SELECT "events".* FROM "events" WHERE (category_id = '1') AND (title LIKE '%%' or event_date = '2018-02-14') ORDER BY "events"."title" ASC
View file:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('cat_id'),
options_from_collection_for_select(#categories, :id, :cat_name, 0 ),
:prompt => "Select a Category" %>
<div class="datepicker">
<% #event_date_format %>
<%= text_field_tag :event_date_search %>
</div>
<!-- Key word:-->
<%= text_field_tag :search_string %>
<%= submit_tag 'Search' %>
<% end %>
It's because you have an or in your sql statement. However you should also clean up your code a bit.
def self.multi_find(cat_id, search, date_search)
result = self.all
result = result.where(category_id: cat_id) if cat.id.present?
result = result.where('title LIKE ?', "%#{search}%") if search.present?
result = result.where(event_date: date_search) if date_search.present?
result
end

Trying to search by first_name and last_name

I am trying to search a user by both first and last name in my rails app and am currently getting mixed results for each method I try. Is there a way to re-write any of these methods to get my desired results?
user_controller.rb
method #1
def self.search(query)
where("first_name LIKE ? OR last_name LIKE ?", "%#{query}%", "%#{query}%")
end
This works for either first or last name but not both.
Method #2
def self.search(keywords)
if keywords
where(:all, :conditions => ["concat(first_name," ",last_name) like?", "%#{keywords}%"])
end
end
This doesn't return any result
Method#3
def self.search(search)
if search
select('(first_name || " " || last_name) as \'ful_name\', *')
where ['first_name LIKE :s OR last_name LIKE :s OR ful_name LIKE :s', :s => "%#{search}"]
else
scoped
end
end
This returns the error
SQLite3::SQLException: no such column: ful_name: SELECT "users".* FROM "users" WHERE (first_name LIKE '%Spider Man' OR last_name LIKE '%Spider Man' OR ful_name LIKE '%Spider Man') ORDER BY created_at DESC
app/views/users/index.html.erb:5:in `_app_views_users_index_html_erb__848623016_40254132'
index.html.erb
<% provide(:title, 'Search') %>
<h1>Search</h1>
<ul class="span4 users">
<%= render #users %>
</ul>
_user.html.erb
<li>
<%= image_tag user.avatar(:medium) %>
<h4><%= link_to user.full_name, feed_user_path(user), :class => "follow-color" %></h4>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</li>
_header.html.erb
<%= form_tag users_path, method: "get", class: "search-bar" do %>
<%= text_field_tag :search, params[:search], placeholder: "Search" %>
<% end %>
This one:
:conditions => ["concat(first_name," ",last_name) like?", "%#{keywords}%"]
won't work because you have an (insidious) quote problem. In Ruby, this:
"a" "b"
is the same as:
"ab"
so your :conditions is really this:
:conditions => ["concat(first_name,,last_name) like?", "%#{keywords}%"]
You mean to say:
:conditions => ["concat(first_name, ' ', last_name) like ?", "%#{keywords}%"]
String literals in SQL use single quotes, not double quotes. Also, if you're using a database that claims to support standard SQL, you should use the || operator for string concatenation:
:conditions => ["first_name || ' ' || last_name like ?", "%#{keywords}%"]
The third one won't work because aliases defined in a SELECT clause are not generally available in the WHERE clause, hence the "unknown column" error. You're also throwing away the result of the select call so I think you're missing a . in here too:
select('(first_name || " " || last_name) as \'ful_name\', *')
where ['first_name LIKE :s OR last_name LIKE :s OR ful_name LIKE :s', :s => "%#{search}"]
There's also a potential quoting problem: string literals use single quotes in SQL, double quotes are for identifiers. You want to say just:
where("first_name like :s or last_name like :s or first_name || ' ' || last_name like :s", :s => "%#{search}")
or just:
where("first_name || ' ' || last_name like :s", :s => "%#{search}")
A couple caveats:
String concatenation operators are database-specific. Standard SQL uses || but, depending on the configuration, MySQL wants to use the concat function. AFAIK, SQLite supports a lot of MySQL-isms but you need to be aware when you're using them and you should stick to the standard as much as possible.
Quoting is, again, database-specific. Standard SQL uses single quotes for string literals and double quotes for identifiers (such as table and column names). MySQL uses back-ticks for identifiers, SQLite (AFAIK) lets you use double quotes or back-ticks for identifiers and single or double quotes for strings. Again, stick to the standard as much as possible to build good habits.
I wanted to do the same, i followed this good tuto : https://www.youtube.com/watch?v=s88Uc0InOAM
Then i had the same problem u have, i wanted to search in both name and surname.
Here it's what i did and it's working :
#indiens = Indien.where("name LIKE ?", "%" + params[:q] + "%" ).or(Indien.where("surname LIKE ?", "%" + params[:q] + "%" ))
It may be possible to chain more, but i didn't test.
I know it has been 5 year, maybe it will help others.

Ruby/Rails - Group By performance

I have a collection of Episodes which is connected to a Season and a Show.
I need to display them as such:
Show title
....Season number 1
........Episode name
........Episode name
....Season number 2
........Episode name
........Episode name
My controller:
def index
#show_ids = Following.find_all_by_user_id(current_user.id).collect(&:show_id)
#seen_ids = SeenEpisode.find_all_by_user_id(current_user.id).collect(&:episode_id)
if #seen_ids.any?
#episodes = Episode.find(:all, :conditions => ["show_id IN (?) AND episodes.id NOT IN (?)", #show_ids, #seen_ids], :joins => [:show, :season])
else
#episodes = Episode.find(:all, :conditions => ["show_id IN (?)", #show_ids], :joins => [:show, :season])
end
end
My view:
<ul>
<% #episodes.group_by(&:show).each do |show, episodes| %>
<li><h2><%= show.name %></h2></li>
<% episodes.group_by(&:season).each do |season, episodes| %>
<li><strong><%= season.number %></strong></li>
<% episodes.each do |episode| %>
<li><%= episode.name %></li>
<% end %>
<% end %>
<% end %>
</ul>
This works fine, although I know this is not a good method, and the performance is SHIT (like 10 seconds for about 150 records). How can I have a grouping like this with good performance?
Also, how can I refactor this?
if #seen_ids.any?
#episodes = Episode.find(:all, :conditions => ["show_id IN (?) AND episodes.id NOT IN (?)", #show_ids, #seen_ids], :joins => [:show, :season])
else
#episodes = Episode.find(:all, :conditions => ["show_id IN (?)", #show_ids], :joins => [:show, :season])
end
First, make sure your database has indexes on any foreign key columns you're querying against (I generally index anything_id as a matter of course:
add_index :episodes, :show_id
add_index :followings, :user_id
To clean up your finds, try something like this (from your updated post):
#episodes = Episode.scoped(
:conditions => ["show_id IN (?)", #show_ids],
:include => :show )
if #seen_ids.present?
#episodes = #episodes.scoped(
:conditions => "seen_episodes.show_id IS NULL",
:joins => :seen_episodes )
end
The above assumes you're using Rails 2 (since you were using the .find(:all) syntax...) but you can clean that up further by using .where, etc. instead of .scoped if you're on Rails 3.
using the "NOT IN" clause is generally slow. Instead left join on the SeenEpisode table and add a condition where SeenEpisode is NULL
Episode.find(:all, :joins => "LEFT JOIN SeenEpisode ON SeenEpisode.show_id = Episode.show_id", :conditions => "SeenEpisode.show_id IS NULL")
Note that I omitted some of the clauses for clarity. What this does is keep all records from Episode and add in columns from SeenEpisode that match. The condition then takes out those matching records.
I noticed that my db got queried Select * from shows Where Id = 100 for each record in the loop (show.name). I'm guessing the join did not work because of ambiguous named columns (episodes.name and shows.name)
This is what I ended up with.
query:
#episodes = Episode.find(:all, :select => "episodes.*, shows.name AS show_name", :conditions => ["show_id IN (?) AND seen_episodes.episode_id IS NULL", #show_ids], :joins => "INNER JOIN shows ON shows.id = episodes.show_id LEFT JOIN seen_episodes ON seen_episodes.episode_id = episodes.id")
view:
<ul>
<% #episodes.group_by(&:show_name).each do |show_name, episodes| %>
<li><h2><%= show_name %></h2></li>
<% episodes.group_by(&:season_number).each do |season_number, episodes| %>
<li><strong><%= season_number %></strong></li>
<% episodes.each do |episode| %>
<li><%= episode.name %></li>
<% end %>
<% end %>
<% end %>
</ul>
Also, I already had the season_number in a "cache column" on each episode.
I think this is OK. The query is not very pretty, but at least I like the result:
Completed in 109ms (View: 31, DB: 15)

How to make optional :conditions for a find

Hello I have the followong struggle in my head. I want a text-field in which the use can type in some parameters, which will be used as filter-criteria for the :conditions hash in my find method.
I have created a helper, with takes an option and merge the hash to the options:
In my controller:
#bills = adminbill_filter(:limit=>params[:limit] || 50,:offset=>params[:offset] || 0, :conditions=>params[:options])
In my helper:
def link_to_with_current(text, link, condition, *args)
options = args.first || {}
options[:class] = condition ? 'current' : nil
link_to text, link, options
end
In my view:
<%= text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= button_to_with_filter 'Start Filter', 'index', :filter_condition=>true, :options=>{:id=>81}%>
Is it somehow possible to pass the value of text_field into the :option=>{...} of the button_to_with_filter? I find this solution (if it is working) quite unhandy. Your comments are as always very helpful.
Greetings
Matthias
It seems kind of terrifying to put in the contents of user-submitted params without vetting them in any capacity. You're probably going to run into all kinds of exceptions if the data doesn't come in as expected, or is formulated to be malicious.
I've found it's often easier to use a chained scopes approach:
def index
bills_scope = Bill
# Use an example Bill.with_id scope
if (params[:with_id])
bills_scope = bills_scope.with_id(params[:with_id])
end
# Repeat as required
# Finally, use the scope to retrieve matching records
#bills = bills_scope.paginated
end
Using something like will_paginate can help with your offset and limit values.
If the text field and button were encapsulated in a form, and the button was the submit button, the text field's value would automatically be brought into the params hash. Then you wouldn't have to deal with it. I can't recall at the moment the exact Rails helpers that will do this for you, but you want the resulting form to probably be something like this:
<% form_for :options, :url => {:action => :index}, :html => { :method => :get } do |f| %>
<%= f.text_field :filter ,:criteria, :class=>'roundRect',:id=>'name', :value=>12009%>
<%= f.submit 'Start Filter' %>
<% end %>
Which may change some, since I don't know the underlying code behind your methods.
Otherwise, the only thing I can think of is using a Javascript event on the button that grabs the value of the text field before it submits.
Thanks for your help, I came across named_scope and solved the problem with the following code:
Bill model:
class Bill < ActiveRecord::Base
# named_scope werden fuer Filterfunktionen bei Adminbill benoetigt
named_scope :standard, :order => "created_at DESC"
named_scope :limit, lambda {|*args| {:limit=>(args.first)|| 50}}
named_scope :offset, lambda {|*args| {:offset=>(args.first || 10)}}
named_scope :paid, :conditions=>"paid IS NOT NULL"
named_scope :not_paid, :conditions=>{:paid=>nil}
named_scope :test_bill, :conditions => {:test_bill=>true}
named_scope :no_test_bill, :conditions => {:test_bill=>false}
named_scope :find_via_bill_id, lambda {|*args|{:conditions=>{:id=>(args.first || 210)}}}
named_scope :find_via_email, lambda {|*args| {:conditions=>{:buyer_id=>args.first}}}
controller:
def index
logger.debug "The object is #{current_user}"
if params[:filterInput] != nil && !params[:filterInput].empty?
filter_array = params[:filterInput].split('&')
bill_scope = Bill.scoped({})
bill_scope = bill_scope.standard
# Filtere via Regexp-Matching die Zahlen der Eingabe heraus
filter_array.each do |el|
if el =~ /limit\([0-9]+\)/
number =
bill_scope = bill_scope.limit(el.scan(/\d+/)[0])
elsif el =~ /offset\([0-9]+\)/
bill_scope = bill_scope.offset(el.scan(/\d+/)[0])
elsif el == 'paid'
bill_scope = bill_scope.paid
elsif el == 'not_paid'
bill_scope = bill_scope.not_paid
elsif el == 'test_bill'
bill_scope = bill_scope.test_bill
elsif el =~ /find_via_bill_id\([0-9]+\)/
bill_scope = bill_scope.find_via_bill_id(el.scan(/\d+/)[0])
elsif el =~ /find_via_email\([A-Za-z0-9.#-]+\)/
email = el.scan(/\([A-Za-z0-9.#-]+\)/)[0]
# TODO geht bestimmt auch eleganter durch besseres Matching
email = email.gsub("(", "")
email = email.gsub(")", "")
user = User.find_by_email(email) unless User.find_by_email(email).blank?
bill_scope = bill_scope.find_via_email(user.id)
end
end
#bills = bill_scope
else
#bills = Bill.standard.limit.offset
end
And in the view:
<% form_tag(:action => 'index') do %>
<%= text_field_tag 'filterInput', nil, :size => 40 %>
<%= submit_tag 'Start Filter'%>
<% end %>
Now you can pass in the tex-field e.g.the following valid expression: paid&limits(20)
I know that the controller solution isn't very elegant but for me it was the fastest way to solve this problem.

Searching in Ruby on Rails - How do I search on each word entered and not the exact string?

I have built a blog application w/ ruby on rails and I am trying to implement a search feature. The blog application allows for users to tag posts. The tags are created in their own table and belong_to :post. When a tag is created, so is a record in the tag table where the name of the tag is tag_name and associated by post_id. Tags are strings.
I am trying to allow a user to search for any word tag_name in any order. Here is what I mean. Lets say a particular post has a tag that is 'ruby code controller'. In my current search feature, that tag will be found if the user searches for 'ruby', 'ruby code', or 'ruby code controller'. It will not be found if the user types in 'ruby controller'.
Essentially what I am saying is that I would like each word entered in the search to be searched for, not necessarily the 'string' that is entered into the search.
I have been experimenting with providing multiple textfields to allow the user to type in multiple words, and also have been playing around with the code below, but can't seem to accomplish the above. I am new to ruby and rails so sorry if this is an obvious question and prior to installing a gem or plugin I thought I would check to see if there was a simple fix. Here is my code:
View: /views/tags/index.html.erb
<% form_tag tags_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search], :class => "textfield-search" %>
<%= submit_tag "Search", :name => nil, :class => "search-button" %>
</p>
<% end %>
TagsController
def index
#tags = Tag.search(params[:search]).paginate :page => params[:page], :per_page => 5
#tagsearch = Tag.search(params[:search])
#tag_counts = Tag.count(:group => :tag_name,
:order => 'count_all DESC', :limit => 100)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #tags }
end
end
Tag Model
class Tag < ActiveRecord::Base
belongs_to :post
validates_length_of :tag_name, :maximum=>42
validates_presence_of :tag_name
def self.search(search)
if search
find(:all, :order => "created_at DESC", :conditions => ['tag_name LIKE ?', "%#{search}%"])
else
find(:all, :order => "created_at DESC")
end
end
end
If I read your problem correctly, you want to return a row if the tag names for the row matches one of the words passed in the query string.
You can rewrite your search method as follows:
def self.search(search)
all :conditions => (search ? { :tag_name => search.split} : [])
end
If you need partial matching then do the following:
def self.search(str)
return [] if str.blank?
cond_text = str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values = str.split.map{|w| "%#{w}%"}
all(:conditions => (str ? [cond_text, *cond_values] : []))
end
Edit 1
If you want pass multiple search strings then:
def self.search(*args)
return [] if args.blank?
cond_text, cond_values = [], []
args.each do |str|
next if str.blank?
cond_text << "( %s )" % str.split.map{|w| "tag_name LIKE ? "}.join(" OR ")
cond_values.concat(str.split.map{|w| "%#{w}%"})
end
all :conditions => [cond_text.join(" AND "), *cond_values]
end
Now you can make calls such as:
Tag.search("Ruby On Rails")
Tag.search("Ruby On Rails", "Houston")
Tag.search("Ruby On Rails", "Houston", "TX")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah")
Tag.search("Ruby On Rails", "Houston", "TX", "Blah", ....) # n parameters
Caveat:
The wild card LIKE searches are not very efficient(as they don't use the index). You should consider using Sphinx (via ThinkingSphinx) OR Solr(via SunSpot) if you have lot of data.
You can try to set up ferret, or if you are really bend on just using rails, try this:
# Break the search string into words
words = params[:search].blank? ? [] : params[:search].split(' ')
conditions = [[]] # Why this way? You'll know soon
words.each do |word|
conditions[0] << ["tag_name LIKE ?"]
conditions << "%#{word}%"
end
conditions[0] = conditions.first.join(" OR ") # Converts condition string to include " OR " easily ;-)
# Proceed to find using `:conditions => conditions` in your find
hope this helps =)
Sounds like you need a full text search. The best search integration right now is with Sphinx and the Thinking_Sphinx plugin. I have used it on several projects and it's super easy to setup.
You do need to install sphinx on your host so if you are using a shared host that could present some issues.
You could also use full text search in a MyISAM MySQL database, but performance on that is pretty poor.
Once you have your sphinx installed you just put what you want to index in your model and call model.search. The results will be a list of model objects. It supports will_paginate as well.
I'd suggest looking at Searchlogic if you don't want to use a separate fulltext search engine (Ferret, Sphinx, etc). It makes simple searches extremely easy, although you may not want to use it in a public facing area without lots of testing.
Also check out the Railscast on it: http://railscasts.com/episodes/176-searchlogic
1.You can do some coding in your controller post as such:-
<pre>
def show
#post = Post.find(params[:id])
#tag_counts = Tag.count(:group => :name, :order => 'updated_at DESC', :limit => 10)
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
</pre>
2.Now make some changes in your view file:-
<pre>
<b>Tags:</b>
<%= join_tags(#post) %>
<%unless #tag_counts.nil?%>
<% #tag_counts.each do |tag_name, tag_count| %>
<tr><td><%= link_to(tag_name, posts_path(:name => tag_name)) %></td>
<td>(<%=tag_count%>)</td>
</tr><% end %>
<%end%>
</pre>
3. And one important thing is that there should be many to many relationship between tags and post.

Resources