Rails: Is there a more efficient way to query ActiveRecord than my current method?

rails

#1

I have nested resources:

  resources :tomes, only: [:index, :show] do
    resources :books, only: [:index, :show] do
      resources :parts, only: [:index, :show]
    end
  end

Modelwise: each tome has many books, each book has many parts and each part has many chapters. Each book belongs to :tome, etc…

I think I’m fine with that. But from here, I’m not sure…

I have a controller method that returns:

  @parts = book.parts.find_by_slug(params[:id])

For each of those parts I want:

  • The part number.
  • An extract from the first chapter.
  <ul>
    <% @parts.each do |part| %>
    <ul><%= link_to "Part #{part.number}", tome_book_part_path(Tome.find(@book.tome_id).slug, @book, part) %>
      <p class="chapter_extract"><%= part.chapters.first.text.truncate_words(30) %></p>
    </ul>
    <% end %>
  </ul>

I’m pretty green to ActiveRecord and SQL but it seems to me that there’s probably a more efficient way to do this.

Do I really need to fetch all those parts @parts = book.parts.find_by_slug(params[:id]) when all I want is the number of each part and the first thirty words from the first chapters?

Is there a better way of doing this?


#2

You have to mix in SQL to your ActiveRecord method chain to achieve this. Fortunately, ActiveRecord makes this fairly simple. The trick here is joining the chapters but only the first one. I’m going to assume there is a chapter number property on your Chapter model. For this case, I think you want something like this:

@parts = book.parts.joins("INNER JOIN chapters ON chapters.part_id = parts.id AND chapters.chapter_number = 1").select("parts.number as number", "substring(chapters.text, 0, 60) as snippet").where("parts.slug = ?", params[:id])

I don’t believe there is a sql function for selecting words, so I did a substring of 60 characters in my example.