Created
January 13, 2016 19:52
-
-
Save stevenharman/63f5f07672bd7893b3d6 to your computer and use it in GitHub Desktop.
Trying to avoid an N+1 query when we only want the first record of an ordered ActiveRecord association.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Organization < ActiveRecord::Base | |
has_many :people, dependent: :destroy, inverse_of: :organization | |
has_many :locations, dependent: :destroy | |
has_many :activity_items, -> { order(last_activity_at: :desc) }, dependent: :destroy | |
has_one :last_activity, -> { order(last_activity_at: :desc) }, class_name: 'ActivityItem' | |
end | |
class ActivityItem < ActiveRecord::Base | |
belongs_to :organization | |
belongs_to :user | |
belongs_to :item, polymorphic: true | |
end | |
# item is one of Email::Message, PhoneCall, or Note | |
module Email | |
class Message < ActiveRecord::Base | |
belongs_to :thread, class_name: 'Email::Thread' | |
belongs_to :user | |
has_many :activity_items, as: :item, dependent: :destroy | |
end | |
end | |
class PhoneCall < ActiveRecord::Base | |
belongs_to :user | |
has_many :activity_items, as: :item, dependent: :destroy | |
end | |
class Note < ActiveRecord::Base | |
belongs_to :user | |
has_many :activity_items, as: :item, dependent: :destroy | |
end | |
# We need to load a list of Organizations, and each org's: Locations, People + ContactMethod, and last ActivityItem + Item (email, note, call). | |
# The killer here is that `last_activity` relation, which is just the most recent `ActivityItem` plus its associated Item. | |
# The N+1 ends up being the `ActivityItem#item` relationship. | |
organizations = current_dealership.organizations. | |
order(id: :desc). | |
includes(:locations, :last_activity, people: :contact_methods). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Defining the offending
has_one
association as this seems to do the trickThis works by ordering the
activity_items
table first byorganization_id
and then breaks any ordering ties usingcreated_at desc
. Then Postgres limits the result set such thatorganization_id
is distinct, which leaves only the "top" result for each organization.