Rails order by count based on values of column -
rails 5.1.2
i have 2 models student
, award
way:
class student < applicationrecord has_many :awards end class award < application record belongs_to :students # categories scope :attendance, -> { where(category: 0) } #...(other award categories) scope :talent, -> { where(category: 8) } # ranks # gold scope :rank_1, -> { where(rank: 1) } # silver scope :rank_2, -> { where(rank: 2) } # bronze scope :rank_3, -> { where(rank: 3) } end
award
has these columns: rank
, category
.
now, want top student given category. criteria is, order count of "gold" awards (rank 1
), order count of "silver" (rank 2
) awards, , order count of "bronze" (rank 3
) awards.
so, if student
meets top criteria category 0
(which handled attendance
scope described in model above), thought query should like:
student.joins(:awards).where(awards: { category: 0 }).group('students.id').order('count(awards.rank == 1) desc', 'count(awards.rank == 2) desc', 'count(awards.rank == 3) desc').take
however, returns student highest count of awards, regardless of rank. example, if remove take
, order looks this:
# |st.id | gold | slvr. | brnz. | ---------------------------------- 1 | 12 | 4 | 12 | 8 | ---------------------------------- 2 | 1 | 9 | 0 | 4 | ---------------------------------- 3 | 6 | 9 | 1 | 0 | ---------------------------------- 4 | 18 | 5 | 2 | 2 | ---------------------------------- ...
so, order i'm getting ids 12, 1, 6, 18, ...
, when should ids 6, 1, 18, 12, ...
.
i realize order('count(awards.rank == 1) desc', 'count(awards.rank == 2) desc', 'count(awards.rank == 3) desc')
part ordering count of awards total (rather count of awards particular value in column rank
).
i can solve adding counter cache each category of awards, isn't elegant nor flexible solution.
as bonus, after query returns successful result, search database again find students have same score (as there ties). i'm not aware of way in 1 query (perhaps means of subqueries after getting values each rank).
i think problem might double equal??
edit: more proper way (assuming mysql):
student.joins(:awards).where(awards: { category: 0 }).group('students.id').order('count(if(awards.rank = 1)) desc', 'count(if(awards.rank = 2)) desc', 'count(if(awards.rank = 3)) desc').take
first try code without it, if doesn't work, might want try this:
select students.id, gold, silver, bronze) students join on (select students.id id count(awards) bronze students join awards on students.id = awards.student_id awards.rank = 1 group students.id) q1 q1.id = students.id join on (select students.id id count(awards) silver students join awards on students.id = awards.student_id awards.rank = 2 group students.id) q2 q2.id = students.id join on (select students.id id count(awards) gold students join awards on students.id = awards.student_id awards.rank = 3 group students.id) q3 q3.id = students.id order gold, silver, bronze
Comments
Post a Comment