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
,您仍然可以访问其他页面;他们每个人都有非常有限的记录。
此外,如果我删除了.page(params[:page])
并摆脱了分页,则所有结果都会显示我是在Conversation
日期还是ConversationMessage
日期之前订购。
只有在按ConversationMessage
进行订购并同时使用分页时,一切似乎才变得混乱。
有什么想法吗?
答案 0 :(得分:1)
该查询适合于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,但也会导致错误。
要正确执行此操作,您需要使用子查询。通过使用ActiveRecord鲜为人知的#from
方法,您可以在Rails环境中进行此操作。这样,您可以生成子查询,然后对该子查询进行操作,例如,通过对其进行排序或使用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
进行类似的查询,并且它按预期工作。
答案 1 :(得分:0)
基本上,您需要明智地将结果分组进行对话,因为从对话到session_messages都有一对多的关系。我会做这样的事情
@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])
希望这会有所帮助。