Antonio Fergesi
Antonio Fergesi

Reputation: 181

Cleanest way to query polymorphic association?

Problem: I have a working solution, but have a sneaking suspicion that it's probably quite calculation heavy. Would love to hear any more efficient ways to achieve this.

Goal: I want to pull a list of books, ordered by priority, where the book is EITHER polymorphically associated with school or class.

Current class view file:

<%  @school_class_books.order("priority ASC").each do |book| %>
    <b><a href="<%= book.url %>"><%= book.name %></a></b><br />
    <%= book.long %><br /><br />
<% end %>

Current class.rb controller:

@school_class_books = Book.all.where(bookable_type: ["School","Class"],
 bookable_id: [@class.school.id,@class.id])

Upvotes: 1

Views: 46

Answers (1)

Luke Griffiths
Luke Griffiths

Reputation: 899

Before any performance issues are considered, you should know that your code as written will cause bugs by finding Books that should not be found.

For example, if @school.id = 10 and @class.id = 15, this query will return a book with its bookable fields set to: bookable_id = 15 and bookable_type = "School".

That book belongs to a different school!

It may be simpler to do:

@books = Book.where(bookable: @school).to_a.concat(Book.where(bookable: @class).to_a)

This is polymorphic syntax sugar for:

@books = Book.where(bookable_type: @school.class.to_s, bookable_id: @school.id).to_a.concat(Book.where(bookable_type: @class.class.to_s, bookable_id: @class.id).to_a)

In other words, simply do two lookups and combine the results.

As for performance, using syntax like where(my_attribute: [value1, value2, value3]) will produce SQL like WHERE books.my_attribute IN (value1, value2, value3).

SQL statements using IN can be inefficient because they make it harder for the database server to use indexes on the my_attribute field (in this case bookable_id).

Finally, you should consider renaming your Class model to Course to avoid namespace collisions with Ruby's class keyword, or programmer confusion when reading variable names. Consider the awkwardness of bookable_type: @class.class.to_s

Upvotes: 1

Related Questions