Rails 5: iteration with condition to map matching records - ruby-on-rails

I have this method in my rating/rating.rb Model, where I basically need to create array of arrays with matching inventory and rating IDs:
def inventory_ratings
inventory = Inventory::Inventory.where(id: inv).order(date: :desc)
rating = Rating::Rating.where(id: rtg).order(valid_from: :desc)
columns = [:inventory_id, :rating_id]
values = inventory.map {|inv|
if (inv.position_id == rating.position_id &&
rating.valid_from..rating.valid_to.include?(inv.date))
r = rating.id
end
[ inv.id, r ]
}
Rating::InventoryRating.import columns, values, validate: false
end
At the moment I get this error:
NoMethodError: undefined method "position_id" for #<Rating::Rating::ActiveRecord_Relation:0x007ffff6067bf8> since I probably have to somehow iterate through each rating to get position_id, valid_from and valid_to.
How do I add that extra iteration so each inventory record iterates through each rating record and maps if it matches IF statement, please? Thank you!

How about this:
values = []
inventory.each do |inv|
values.concat([inv.id].product(rating.where('position_id = ? AND valid_from <= ? AND valid_to >= ?', inv.position_id, inv.date, inv.date).ids))
end.uniq.compact

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")

Sum ActiveRecord Relationship (Model + Model = Array?)

I'm trying to sum two Active Model Relationship and order it by created_at.
The sum of dogs and cats make an Array. So I try to sort this array by created_at with .sort_by( &:created_at ) but I get this strange error:
dogs = current_user.dogs
cats = current_user.cats
total = (dogs + cats).sort_by( &:created_at )
comparison of ActiveSupport::TimeWithZone with nil failed
There is another best way to sum two active record relationship and order it by created_at ?
Thank you very much.
One of your results has a created_at of nil it seems like, which is why you're getting that error. You can do the following to filter nil results
dogs = current_user.dogs.where.not(created_at: nil)
cats = current_user.cats.where.not(created_at: nil)
sorted_results = (dogs + cats).sort_by(&:created_at)
If you want to see which record(s) have created_at as nil do
nil_created_at_dogs = current_user.dogs.where(created_at: nil)
nil_created_at_cats = current_user.cats.where(created_at: nil)
I solve the problem.
If use .where, all works properly.
dogs = Dog.where(user_id: current_user.id)
cats = Cat.where(user_id: current_user.id)
total = (dogs + cats).sort_by( &:created_at )
I don't know why, but this is the way.

Compose an ActiveRecord query having array of string conditions

I have an array of strings that are to serve as params for a where call in a model.
How do I append each of the strings in the models where call and return the active record relation for an additional limit call
I have tried the following but it only adds the first item to the where clause
array = ['active = true', 'expired = false', 'created_at > 2017-04-18 10:36:28']
array.reduce { | item | Post.where(item) }
returns
Posts.where('active = true')
whereas am begging for
Posts.where('active = true').where('expired = false').where('created_at > 2017-04-18 10:36:28')
Thanks.
Just join array's elements into a single string:
array.join(' AND ')
#=> "active = true AND expired = false AND created_at > 2017-04-18 10:36:28"
And use it:
Post.where(array.join(' AND '))
P.S.
created_at > 2017-04-18 10:36:28 will probably throw you a syntax error, but that's out of the question's scope.

Use Rails .where to return objects with an association count greater than 0

I have the following method that returns all orders in current month that have a newspaper placement that is running. An order has_many newspaper_placements.
def self.orders_with_placements_in_current_month(all_cc_mc)
filter_start = Date.today.beginning_of_month
filter_end = Date.today.end_of_month
all_orders = []
Order.where(client_companies_media_company_id: all_cc_mc).each do |x|
if x.newspaper_placements.count > 0
if (filter_start..filter_end).overlaps?(x.newspaper_placements.first.date..x.newspaper_placements.last.date)
all_orders << x
end
end
end
end
After the .where that is called on Order, I am including a conditional to see if the newspaper_placement count is greater than 0, because the code that follows it will fail if there are no placements returned. Is there a way that I can only return orders that have a newspaper_placement count greater than 0 in my original query? So what I want to do is have:
Order.where(client_companies_media_company_id: all_cc_mc)
return only orders with a placement count > 0, so I can eliminate the conditional that follows.
Somethling like:
Order.joins(:newspaper_placements).where(client_companies_media_company_id: all_cc_mc)

Nested ActiveRecords: Find many childrens of many parents

In my Rails 3.2 app a Connector has_many Incidents.
To get all incidents of a certain connector I can do this:
(In console)
c = Connector.find(1) # c.class is Connector(id: integer, name: string, ...
i = c.incidents.all # all good, lists incidents of c
But how can I get all incidents of many connectors?
c = Connector.find(1,2) # works fine, but c.class is Array
i = c.incidents.all #=> NoMethodError: undefined method `incidents' for #<Array:0x4cc15e0>
Should be easy! But I don't get it!
Here’s the complete code in my statistics_controller.rb
class StatisticsController < ApplicationController
def index
#connectors = Connector.scoped
if params['connector_tokens']
logger.debug "Following tokens are given: #{params['connector_tokens']}"
#connectors = #connectors.find_all_by_name(params[:connector_tokens].split(','))
end
#start_at = params[:start_at] || 4.weeks.ago.beginning_of_week
#end_at = params[:end_at] || Time.now
##time_line_data = Incident.time_line_data( #start_at, #end_at, 10) #=> That works, but doesn’t limit the result to given connectors
#time_line_data = #connectors.incidents.time_line_data( #start_at, #end_at, 10) #=> undefined method `incidents' for #<ActiveRecord::Relation:0x3f643c8>
respond_to do |format|
format.html # index.html.haml
end
end
end
Edit with reference to first 3 answers below:
Great! With code below I get an array with all incidents of given connectors.
c = Connector.find(1,2)
i = c.map(&:incidents.all).flatten
But idealy I'd like to get an Active Records object instead of the array, because I'd like to call where() on it as you can see in methode time_line_data below.
I could reach my goal with the array, but I would need to change the whole strategy...
This is my time_line_data() in Incidents Model models/incidents.rb
def self.time_line_data(start_at = 8.weeks.ago, end_at = Time.now, lim = 10)
total = {}
rickshaw = []
arr = []
inc = where(created_at: start_at.to_time.beginning_of_day..end_at.to_time.end_of_day)
# create a hash, number of incidents per day, with day as key
inc.each do |i|
if total[i.created_at.to_date].to_i > 0
total[i.created_at.to_date] += 1
else
total[i.created_at.to_date] = 1
end
end
# create a hash with all days in given timeframe, number of incidents per day, date as key and 0 as value if no incident is in database for this day
(start_at.to_date..end_at.to_date).each do |date|
js_timestamp = date.to_time.to_i
if total[date].to_i > 0
arr.push([js_timestamp, total[date]])
rickshaw.push({x: js_timestamp, y: total[date]})
else
arr.push([js_timestamp, 0])
rickshaw.push({x: js_timestamp, y: 0})
end
end
{ :start_at => start_at,
:end_at => end_at,
:series => rickshaw #arr
}
end
As you only seem to be interested in the time line data you can further expand the map examples given before e.g.:
#time_line_data = #connectors.map do |connector|
connector.incidents.map do |incident|
incident.time_line_data(#start_at, #end_at, 10)
end
end
This will map/collect all the return values of the time_line_data method call on all the incidents in the collection of connectors.
Ref:- map
c = Connector.find(1,2)
i = c.map(&:incidents.all).flatten

Resources