Configuring rails database query so that blank string parameters are ignored - sql

I'm making a rails application so that users can search a database of midi records and find midi files that correspond to the attributes that I've given them.
For example, a user might enter data into an html form for a midi file with name = "blah" composer= "buh" and difficulty = "insane".
This is all fine and well, except that I would like when the user enters no data for a field, that field is ignored when doing the select statement on the database.
Right now this is what my select statement looks like:
#midis=Midi.where(:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty])
This works as expected, but if for example he/she leaves :composer blank, the composer field should not considered at all. This is probably a simple syntax thing but i wasn't able to find any pages on it.
Thanks very much!

Not sure if Arel supports that directly, but you could always do something like:
conditions = {
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer=> params[:midi][:composer],
:difficulty => params[:midi[:difficulty]
}
#midis=Midi.where(conditions.select{|k,v| v.present?})

Try this:
# Select the key/value pairs which are actually set and then convert the array back to Hash
c = Hash[{
:name => params[:midi][:name],
:style => params[:midi][:style],
:numparts => params[:midi][:numparts],
:composer => params[:midi][:composer],
:difficulty => params[:midi][:difficulty]
}.select{|k, v| v.present?}]
Midi.where(c)

Related

phpbb 3.2.x user_add including custom Profile field

This has been driving me nuts for 2 days and I can't find an answer anywhere on Google so would really appreciate a little help..
I have a custom registration form on my website, which sends the data to a fairly standard PHPBB3 user_add process as follows:
$user_row = array(
'username' => request_var('createUsername',''),
'user_password' => phpbb_hash(request_var('createPassword','')),
'user_email' => request_var('createEmail',''),
'group_id' => '2',
'user_timezone' => '1.00',
// 'user_dst' => '0',
'user_lang' => 'en',
'user_type' => $user_type,
'user_actkey' => $user_actkey,
'user_ip' => $user->ip,
'user_regdate' => time(),
'user_inactive_reason' => $user_inactive_reason,
'user_inactive_time' => $user_inactive_time,
);
// Register user...
$user_id = user_add($user_row, $cp_data);
// If creating the user failed, display an error
if ($user_id === false)
{
trigger_error('NO_USER', E_USER_ERROR);
}
That works fine and I'm happy with it, however, I have created a custom profile field in the Admin Control Panel called 'ea_real_name' which I want to hold the user's real name. This corresponds to a field on the registration form called 'createRealName' (sent through as $_POST['createRealName'])
I know that user_add takes an optional field called 'cp_data', but I can't for the life of me work out how to format this data... Should it be an array (something like 'ea_real_name' => request_var('createRealName','') or something else?
PHPBB's wiki for the field is empty (https://wiki.phpbb.com/Custom_profile::submit_cp_field) so not much help...
Thanks! :-)
I was right in my assumption! It's an array with the field name prefixed by pf_.
Finally found an answer here: https://www.phpbb.com/community/viewtopic.php?f=71&t=1638905
$cp_data = array(
'pf_ea_real_name' => request_var('createRealName','')
);
Is the correct way to do it...

rails - find with condition in rails 4

I recently upgraded my rails to Rails 4.1.6.
This query used to work :
#user = User.find(:all, :conditions => { :name => 'batman' })
Now I get this error message:
Couldn't find all Users with 'id': (all, {:conditions=>{:name=>"batman"}}) (found 0 results, but was looking for 2)
When I check the logs I can see that rails is trying to do a completely different query :
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" IN ('all', '---
:conditions:
:name: batman
')
It looks like, it's trying to get all the users with the id "all" and "{:conditions=>{:name=>"batman"}}". Please help.
UPDATE:
My real question behind that was I want to get a specific user and add to it his cars, only the cars that are blue. For example this is my query, the user id is 20.
#user = User.joins(:cars).find(20, :cars => {:color => "blue"})
But I get this error:
Couldn't find all Users with 'id': (20, {:cars=>{:color=>"blue"}})
(found 41 results, but was looking for 2)
You should definitely read this ActiveRecord Query Interface quide
User.where(name: "batman")
Some others already pointed out: The query syntax changed. Try this:
#user = User.joins(:cars).where(:cars => { :color => "blue" }).find(20)
Note that this will raise an exception if that record is not found, to return an array empty instead call:
#user = User.joins(:cars).where(:id => 20, :cars => { :color => "blue" })
I suggest to read: http://guides.rubyonrails.org/active_record_querying.html
If you want to load the user even if he does not have any cars and than display only his blue cars, I would do it like this:
#user = User.find(20) # returns the user
#user.cars.where(:color => 'blue') # returns the user's blue cars (or an empty array)
The find method is deprecated in this version of Rails (see the reference).
Instead, you must use the where method.
In your case, you should write #user = User(:name => 'batman') or #user = User(name: 'batman')

Search multiple colums in model rails 3

I have a search form where I can search one column in my Recipe model using
#countrysearch = Recipe.where(:dish_name => params[:search]).all
So when i search for a dish say lasagne I get a result, however i would like to be able to search another 3 columns within the recipe model, country_of_origin, difficulty and preperation_time.
I have tried this
#countrysearch = Recipe.where({:dish_name => params[:search], :country_of_origin => params[:search], :difficulty => params[:search], :preperation_time => params[:search]}).all
but this does not seem to work either
Can anyone offer a suggestion?
Your code uses AND but you want OR I think:
#countrysearch = Recipe.where("dish_name = ? OR country_of_origin = ? OR difficulty = ? OR preperation_time = ?",
params[:search],
params[:search],
params[:search],
params[:search]
)
If you don't want to use an SQL string you can use the arel_table:
at = Recipe.arel_table
search = params[:search]
#countrysearch = Recipe.where(at[:dish_name].eq(search).or(at[:country_of_origin].eq(search)).or(at[:difficulty].eq(search)).or(at[:preperation_time].eq(search)))
But for the current version of Rails I would prefere the first method because this is better readable. In Rails 5 you will have better methods for this sort of queries. (I will update this post if this becomes available.)

How can I get an ActiveRecord query to ignore nil conditions?

In order to avoid having to construct complicated dynamic SQL queries, I'd like to be able to just pass in nil values in my conditions, and have those ignored. Is that supported by ActiveRecord?
Here is an example.
event = Event.find(:all, :conditions => {
:title => params[:title],
:start_time => params[:start_time],
:end_time => params[:end_time]
}).first
In that particular case, if params[:start_time] is set to nil, ActiveRecord will search for those Events that have their start_time set to null. Instead, I'd like it to just ignore start_time. How do I do that?
You don't have to "create complicated dynamic SQL queries" to do what you need. Simply construct your conditions hash separately, and either exclude the null values at the time of creation or after you've created the hash.
conditions = {}
conditions[:title] = params[:title] unless params[:title].blank?
conditions[:start_time] = params[:start_time] unless params[:start_time].blank?
conditions[:end_time] = params[:end_time] unless params[:end_time].blank?
or
conditions = {:title => params[:title], :start_time => params[:start_time], :end_time => params[:end_time]}
conditions.delete_if {|k,v| v.blank? }
or
conditions = params.reject {|k,v| !([:title, :start_time, :end_time]).include?(k) }
but that last form will only work if the keys are actually symbols. In Rails the params hash is a HashWithIndifferentAccess which allows you to access the text keys as symbols. Of course you could just use the text values in your array of keys to include if necessary.
and then query with your pre-built conditions hash:
event = Event.find(:all, :conditions => conditions).first

Will_Paginate and order clause not working

I'm calling a pretty simple function, and can't seem to figure out whats going on. (I'm using rails 3.0.3 and the master branch of 'will_paginate' gem). I have the following code:
results = Article.search(params) # returns an array of articles
#search_results = results.paginate :page => params[:page], :per_page=>8, :order => order_clause
No matter what I make the order_clause (for example 'article_title desc' and 'article_title asc'), the results are always the same in the same order. So when I check using something like #search_results[0], the element is always the same. In my view, they are obviously always the same as well. Am I totally missing something?
I'm sure its something silly, but I've been banging my head against the wall all night. Any help would be much appreciated!
Edited to Add: The search clause does the following:
def self.search(params)
full_text_search(params[:query].to_s).
category_search(params[:article_category].blank? ? '' : params[:article_category][:name]).
payout_search(params[:payout_direction], params[:payout_value]).
length_search(params[:length_direction], params[:length_value]).
pending.
distinct.
all
end
where each of these guys is a searchlogic based function like this:
#scopes
scope :text_search, lambda {|query|
{
:joins => "INNER JOIN users ON users.id IN (articles.writer_id, articles.buyer_id)",
:conditions => ["(articles.article_title LIKE :query) OR
(articles.description LIKE :query) OR
(users.first_name LIKE :query) OR
(users.last_name LIKE :query)", { :query => "%#{query}%" }]
}
}
scope :distinct, :select => "distinct articles.*"
#methods
def self.payout_search(dir, val)
return no_op if val.blank?
send("payment_amount_#{dir.gsub(/\s+/,'').underscore}", val)
end
def self.length_search(dir, val)
return no_op if val.blank?
send("min_words_#{dir.gsub(/\s+/,'').underscore}", val)
end
Thanks.
If you look at the example from the will_paginate github page you can spot one important difference between their use of the :order clause and yours:
#posts = Post.paginate :page => params[:page], :order => 'created_at DESC'
This calls paginate on the Post object (with no objects being selected yet - no SQL has been executed before paginate comes along). This is different in your example: as you state in the first line of code "returns an array of articles". The simplest I can come up with showing the problem is
results = Model.limit(5).all
#results = results.paginate :order => :doesnt_matter_anymore
won't sort, but this will:
results = Model.limit(5)
#results = results.paginate :order => :matters
It should suffice to take the all out of the search method. It makes ActiveRecord actually perform the SQL query when calling this method. Will_paginate will do that for you when you call paginate (if you let it...). Check out the section on Lazy Loading in this post about Active Record Query Interface 3.0