Rails includes with where condition - sql

I have two models:
user.rb
class User < ApplicationRecord
has_many :orders, inverse_of: :user, dependent: :restrict_with_exception
end
order.rb
class Order < ApplicationRecord
belongs_to :user, inverse_of: :orders
end
I am using includes with where like this:
User.where(id: selected_salesmen.pluck(:id))
.includes(:orders)
.where("order.booked_at > ? AND order.booked_at < ?",
booked_at_gteq,
booked_at_lteq)
However, it's not giving me required users with orders. Any explanation of why this isn't working?

This may be some confusing between the methods includes and joins, which have related, but very different meanings.
includes will eager-load related records, which prevents multiple database calls later on. It's mostly used for performance tuning.
joins will include a join to the related table in your database query, which allows you to build conditions based on the related model.
Note that, to use joins, you need to refer to the table name, not the relation name. By default, ActiveRecord will connect to a table which is the pluralised name of the model.
So, change includes to joins and 'order' to 'orders':
User.where(id: selected_salesmen.pluck(:id))
.joins(:orders)
.where(
"orders.booked_at > ? AND orders.booked_at < ?",
booked_at_gteq,
booked_at_lteq
)
You may also want to check that selected_salesmen.pluck(:id) returns some ids, too.

Related

How to sub-query on the "many" part in a one-to-many association?

I have a standard one-to-many association in two models:
class User < ActiveRecord::Base
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
How do I query for messages that are last for users with name "John"?
I know I can use a joins to filter messages by user attributes. I am also convinced that the "last for that user" part of the query can be done with what is called an sql sub-query, but how exactly it is done is beyond me.
Message.joins(:user).where(users: {name: "John"}).<subquery?>

Sorting based on created_at table after joins two table in rails

I have 4 models: User, Micropost, Answer, and Activity. Activity belongs to Micropost. I want to join the Micropost table and Activities table and sort the results based on the created_at column in Activities where user_id in Activities is the current user. This is my method feed:
def feed
Micropost.joins(:activities).where('activities.user_id=?',id)
.order('activities.created_at DESC')
end
But the output is still sorted by Micropost created_at column. I changed order to reorder; still same result. I googled a lot and logic seems to be correct. Can some one help? Thanks in advance.
I understood the problem. Activity model has two columns where is stores micropost ids. one with micropost_id and other action_id. Its joining through action_id. How can I change that?
Your query does look correct, so I'm assuming the issue is as you describe in your edit (the join is happening through the wrong ID). Rails decides how to join two tables based on the relations that you've set up, so if the join isn't as you'd expect then you need to make sure your relations are correct. In your case, you would want something like:
class Micropost < ActiveRecord::Base
has_many :activities
has_many :action_activities, class_name: 'Activity', foreign_key: :action_id
end
class Activity < ActiveRecord::Base
belongs_to :micropost
belongs_to :action, class_name: 'Micropost'
end
Since Micropost is related to Activity in two different ways, it needs to be told that explicitly. I used the foreign_key option to make sure it knows which is which. With that code, you can see that this generates the expected SQL:
irb> Micropost.joins(:action_activities)
Micropost Load (2.8ms) SELECT "microposts".* FROM "microposts" INNER JOIN "activities" ON "activities"."action_id" = "microposts"."id"
The other option is to be explicit in your join, like so:
Micropost.joins("JOIN activities ON activities.action_id = microposts.id")
.where('activities.user_id=?',id)
.order('activities.created_at DESC')
Which is useful in situations when you want a more complicated join, mostly. In your case, I think your relations are the real issue.

Preloading using includes on unrelated models

Suppose I have three models, set up something like this:
class Student < ActiveRecord::Base
has_many :tests
has_many :cars
end
class Car < ActiveRecord::Base
belongs_to :student
end
class Test < ActiveRecord::Base
belongs_to :student
end
I want to query all tests whose student does not have car. Preloaded.
I've tried the following:
Test.includes(:cars) # does not work because the two are not associated
Test.joins('inner join cars ON tests.student_id = cars.student_id') # works, but it doesn't preload the Cars model in my result
I'd prefer not to create a has_many :through relationship, because they really aren't related at all, but I'm not opposed to it if that's the best solution.
Thoughts?
Rails 4.1.5
PostgreSQL 9.3.4
ruby 2.1.2
A join across three tables is an inefficient way to do this. Rails might even be smart enough to realise this and split it into seperate db queries.
I would do it like this, which has two simple queries instead
student_ids_with_car = Car.select("student_id").distinct
#tests = Test.where("student_id not in (?)", student_ids_with_car)
You don't have to use has_many :through to associations and associations of those associations at the same time.
Test.includes(:student => :cars)
Will include student's and their cars (by default it will preload, you can force a joins based include by using eager_load instead of preload).

how to access data with has_many :through relation

Hi I have three models: company, plan and subscription with following association
class Company < ActiveRecord::Base
has_one :plan, :through => :subscriptions
has_many :subscriptions
end
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :company
end
class Plan < ActiveRecord::Base
has_many :subscriptions
has_many :companies, :through => :subscriptions
end
I have two plans in my application plan 'A' and plan 'B'. Plan 'A' is free and 'B' has some fee. Now i want to get companies registered with plan 'A' and companies with plan 'B'.
I want this data in my model, i know their is definitely a simple way to get all this but
every thing i have used i not giving me right data.any help would be thankful.
To get companies registered with plan 'A' and companies with plan 'B'.
Take object of plan, and then through following relationship code, you will get count of companies.
This is join concept.
eg. #plan is object of Plan 'A'.
then #plan.companies.count.
I suggest to use "polymorphic association" concept.
You need to insert new records through the association. Here is a related link that might help. how to add records to has_many :through association in rails
But a Pseudo-code will be like
1. You have a company object
2. you will have company.subscriptions
3. Insert new Plan objects in company.subscriptions
4. Save the data.
If you are still facing problem, I will try to add some code example.

Crafting the most efficient Rails 3 has_many :through query

I have two Rails 3 models, Product and Room defined per below. Each Room can have multiple products, and each Product can be in multiple rooms:
class Product < ActiveRecord::Base
#...
has_many :rooms, :through => :product_selection
has_many :product_selection
end
class Room < ActiveRecord::Base
#...
has_many :products, :through => :product_selection
has_many :product_selection
end
class ProductSelection < ActiveRecord::Base
#...
belongs_to :product
belongs_to :room
end
I'd like to create a query within rooms_controller.rb to return records for the first 10 rooms, and for each Room include a count of the number of products pushed to that room (to find out how many products are in each Room) and a sum of a column within Product called cost for all products in the Room (to get the total cost of all the products in the room). After calling the query, I would ideally be able to call #rooms[i].total_products and #rooms[i].total_cost (or something similar) along with the Room fields so it can be easily digested and iterated by a template.
I know I could create 10 different calls then loop through each as #room.products.count and #room.products.sum(cost), but there has to be a more efficient way...and I have a feeling I'm overlooking something obvious.
Thanks!!
The naive implementation will have you do N+1 queries...
If your desire was to just count your products efficiently, I would suggest implementing a :counter_cache
Though as you also want to perform a calculation (summation on cost), eager loading the products with the rooms query will be more desirable. The idea being to return your list of rooms and their products.
room_array = Room.includes(:products).limit(10)
If there was the further desire to make this efficient, you may want to have the database do the summation, too.