Crafting the most efficient Rails 3 has_many :through query - ruby-on-rails-3

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.

Related

Rails Postgres hstore or jsonb to store many to many relations

There is a table 'Products' with columns (id, tile, price, description, shop_id, timestamps)
Column price and shop_id are dynamic. The rest of column are static.
Also there is a table 'Shops' with columns (id, name, timestamps).
Shop has many products. Product can migrate among all shops and products.shop_id can be blank.
I want to have history about which products a shop had. For example, yesterday shop had products and their prices. Day before yesterday shop had other products and/or other their prices. So I want to have something like
'ShopHistory' (id, data, date, timestamps), where data is hstore or json. So it should have hash like { key: value}, where key is id of product and value is its current price. But I would like to joins data column with products in order to know about title and description of products. I have no idea how to do that.
It looks like ShopHistory is a join table between shop and products, but all information are stored in one row.
Could we help me? Maybe anyone knows a better way to implement all of this. Any thought and articles are welcome.
Thanks!
P.S. I use rails(ActiveRecord) and PostgreSQL, but answers from guys who know only Postgres are good for me too.
I get your intent, but frankly I don't think it's going to work out for you in a straightforward, easy way. (As a rule of thumb, in ruby and in rails, if it isn't straightforward, you're probably not doing it the ruby/rails way).
Why do I know this? Because I tried before to do a very similar thing to what you want to do with an hstore, with no luck:
Can I use ActiveRecord relationships with fields from an Hstore?
As a better solution (what worked out for me in the end), please consider making an intermediate model to match Shops to Products, (maybe you'll want to name this intermediate model something like Shop_product, with a join table shops_products, but the name is up to you) then join both models using the intermediate model with a has_many through: relationship as detailed here:
http://edgeguides.rubyonrails.org/association_basics.html#the-has-many-through-association
Something like:
class Shop << ActiveRecord::Base
has_many :shop_products
has_many :products, through: :shop_products
end
class ShopProduct << ActiveRecord::Base
belongs_to :shop
belongs_to :product
end
class Product << ActiveRecord::Base
has_many :shop_products
has_many :shops, through: :products
(There's more info about how to create all of that in the link, I recommend that read)
Now you will have an association set up, so you can get:
Shop.find_by_id(1).products
Product.find_by_id(1).shops
Finally, I think you can use ActiveRecord scopes to solve the second computational problem that you need to solve (that is, find the price per day in the past).
Scopes will allow you, ideally, to do queries like:
Shop.products.active_yesterday
class Product << ActiveRecord::Base
scope active_yesterday -> { where('updated_at BETWEEN ? AND ?', 1.day.ago.beginning_of_day, 1.day.ago.end_of_day) }
has_many :shop_products
has_many :shops, through: :shop_products
end
All of my code is not production-ready and not tested, but I think my examples and links should be enough to get you on the right path.
Let me know if you need more help and I can try to help.

HABTM associations in Rails : collecting and counting the categories of a model's children

I have a has_and_belongs_to_many relationship setup. It looks like this:
books have_and_belong_to_many categories
categories have_and_belongs_to_many books
a store has_many books
a book belongs_to a store
I'm trying to show how many books in each store belong to each category. So my view would show Store X has 200 books and 80 of them are mystery, 60 are non fiction, etc.
I have been trying out a bunch of different ways of doing this, but no success so far. I think I'm starting in the wrong place. Any direction would be much appreciated.
Thanks
This is Rails 4 and psql by the way.
Provided that you have a books_categories join table you can add a has_many :categories, through: :books association to which links stores and categories through books.
class Store < ActiveRecord::Base
has_many :books
has_many :categories, through: :books
end
That's the easy part. Now lets get each category and the books count (revised):
def books_per_category
categories.select('categories.id, categories.name, count(books.id) as count')
.group('categories.id, categories.name')
.map do |c|
{
name: c.name,
count: c.count
}
end
end
Courtesy of #jakub-kosiƄski
Generally, instead of using Rails' built-in 'has_and_belongs_to_many' method, it is better practice to use a join table. In this setup, you have three tables:
Books
Categories
BookCategories
The BookCategories (or whatever you decide to call it) is a join table that belongs_to both Books and Categories and has a Foreign ID of each. You would then use Rails' "has_many :through" to link the Books and Categories.
The store would have a 'has_many' relationship with books. With the prior relationship setup right, you can then use this method to get the count for a store for a particular category:
Store.books.where(category:'Mystery')

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.

ActiveRecord associations, has_many_through

I've been trying to wrap my head around these associations and I've run into a bit of a snag. The app I'm working on has 2 models at the moment: Ingredient, and Recipe. The logic behind each is as follows:
The Ingredient model contains info on food items, such as name, bulk price, etc.
The Recipe model contains the name of the recipe, preparation instruction, and a list of ingredients (along with the amount required for the recipe).
After digging through some of the questions on here, and watching a few railscasts I've determined that a has_many_through relationship may be better for what I'm attempting to do, since I'd like to include additional info about the ingredient in the recipe (amount required).
Based on my limited experience in this area, I'd like to approach it like this:
class Recipe < ActiveRecord::Base
has_many :ingredients_recipes
has_many :ingredients, :through => :ingredients_recipes
end
class Ingredient_Recipe < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
end
class Ingredient < ActiveRecord::Base
has_many :ingredients_recipes
has_many :recipes, :through => :ingredients_recipes
end
Where the ingredients_recipes table would have fields similar to this: recipe_id, ingredient_id, ingredient_qty (for storing how much of a particular ingredient is in a recipe)
Is this the proper way to approach it code-wise, based on the functionality I mentioned above? Any help would be greatly appreciated. Thanks!
Edit
I'd also like to be able to add these new ingredients to the recipe from the recipe form. Would this require anything extra, like having to do accepts_nested_attributes_for the through table, or is this something that Rails would handle on it's own?
The :has_many through approach works best when one object can have zero or many other objects connected & there are some properties associated with each connection.
For example, in your case, you'd be storing the amount of each ingredient along with the recipe_id an ingredient_id in the Connections table.
So, it looks GOOD to me. :)
However, you should consider naming your Ingredient_Recipe table to something more appropriate.