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])
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