Rails MySQL query with Single Table Inheritance N+1 issue - mysql

I am trying to find all users that signed up during a given period of time to the ActionMovie plan. I am running into an N+1 problem and it's taking me a very long time to get the number of new signups. I was wondering if there was any creative thing I could do with arel_tables or something like that that could help cut down on this process?
My current code looks similar to the below:
#find all UserMovies created during time frame
user_movies = UserMovie.where(:created_at => start_time..end_time)
#find users
users = user_movies.collect {|um| um.user}
#iterate through each users user_movies and see if the their first action movie was during the time frame I am looking for
users.each do |user|
user_movies_array = user.user_movies.map {|um| {um.movie.type => um.created_at}}
user_movies_array.each do |um|
if um["ActionMovie"] > start_time
puts "new user"
end
end
end
Class User
has_many :user_movies
has_many :movies, :through => :user_movies
end
Class Movie
has_many :user_movies, :foreign_key => :movie_id
has_many :users, :through => :user_movies
end
Class UserMovie
belongs_to :user
belongs_to :movie
end
Class ActionMovie < Movies
end
Class SuspenseMovie < Movies
end

Have you tried eager loading the Movie association using the :include option?
Take a look at the API docs for #has_many to see specific implementation and scroll to the top to the section called Eager loading of associations to see a general overview.

Related

Rails: active-records query for entry in range & included in

I am working on a shipping implementation for a checkout process.
My app has carts, cart_items, orders and order_items.
Weight and size of all items are in the database and I calculate total_weight in the order and cart models. I also have a shipping_service model with weightmin and weightmax for each shipping service + a postzone and land (country) model.
Now I would like to show on the shopping cart page only the shipping services which are conform to the weight of the cart or order.
I suppose my carts_controller should be something like:
class CartsController < ApplicationController
def show
#cart = Cart.find(params[:id])
#lands = Land.find(:all)
#shippingservices = Shippingservice.where('#cart.total_weight BETWEEN ? AND ?', :weightmin, :weightmax)
end
My cart model is:
class Cart < ActiveRecord::Base
attr_accessor :total_weight
has_many :cart_items
has_many :products, :through => :cart_items
has_many :lands
has_many :shipping_services, :through => :postzones
def total_weight
cart_items.inject(0) {|sum, n| n.weight * n.amount + sum}
end
end
My land model is
class Land < ActiveRecord::Base
has_many :shippingservices, :through => :postzones
has_many :postzones
has_many :carts
end
My shipping_service model is:
class Shippingservice < ActiveRecord::Base
has_many :lands, :through => :postzones
has_many :postzones
has_many :carts
has_many :orders
end
My postzone model is:
class Postzone < ActiveRecord::Base
belongs_to :shippingservice
belongs_to :land
end
The postzone table has foreign keys for lands and shipping_services.
Latter I would like to implement two selector fields: one for ship_to_countries and one for shipping_services, with the second selector being populate only with entries related to the entry selected in the first selector.
I had already this working inside the carts_controller:
#shippingservices = Shippingservice.includes(:lands, :postzones).where('postzones.land_id = ?', Land.first.id)
Which load only shipping services for a specific country into the second selector. But I do not know how to combine the two where clauses relative to weight and postzone into one query.
Any help is very much appreciated!
Thank you in advance.
The method total_weight is a ruby method which is defined in the model Cart
Then you cannot call this method within an SQL statement.
You need to calculate the total weight in the SQL statement.
You should try something like
#shippingservices = Shippingservice.joins(carts: :cart_items).where(
'(cart_items.weight * cart_items.amount) BETWEEN ? AND ?', :weightmin, :weightmax
)
I didn't try but I think it should work :)

Query in Ruby on Rails 4 to make a selection based on the current user

I am new to Ruby on Rails and try to make the right query. But after reading the documentation and examples I don't manage to get the right query out of it. So I hope someone can point me in the right direction.
Situation
I build an app where trainers can setup trainings, and give these trainings on several dates (the dates are called thrills), other users can subscribe for these thrills. It has the following structure: models and needed table.
My models code looks like this:
class User
has_many :trainings
has_many :thrills, through: :reservations
has_many :reservations
class Training
belongs_to :user
has_many :reservations
has_many :thrills
has_many :users, through: :thrills
class Thrill
belongs_to :training
has_many :reservations
has_many :users, through: :reservations
class Reservation
belongs_to :user
belongs_to :thrill
has_one :training, through: :thrill
On an index page I want to show all the thrills that the current user has setup sorted by date. I think I need a query that comes up with the table in the uploaded image, and from that table I can select all Thrills where current_user.id = user_id
On the search page I want to show only the trainings that have a Thrill that is not full (therefore I want to make a count of the Reservations)
I was thinking of something like this:
#thrills = Thrill.joins(:trainings).where('? = trainings.user_id, current_user.id')
or
#thrills = Thrill.where('? = #thrill.training.user_id', current_user.id).all
or
#thrills = Thrill.joins(:trainings).where(trainings: { user_id: current_user.id })
But unfortunately none of them works. Does someone have an idea how to solve this? Thanks in advance!
Usually you have these two models:
class Thrill < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :thrills
end
So for your index page what you can do is:
current_user.thrills # => ActiveRecord::Relation (can call each, map etc.)

Rails 4 - how to generate statistics through "joins" and "includes" commands in Activerecord?

I kind of stuck at trying to generate statistics for my application. The relevant part of the application has the following structure:
class CarRegistration < ActiveRecord::Base
belongs_to :ride
belongs_to :car
...
end
class Car < ActiveRecord::Base
has_many :car_registration
...
end
class Ride < ActiveRecord::Base
belongs_to :passenger
belongs_to :driver
has_many :car_registration
...
end
class Driver < ActiveRecord::Base
has_many :cars
...
end
class Passenger < ActiveRecord::Base
has_many :cars
...
end
I am trying to get a list of rides, top drivers and and top passengers. I originally tried something like this:
#rides_finished = Ride.joins(:car_registration)
.select('rides.id')
.where("(car_registrations.ride_id = rides.id)
AND rides.status = 3
AND rides.driver_currency = ?
AND rides.passenger_currency = ?", currency, currency)
.distinct # against displaying one shipment multiple times
And then I tried:
#top_pasengers = #rides_finished.joins(:passenger)
.select('passengers.id, passengers.name, count(rides.passenger_id) AS count_all')
.where('rides.passenger_id IS NOT NULL')
.group('passengers.id')
.order('count_all DESC')
.limit(10)
But when I run these queries, I get
Mysql2::Error: Unknown column 'count_all' in 'order clause': ...
Any help how to get the needed numbers?
Thank you very much
Your question is a little confusing because your query uses Ride but there is no Ride in the model definitions listed. I've focussed purely on the example queries you listed.
I think it would be easier to start with a single query chain for 'top passengers':
Passenger
.select('passengers.*')
.select('count(1) as ride_count')
.joins(:rides)
.where(rides: { status: 3,
driver_currency: currency,
passenger_currency: currency })
.group('passengers.id')
.order('ride_count desc')
.limit(10)
That will get you an ActiveRecord::Relation of Passenger models that also respond to a ride_count call, e.g. you could use it like:
results.each do |p|
puts "#{p.name}: #{p.ride_count}'
end
If all that works, you should be able to adjust the query to get the top drivers.
To get the list of finished rides, I suggest a separate, simple query:
Ride.where(status: 3,
driver_currency: currency,
passenger_currency: currency)
Let me know if any of that produces an error.

Pundit Scoping for Model Ownership through a Join table - Rails 4

I'm associating users with given firms through a join table because I need to be able to have a bunch of users with every firm and vice versa.
class User
has_many :firm_connections, dependent: :destroy
has_many :firms, through: :firm_connections
end
class FirmConnection
belongs_to :user
belongs_to :firm
end
class Firm
has_many :firm_connections
has_many :users, through: :firm_connections
end
My question is, when a user hits the index page for firms, how do I scope it to only show what those users are associated with?
class FirmPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
scope.where #only the firms associated with that user
end
end
end
Do I need to create a scope at the firm level that accepts a #user? Or can I do this all directly inline? I could hack something together, but haven't wrapped my head around pundit yet, so any direction would be greatly appreciated!
like this:
def self.associated_with(user)
all.select { |m| m.users.include?(user) }
end
This should work for you
class Firm
def index
#firms = policy_scope(Firm)
end
end
class FirmPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.all
else
user.firms #only the firms associated with that user
end
end
end
end
The policy doesn't always have to call it the way that you think, it just has to return something (for scopes, almost always an ActiveRecord::Relation, for regular, true or false). You could do
scope.includes(:firm_connections).where(firm_connections: { user_id: user.id })
but that's not as readable (IMO).

Activerecord - how to made this?

Everyday, I need to run the a script and send all of my users an 'exam' or set of questions. I have modelled as class 'Exam' which subclasses ActiveRecord::Base. Now, how do I send user's instances of Exam?
What I was thinking was create a new class called 'ExamInstance' which would have a reference to 'Exam' and the user.
I am new to SQL and ActiveRecord so if someone can help me better model this so I can avoid problems later on or just give me some insight, that would be great.
Thanks
I'll suggest just use has_many :through create a model UserExam for many to many relation between exam and user
class User < ActiveRecord::Base
  has_many :users_exams
  has_many :exams, :through => :users_exams
end
 
class UserExam < ActiveRecord::Base
  belongs_to :users
  belongs_to :exams
end
 
class Exam < ActiveRecord::Base
  has_many :users_exams
  has_many :users, :through => :users_exams
end
For more information on has_many :through
Add 'ExamUser' model to keep track of exam and the corresponding users references. The model skeleton will look something like:
class ExamUser < ActiveRecord::Base
belongs_to :exam
belongs_to :user
end
You could then loop through the records of this table to send your questions.
You can design your new model like this..
class ExamUser < ActiveRecord::Base
belongs_to :user # user has many exams
belongs_to :exam # exam has many users
end