Combining params in Rails - ruby-on-rails

Hi im doing a filter section for "players" on my app..
Im filtering by "position" at this moment, but i need to filter by "birthday.year" in database the birthday is complete 1900-00-00
I actually do a good research before but i can "mix" or "combine" my params.. the best answer i found was here (so its not a duplicate)
Rails: combining optional params into a query
im a noob in Rails,so i will appreciate any help im just doing the integration of a design..
Here is my code but how can i use minyear, and maxyear to filter by position and age, for example..
thanks!!
def index
#candidates = Player.order("created_at DESC")
position = params[:position]
minyear = params[:minyear]
maxyear = params[:maxyear]
if position == 'goalkeeper'
#candidates = #candidates.where(position:'goalkeeper')
elsif position == 'cedefense'
#candidates = #candidates.where(position:'cedefense')
elsif position == 'ridefense'
#candidates = #candidates.where(position:'ridefense')
elsif position == 'ledefense'
#candidates = #candidates.where(position:'ledefense')
elsif position == 'defmedium'
#candidates = #candidates.where(position:'defmedium')
elsif position == 'ofemedium'
#candidates = #candidates.where(position:'ofemedium')
elsif position == 'rimedium'
#candidates = #candidates.where(position:'rimedium')
elsif position == 'lemedium'
#candidates = #candidates.where(position:'lemedium')
elsif position == 'offensive'
#candidates = #candidates.where(position:'offensive')
elsif position == 'scoach'
#candidates = #candidates.where(position:'scoach')
elsif position == 'sprepf'
#candidates = #candidates.where(position:'sprepf')
else
#candidates = Player.all
end
After a lot of research i come up with this
position = params[:position]
minyear = params[:minyear]
maxyear = params[:maxyear]
if params[:position].nil?
#candidates = Player.all
elsif !params[:position].nil? && params[:minyear].nil?
#candidates = #candidates.where("position = ?", position)
elsif !params[:minyear].nil?
#candidates = #candidates.where("position = ? and birthday = ?", position, minyear )
else
#candidates = Player.all
end
The only problem now is that birthday as i said before has a full format, im just interested just in the year... how can i solve this?
thanks in advance
Nevermind it works like this
#candidates = #candidates.where("position = ? and birthday < ?", position, minyear )
Thanks to Alex D i ha this now,
#candidates = Player.scoped # for Rails 3
if params[:position].present?
#candidates = #candidates.where(position: position)
end
if year = params[:year]
date = Date.new(year)
# this will find all people whose birthday is within the given year
# using YEAR(birthday) will likely cause a full table scan;
# it's better to use a range query
#candidates = #candidates.where("birthday >= ? AND birthday <= ?", Date.new(minyear), Date.new(maxyear).end_of_year)
end

First of all, you can improve your code if you know that Active Record's where and other similar methods are chainable:
#candidates = Player.order('created_at DESC')
# if you don't want to set a default order, you can use Player.scoped in Rails 3
# I forget what it is for Rails 4. Maybe just Player.all.
# In Rails 3, .all returns an Array, which doesn't allow you to chain additional
# where conditions, etc.
if params[:position].present?
#candidates = #candidates.where(position: position)
end
if params[:minyear].present?
#candidates = #candidates.where(birthday: minyear)
end
Now the second part: you actually want to match on the birthday year. There are a couple ways to do this, but this is the way which can benefit from database indexes if you have the right indexes in place:
if year = params[:year]
date = Date.new(year)
# this will find all people whose birthday is within the given year
# using YEAR(birthday) will likely cause a full table scan;
# it's better to use a range query
#candidates = #candidates.where("birthday >= ? AND birthday <= ?", date, date.end_of_year)
end
Since your param is called minyear, I'm guessing you may actually want all the people whose birthday is during the given year or later. In that case:
#candidates = #candidates.where("birthday >= ?", Date.new(year))
Or if it's minyear and maxyear:
#candidates = #candidates.where("birthday >= ? AND birthday <= ?", Date.new(minyear), Date.new(maxyear).end_of_year)

#candidates = #candidates.where("position = ? and year(birthday) < ?", position, minyear )
Try it, this will serve your purpose.

Related

Activerecord query where current employer is X and previous employer is Y

Basically I'd like to return all people whose current job title is X and whose previous job title is Y. As an example, I have a talent whose current emnployment is "Airbnb (company_id = 1)" and whose previous employment is at "Youtube (company_id = 2)".
If I run a query to find talent where current employment is Airbnb:
Talent.joins(:job_histories).where(["job_histories.company_id = ? and job_histories.end_year = ?", 1, "Present"])
I get the person.
If I run a query where previous employment is Youtube (hence the end_year != "Present" below)
Talent.joins(:job_histories).where(["job_histories.company_id = ? and job_histories.end_year != ?", 2, "Present"])
I also get the same person.
However, if I chain them together to find talents where current employer is Airbnb AND previous employer is Youtube, like this:
#talents = Talent.all
#talents = #talents.joins(:job_histories).where(["job_histories.company_id = ? and job_histories.end_year = ?", 1, "Present"])
#talents = #talents.joins(:job_histories).where(["job_histories.company_id = ? and job_histories.end_year != ?", 2, "Present"])
I do not get any results. I've tried several variations of the query but none return anything.
The only way I can get it to work is by using the first query and then looping over each talent to find where job_histories.company_id == 2.
if params[:advanced_current_company] && params[:advanced_previous_company]
#talents = #talents.joins(:job_histories).where(job_histories: { company_id: params[:advanced_current_company] }).distinct if params[:advanced_current_company]
#talents.each do |talent|
talent.job_histories.each do |job_history|
if job_history.company_id == params[:advanced_previous_company][0].to_i
new_talents.append(talent.id)
end
end
end
#talents = Talent.where(id: new_talents)
end
Any direction would be amazing. Thanks!
You had the right idea with a double join of the job_histories, but you need to alias the job_histories table names to be able to differentiate between them in the query, as otherwise activerecord will think it's only one join that needs to be done.
Talent.joins("INNER JOIN job_histories as jh1 ON jh1.talent_id = talents.id")
.joins("INNER JOIN job_histories as jh2 ON jh2.talent_id = talents.id")
.where("jh1.company_id = ? and jh1.end_year = ?", 1, "Present")
.where("jh2.company_id = ? and jh2.end_year != ?", 2, "Present")

Greater/Lower than works but equals doesn't on >= and <= on .where()

I have a Rails 4 app and I'm trying to make a simple search for my invoices with 3 optional arguments: Name of the client, Start Date, End Date.
The search works fine mostly, if I put a start date and an end date it works for < and >, but eventhough i used >= and <=, if the invoice date is the same to either start or end, it just won't show on the result list.
The tables used look like this:
Client Table
ID
Name
The rest of the fields aren't necessary
Invoice Table
ID
Client_ID
Total_Price
Created_At *only here for relevance*
My Invoice Controller Search method looks like this:
def search
if request.post?
#count = 0
#invoices = Invoice.all
if params[:start_date].present?
#invoices = Invoice.invoices_by_date(#invoices, params[:start_date], 'start')
if #invoices.present?
#count = 1
else
#count = 2
end
end
if params[:end_date].present?
#invoices = Invoice.invoices_by_date(#invoices, params[:end_date], 'end')
if #invoices.present?
#count = 1
else
#count = 2
end
end
if params[:name].present?
#invoices = Invoice.invoices_by_client(#invoices, params[:name])
if #invoices.present?
#count = 1
else
#count = 2
end
end
if #count == 2
flash.now[:danger] = "No results found."
#invoices = nil
end
#name = params[:name]
#start_date = params[:start_date]
#end_date = params[:end_date]
end
end
And the Invoice Model methods i use look like this:
def self.invoices_by_client(invoices, name)
invoices= invoices.includes(:client)
.select('invoices.created_at', 'invoices.total_price', 'clients.name')
.where("clients.name LIKE ?", "%#{name}%")
.references(:client)
return invoices
end
def self.invoices_by_date(invoices, date, modifier)
if modifier == 'start'
invoices = invoices.includes(:client)
.select('invoices.created_at', 'invoices.total_price', 'clients.name')
.where("invoices.created_at >= ?", date)
.references(:client)
elsif modifier == 'end'
invoices = invoices.includes(:client)
.select('invoices.created_at', 'invoices.total_price', 'clients.name')
.where("invoices.created_at <= ? ", date)
.references(:client)
end
return invoices
end
It probably isn't the best solution overall and I don't know if i did anything wrong so it would be great if you guys could help me with this.
I followed Alejandro's advice and messed around with the time aswell as the date, something like this:
if modifier == 'start'
invoices = invoices.includes(:client)
.select('invoices.created_at', 'invoices.total_price', 'clients.name')
.where("invoices.created_at >= ?", "#{date} 00:00:00") // Added the start time
.references(:client)
elsif modifier == 'end'
invoices = invoices.includes(:client)
.select('invoices.created_at', 'invoices.total_price', 'clients.name')
.where("invoices.created_at <= ? ", "#{date} 23:59:59") // Added end time aswell
.references(:client)
end
I forced the time for the start date as 00:00:00 and the time for the end date as 23:59:59 and it worked as desired. Thank you for the help man and i hope this helps other people!

How to put validation on form in which button is not used in Ruby On Rails?

I have three fields start date , end date and , issue tracker group if I select only group then all records of that group should come from issue request table but when I select date then it should display specific date records .
Controller Code -
def group_report_list
#start = params[:date].to_date
#en = params[:to_date].to_date
#issue_tracker_group = IssueTrackerGroup.find(params[:id])
#issue_requests = IssueRequest.where(issue_tracker_group_id: #issue_tracker_group.id,date: #start..#en)
one way is using normal if..else condition.
def group_report_list
#start = params[:date].to_date unless params[:date].nil?
#en = params[:to_date].to_date unless params[:to_date].nil?
#issue_tracker_group = IssueTrackerGroup.find(params[:id])
unless #start.nil? or #en.nil?
#issue_requests = IssueRequest.where(issue_tracker_group_id: #issue_tracker_group.id,date: #start..#en)
else
#issue_requests = IssueRequest.where(issue_tracker_group_id: #issue_tracker_group.id)
end
end
if you feel this is too lengthy code, you can use the code below
def group_report_list
#start = params[:date]
#en = params[:to_date]
#issue_tracker_group = IssueTrackerGroup.find(params[:id])
condition = "issue_tracker_group_id = #{#issue_tracker_group.id}"
condition += " AND date BETWEEN '#{#start.to_date}' AND '#{#en.to_date}' " unless #start.nil? or #en.nil?
#issue_requests = IssueRequest.where(condition)
end

Rails 3: Filtering merit points by category in leaderboard

The real tactical question I am facing is all categories are set as 'default' therefore if I make options[:category] = 'default' it only adds the points that have no category. Therefore if i add points to cateogry 'arin' it will not be counted to the 'default' total. So I tried to grab all tables if NOT NULL or by category but it keeps grabbing the same amount for 'arin'.
default: 20
arin: 20
Should be total of 40 if category not supplied or at 'default', if params category 'arin' then it should be 20.
Can someone help me understand the concept behind the correct SQL to get the results I am looking for?
New to rails and SQL.
def self.top_scored(options = {})
options[:table_name] ||= :users
options[:since_date] ||= 4.months.ago
options[:end_date] ||= 1.month.from_now
options[:category] ||= nil
options[:limit] ||= 10
alias_id_column = "#{options[:table_name].to_s.singularize}_id"
if options[:table_name] == :sashes
sash_id_column = "#{options[:table_name]}.id"
else
sash_id_column = "#{options[:table_name]}.sash_id"
end
# MeritableModel - Sash -< Scores -< ScorePoints
sql_query = <<SQL
SELECT
#{options[:table_name]}.id AS #{alias_id_column},
SUM(num_points) as sum_points
FROM #{options[:table_name]}
LEFT JOIN merit_scores ON merit_scores.sash_id = #{sash_id_column}
LEFT JOIN merit_score_points ON merit_score_points.score_id = merit_scores.id
WHERE merit_score_points.created_at > '#{options[:since_date]}' AND merit_score_points.created_at < '#{options[:end_date]}' AND (merit_scores.category IS NOT NULL OR merit_scores.category = '#{options[:category]}')
GROUP BY #{options[:table_name]}.id, merit_scores.sash_id
ORDER BY sum_points DESC
LIMIT #{options[:limit]}
SQL
results = ActiveRecord::Base.connection.execute(sql_query)
results.map do |h|
h.keep_if { |k, v| (k == alias_id_column) || (k == 'sum_points') }
end
results
end
end
Seems no one answered and only down voted. Here is to anyone that questions this in the future. I figured out you can split sql statements and use an if statement in rails around the SQL.
sql_query = "SELECT
#{options[:table_name]}.id AS #{alias_id_column},
SUM(num_points) as sum_points
FROM #{options[:table_name]}
LEFT JOIN merit_scores ON merit_scores.sash_id = #{sash_id_column}
LEFT JOIN merit_score_points ON merit_score_points.score_id = merit_scores.id
WHERE merit_score_points.created_at > '#{options[:since_date]}' AND merit_score_points.created_at < '#{options[:end_date]}' "
if(options[:category] != nil)
sql_query += "AND merit_scores.category = \"#{options[:category]}\" "
end
sql_query += "GROUP BY #{options[:table_name]}.id, merit_scores.sash_id
ORDER BY sum_points DESC
LIMIT #{options[:limit]} "
results = ActiveRecord::Base.connection.execute(sql_query)

Rails - Find or Create based on TimeStamp? possible?

In my controller I'd like to do something like the following:
#book = Book.find(:all, :conditions = > [" created_at > with in the last 1 minute "]
if #book.nil?
# Just incase we didn't create a book, we'll initialize one
#book = Book.create()
end
#chapters = #book.chapters.build etc.............
* In sum, when the user is uploading a chapter, if they've recently created a book, I want the chapter to automatically go to that book and to make a new book.
Thoughts? thank you all
Hi Your code may be something like
time = Time.now
#book = Book.find(:all, :conditions = > [" created_at >= '#{Time.utc(time.year, time.month, time.day, time.hour, time.min - 1)}'"]) // .first if you're sure that it'll return just one record
if #book.blank? //.blank? not .nil? because the result of find is [] not nil
# Just incase we didn't create a book, we'll initialize one
#book = Array.new() //if you're sure that find'll return just one book you may don't change your code here
#book.first = Book.create()
end
//if you're sure that find'll return just one book you may don't change your code here
#book.each do |book|
#chapters = #book.chapters.build etc.............
end
if you're looking for a book created by some user you must pass user_id to this method and your conditions'll be
:conditions = > [" created_at >= '?' AND author_id = ?", Time.utc(time.year, time.month, time.day, time.hour, time.min - 1), params[:author_id]])

Resources