#sql #ruby-on-rails #ruby
Вопрос:
Допустим, у нас есть модели ItemStatistic
и Book
. ItemStatistic
пример записи:
item_id: 15, book_id: 3, score: 0.25192368e4,
Пример книги:
id: 3, title: 'Harry Potter',
Нам нужно сгруппировать ItemStatistic
записи по item_id
, затем упорядочить результаты для каждого item_id
ключа по score
и ограничить количество значений для каждого item_id
ключа по 3
и возвращать не ItemStatistic
записи, а Book
. Пример:
{15: [{id: 3, title: 'Harry Potter'}, {id: 4, title: 'The Chronicles of Narnia'},...], 2: [...]}
или
{1: [{},{},{}], 2: [{},{},{}], 3:[{},{},{}]}
Ответ №1:
Если я правильно понимаю желание, должно сработать следующее
item_statistics_table = ItemStatistic.arel_table filter = Arel::Table.new('filtered_results') sub_query = item_statistics_table.project( item_statistics[Arel.star], Arel.sql('ROW_NUMBER() OVER( PARTITION BY item_statistics.item_id ORDER BY item_statistics.score DESC )').as('row_num') ) query = Arel::Nodes::As.new(sub_query, Arel.sql(filter.name)) join_clause = Arel::Nodes::InnerJoin.new( query, Arel::Nodes::On.new( Book.arel_table[:id].eq(filter[:book_id]) .and(filter[:row_num].lteq(3)) ) ) Book .select(Book.arel_table[Arel.star],filter[:item_id]) .joins(join_clause) .group_by(amp;:item_id)
Это должно привести к следующему SQL:
SELECT books.*, filtered_results.item_id FROM books INNER JOIN ( SELECT item_statistics.*, ROW_NUMBER() OVER( PARTITION BY item_statistics.item_id ORDER BY item_statistics.score DESC ) as row_number FROM item_statistics ) AS filtered_results ON books.id = filtered_results.book_id AND filtered_results.row_number lt;= 3
Затем мы группируем все книги по item_id
(виртуальному атрибуту), чтобы результат был
{1 =gt; [Book,Book,Book], 2 =gt; [Book,Book,Book]}
Результирующий размер массива должен быть