Slow performance from rake task - How to streamline these Rails queries - ruby-on-rails

I am experiencing slow performance on a rake task. Upon review, it looks like the same item is being queried 3 times. Is there a way to simplify this so only one query is executed? Would this have a significant performance improvement? Would love to hear your ideas.
Here's a breakdown...
So I check if it exists:
if Ecommerce.exists?(:legacy_id => legacy_id, :company => company)
Then the same item is queried again to pluck a value needed:
historical_interest = Ecommerce.where(:legacy_id => legacy_id, :company => company).pluck(:interest)
Then if the plucked value is different, the record is updated:
if interest != historical_interest
Ecommerce.where(:legacy_id => legacy_id, :company => company).update_all(:interest => interest, :end_date => end_date, :revenue => revenue)
end
else
#other stuff
Full code:
if Ecommerce.exists?(:legacy_id => legacy_id, :company => company)
# It exists.
historical_interest = Ecommerce.where(:legacy_id => legacy_id, :company => company).pluck(:interest)
# Another statement to check that the Interest != UnitSold
if interest != historical_interest
Ecommerce.where(:legacy_id => legacy_id, :company => company).update_all(:interest => interest, :end_date => end_date, :revenue => revenue)
end
else
#other stuff

this should help a little:
ecommerce_array = Ecommerce.where(legacy_id: legacy_id, company: company)
if ecommerce_array.any?
historical_interest = ecommerce_array.pluck(:interest)
return unless interest == historical_interest
ecommerce_array.update_all(....)
else
#....
EDIT:
change this historical_interest = ecommerce_array.pluck(:interest) into this historical_interest = ecommerce_array.collect(&:interest)

Related

Ruby: AND clause if conditional operator under CanCan gem

Here is the sceanrio:
I'm using CanCan gem and try to implement a :read ability accompanied with 2 AND conditions
can :read, Truck, :product => {:company_id => user.company_id}
Now here I wanted to have and additional condition checking for a column value as follows:
can :read, Truck, :product => {:company_id => user.company_id, :category_id => user.category_id}
But the above statement throws error in active_adapter saying "Undefined table_name for nil class"
Also the second set of condition category_id needs to be present only if a flag "check_category_id_flag" is checked. At the end I want something as below to work for me:
can :read, Truck, :product => {:company_id => user.company_id,
( user_role.check_category_id_flag? ? :category_id => user.category_id : 1 == 1)}
You can combine conditions like this
can :read, Truck do |truck|
truck.product.company == user&.company && truck.product.category == user&.category
end

rails sql query

I have:
User id:integer name:integer
class User
has_many :complete_tasks
end
Task id:integer style:string uid:string
CompleteTask id:integer style:string uid:string user_id:integer
class CompleteTask
belongs_to :user
end
i have some records in DB
user = User.first
id:1 name:Den
tasks = Tasks.all
id:1 style => "run" uid => "river"
id:2 style => "jump" uid => "sea"
id:3 style => "run" uid =>"sea"
id:4 style => "run" uid =>"river"
id:5 style => "run" uid =>"forest"
user.complete_tasks.all
id:1 style => "run" uid => "river" user_id => 1
id:2 style => "jump" uid => "sea" user_id => 1
How to get records from Task where fields :style and :uid together not equivalent fields :style and :uid in model CompleteTask.
This is a poor database model and i would recommend to use something else. Maybe a boolean value or smthng like that in the task model so you can check if it's complete or not.
Anyways, here is a solution:
all_tasks = Task.all
complete_tasks = CompleteTask.all
complete_tasks.each do |complete_task|
#find if there is a task with the corresponding uid and style in the complete tasks
tasks = all_tasks.where(:uid => complete_task.uid, :style => complete_task.style)
#remove it from all_tasks (all_tasks means tasks that haven't finished yet)
all_tasks = #all_tasks - tasks
end
return all_tasks
If I'm understanding you correctly, you're trying to find all tasks that don't have the same IDs as complete tasks.
For that you should use a find condition--something like:
Task.find(:all, :conditions => { :complete => false });

Constructing a new object in Rails that is part of an active record association

This is just a simple question. I was trying to create a new object in Rails by passing in parameters to the constructor. However, when I execute the code, I get
SQLite3::SQLException: no such column: awards.user_id: SELECT "awards".* FROM "awards" WHERE "awards"."user_id" = 1
which means the object isn't being constructed properly. Should I be using create instead of new? That isn't working either.
def refresh_awards(user)
new_awards = []
if (user.karma < 40 ) #test award
a = Award.new(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
user.awards.append(a)
new_awards.append(a)
end
new_awards.each do |a|
flash[:notice] = "You received the " + a.name + "!"
end
end
Have you add has_many :awards to the User model? Have you added belongs_to :user to the Award model? Have you added the column user_id to the Award model (using a migration)? You'll need to do these three things to be able to use the user.awards method you're using. Read the Rails Guide on Associations for more detail.
Also, append isn't a Ruby method - the closest method would be <<. You would use it like this:
a = Award.new(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
user.awards << a
But you could neaten this into one line of code using the create method:
a = user.awards.create(:name => "Nobody Award", :description => "From Jonathan", :category => "Community", :value => 1337, :level => 0, :handle => "nobody_award")
EDIT: To create the user_id column in the Award model, run the following code from terminal (while in your app's directory):
rails generate migration AddUserIdToAward user_id:integer
rake db:migrate

ActiveRecord query with conditionals on nested associations

I've got a Person model, who has_many roles, and roles, in turn, belong_to an application. I'd like to query all the roles a person has for a given application. So far I've got:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application_id => 1})
Which works, but it's querying based on Person.roles.application_id; instead, I'd like to query based on Person.roles.application.api_key (another property of an application).
I tried:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application => {:api_key => 'the_api_key'}})
but I receive the error that:
no such column: application.api_key
leading me to think my usage of ActiveRecord isn't joining the tables together correctly.
Any ideas?
Try this:
p = Person.includes(:roles => [:application]).where(:loginid => 'their_loginid', :role_id => Application.find_by_api_key('api_key').role_ids)
try this
p = Person.joins.includes(:roles => [:application]).where(:loginid => 'their_loginid', :roles => {:application => {:api_key => 'the_api_key'}})
In my case it woks.

Adding another condition to existing named_scope

I am working on a Rails 2.3.9 app and my question involves both a self referencial relationship and a named_scope. This application allows users to log and share workouts. A workout can be public or private and that is designated by #workout.public == 1.
I allow users to 'follow' people. So on a current_user's dashboard I display all public workouts from users that current_user follows with the following code:
/dashboard/index.html.erb
<% current_user.friends_workouts.each do |workout| %>
<%= link_to (workout.title), workout %> <br/>
by <%= link_to (workout.user.username), workout.user %> - <%= time_ago_in_words(workout.created_at)%> ago</p>
<% end %>
user.rb
def friends_workouts
#friends_workouts ||= Workout.current.public_workouts.find_all_by_user_id(self.friends.map(&:id), :order => "created_at DESC", :limit => 3)
end
workout.rb
named_scope :public_workouts, :conditions => {:public => 1 }, :order => "created_at DESC"
I now want to add a condition to this scope as I am adding another level of sharing. Users can associate to a "box" (a gym really) through a "membership" model. So if the current_user belongs_to the same "box" as a user they follow, they should not only see the workouts marked public but also workouts where #workout.box_only == 1.
How can I affect the above to include all public workouts from followed users AND workouts from followed users where #workout.box_only == 1 and #workout.user.membership.box_id == current_user.membership.box_id. I know that syntax is incorrect but you get my point (hopefully).
UPDATE:
It also needs to be considered that :public_workouts is being called from pages that don't require a logged_in? user so in that case if the scope is trying to reference current_user it will throw an error.
UPDATE 2:
:user has_many :memberships
I believe something like the following should do it for you:
named_scope :public_workouts,
:joins => ", user, membership"
:conditions =>
"workouts.public = 1 or
membership.box_id = #{current_user.membership.box_id}",
:group => "workouts.id",
:order => "workouts.created_at DESC"
You would have to play around with this for a bit. The hard part every time I try something like this is to get the OR conditions correct. You want to get all public and those where the joined membership.box_id matches regardless of public being 1.
Edit: Admittedly this is perhaps not the most ruby way of building a query and I haven't tested it properly but something like below could also be made to work.
def self.public_workouts
query = Workout.joins(:user => { :membership })
if current_user
query.where('memberships.box_id = ? or workouts.public = 1', current_user.membership.box_id) unless current_user.membership.box_id.nil?
else
query.where('workouts.public = 1')
end
query.group('workouts.id')
query.order("workouts.created_at DESC")
return query
end
Edit2
Another alternative could be to create two separate scopes and create a class method that combines the two scopes. This class method would then be used in the view.
named_scope :box_workouts,
:joins => ", user, membership"
:conditions => "memberships.box_id = #{current_user.membership.box_id}"
:group => "workouts.id",
:order => "workouts.created_at DESC",
:select "workouts"
named_scope :public_workouts,
:conditions => :public => 1
:order => "workouts.created_at DESC"
def self.public_box_workouts
return box_workouts.merge(public_workouts).limit(3) if current_user
return public_workouts.limit(3)
end
Edit3 Not so hard, I believe something like below will work.
def self.box_and_public_workouts(user)
return public_workouts if user.nil? or user.memberships.blank?
return public_workouts + box_workouts(user.memberships.map(&:box_id))
end
named_scope :box_workouts, lambda { |box_ids| { :conditions => ['box_id IN (?)', box_ids], :order => 'created_at DESC' } }
Apologies for taking so long. I was missing confused with how the "old" way of querying the database. I went right for Rails3 :)
Anyway, I didn't want to commit anything so I tried to fork it to send a pull request but github is being rude to me tonight. Might just copy from here then.

Resources