0

I have these 3 models here:

class User < ActiveRecord::Base
  has_many :parties, foreign_key: 'organizer_id'

  has_many :invitations, foreign_key: 'attendee_id'
end

class Party < ActiveRecord::Base
  belongs_to :organizer, class_name: 'User'

    has_many :invitations
    has_many :attendees, through: :invitations
end

class Invitation < ActiveRecord::Base
  belongs_to :party
  belongs_to :attendee, class_name: 'User'
  belongs_to :inviter, class_name: 'User'
end

I want to get all attendees for a specific party and sort them by those who invited the most people.

How do I do this?

Thanks for your help.

1 Answer 1

2

Couple of possible solution:

Dirty one

party.attendees.joins(:invitations).select('parties.*', 'COUNT(DISTINCT invitations.id) as invitation_count').group('parties.id').order('invitation_count')

Pros: might work

Cons: This is ugly, does not explain itself and you will end up with an extremely hard to query relation. Rather avoid.

Slightly cleaner

Sort it on the application level:

party.attendees.includes(:invitations).sort_by {|att| att.inivtaions.to_a.size }

Pros: It is more clear what you're doing

Cons: Returns non-querable array instead of relation. Still need explanation why you're calling to_a (to avoid N+1 problem).

The cleanest

Add an extra column invitations_count to attendees table, and add counter_cache: true to the belongs_to :attendee in your Invitation model. Profit!

party.attendees.order(:invitations_count)

Pros: Nice and self-explanatory. No need to load invitations from the database. Easily querable association.

Cons: You need to update all the records' counter_cache before you start using it:

Attendees.includes(:invitations).each { |att| att.invitation_count = att.invitation.to_a.size }

After this, rails will take care of holding the column in sync with a number of associated with attendee inivtations.

Sign up to request clarification or add additional context in comments.

2 Comments

Great answer! Thx for explaining in details!
Which solution did you tired?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.