A
A
aykc2015-09-13 14:00:17
Ruby on Rails
aykc, 2015-09-13 14:00:17

How to extract the number of entries and the last entry in a has_many through association?

There are three models:

class Category < ActiveRecord::Base
  has_many :posts, dependent: :destroy
  has_many :comments, through: :posts, dependent: :destroy
end

class Post < ActiveRecord::Base
  has_many :comments, dependent: :destroy
  belongs_to :category
end

class Comment < ActiveRecord::Base
  belongs_to :post
  belongs_to :category
end

I need to extract a specific category (params[:id]), all posts in that category, as well as the number of comments for each post and the last comment for each post. How to do it as efficiently as possible (memory, performance)?
Currently implemented like this:
def show
  @category = Category.includes(:comments, :posts).find(params[:id])
end

In the template I output accordingly:
<h1>@category.name</h1>
<% @category.posts.each do |post| %>
  <%= post.name %>
    <%= post.comments.count %>  <!-- либо post.comments.size, работаем с массивом, не делаем лишние запросы -->
    <%= post.comments.last.body %>
<% end %>

I understand that all comments are loaded completely, although only the latest and their number are needed for each post. If there are a lot of posts in a category and comments in each post, then, accordingly, this is not good. If you use joins instead of includes, then you get a lot of queries in the database. I shoveled the whole stackoverflow and railcasts, but I didn’t find a solution, or I came across rails3 and the tasks are a bit different. I need on rails 4. I hope for your help, I have not slept for the second day.
Thanks in advance.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
@
@mgyk, 2015-09-13
@aykc

If you don’t want to pull everything out, then just make separate requests for the number of comments and the last one and remove from comments from includes.

@comment_cnt = Comment.where(post_id: @category.post_ids)).group(:post_id).count
@comment_last = Comment.where(post_id: @category.post_ids).group(:post_id).having('id = MAX(id)')

Then just @comments_last[post_id] and @comment_cnt[post_id]

A
Anton Dyachuk, 2015-09-13
@Renius

You are not saying something
parent.children.last will only load 1 object per 1 request to the parent and 1 request to the child
parent.children.count will make 1 request to the parent and 1 request to count to the child
of all children (comments in your case) load won't
or, I suspect, since I don't see your problematic queries, I can only guess:
try to include/join comments to posts, not categories
@category.posts.includes(:comments).each do |post|

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question