Сортировать по связанной модели с нумерацией страниц - PullRequest
0 голосов
/ 06 декабря 2018

rails: '4.2.10' kaminari: '1.0.1'

Я работаю с очень простыми отношениями, где Conversation имеет много ConversationMessages.Я отображаю эти Conversation записи в индексе и хочу отсортировать их по тем, которые имеют самые последние ConversationMessage.Здесь все становится интереснее.

def index
    @conversations = current_user.conversations.includes(:conversation_messages).order("conversation_messages.created_at DESC").page(params[:page])
end

Что происходит, когда я заказываю с использованием conversation_messages.created_at, большинство записей не отображаются в представлении, на самом деле нумерация страниц полностью исчезла;И все же, если я добавлю ?page=3 к URL, вы все равно сможете получить доступ к дополнительным страницам;все они имеют очень ограниченное количество записей на каждой.

Кроме того, если я удаляю .page(params[:page]) и избавляюсь от нумерации страниц, все результаты отображают, заказываю ли я к дате Conversation или ConversationMessage date.

Это просто при заказе по ConversationMessage И нумерация страниц используется одновременно, когда все кажется бесполезным.

Есть мысли?

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Запрос поддается joins, а не includes.Вы можете сделать что-то вроде одного запроса:

@conversations = current_user.conversations
    .select('conversations.*, max(conversation_messages.created_at) as recent_message_date')
    .left_joins(:conversation_messages).group('conversations.id').order('recent_message_date desc')

Это даст желаемый результат, но он ненадежен.Например, если вы сделаете:

>> @conversations.count

Вы получите ошибку, потому что ActiveRecord заменит предложение select чем-то, что вы не намеревались.Вызов size производит другой SQL, но также приводит к ошибке.

Чтобы сделать это правильно, вам нужно использовать подзапрос.Вы можете сделать это, оставаясь в среде Rails, используя малоизвестный метод ActiveRecord #from.Это позволяет генерировать подзапрос и затем работать с этим подзапросом, например, упорядочивая его или фильтруя с помощью предложения where.

>> subquery = current_user.conversations
     .select('conversations.*, max(conversation_messages.created_at) as recent_message_date')
     .left_joins(:conversation_messages)
     .group('conversations.id')

subquery теперь является ассоциацией ActiveRecord, которая содержит conversation объекты, которые вы хотите, каждый из которых имеет предварительный атрибут recent_message_date.Я говорю предварительный, потому что (как показано ранее) атрибут существует, только если ActiveRecord не решит связываться с нашим предложением select.

Чтобы установить вещи в камне, мы должны дать подзапросу имя (вы хотите использовать имя таблицы, чтобы избежать проблем), а затем извлечь все из подзапроса.Затем мы можем подсчитать итоговые записи и также можем надежно отсортировать или отфильтровать этот атрибут:

>> @conversations = Conversation.from(subquery, :conversations)
     .order('recent_message_date desc')

Чтобы упростить задачу, мы можем создать один или два метода:

class Conversation < ApplicationRecord
  def self.with_recent_message_date
    select('conversations.*, max(conversation_messages.created_at) as recent_message_date')
         .left_joins(:conversation_messages)
         .group('conversations.id')
  end

  def self.most_recent_first
    order('recent_message_date desc nulls last')
  end
end

Или, если хотите, вы можете написать методы в качестве области видимости для вашего класса;результат тот же.Теперь вы можете писать понятные, выразительные запросы:

>> subquery = current_user.conversations.with_recent_message_date
>> @conversations = Conversation.from(subquery, :conversations).most_recent_first

Кроме того, вы можете добавить фильтр или что-то еще, используя новый атрибут:

>> @conversations.where('recent_message_date > ?', Time.current - 3.days)

, и вы должны быть в состоянии надежноразбить этот результатЯ пробовал подобный запрос, используя will_paginate, и он работает как ожидалось.

0 голосов
/ 06 декабря 2018

По сути, вам нужно сгруппировать свой разговор по результатам, так как существует отношение один-ко-многим от разговора к разговорному сообщению.Я бы сделал что-то вроде этого

@conversations = current_user.conversations
  .joins(:conversation_messages)
  .group("conversations.id, recent_message_date")
  .order("recent_message_date DESC")
  .select("conversations.*, conversation_messages.created_at as recent_message_date")
  .page(params[:page])

Надеюсь, это поможет.

...