按带分页的关联模型排序
Order by associated model with pagination
rails: '4.2.10'
雷:“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
日期订购。
只是当按 ConversationMessage
排序并且同时使用分页时,一切似乎都变得一团糟。
有什么想法吗?
基本上您需要明智地对结果对话进行分组,因为从对话到 conversation_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])
希望对您有所帮助。
查询适合 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
子句时才存在。
为了一成不变,我们必须给子查询一个名字(你会想使用 table 的名字以避免出现问题),然后从子查询中获取所有内容。然后我们可以计算结果记录,我们也可以可靠地根据该属性进行排序或过滤:
>> @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
或者,如果您愿意,可以将方法编写为 class 的范围;结果是一样的。现在您可以编写清晰、富有表现力的查询:
>> 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
尝试了类似的查询,它按预期工作。
rails: '4.2.10' 雷:“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
日期订购。
只是当按 ConversationMessage
排序并且同时使用分页时,一切似乎都变得一团糟。
有什么想法吗?
基本上您需要明智地对结果对话进行分组,因为从对话到 conversation_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])
希望对您有所帮助。
查询适合 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
子句时才存在。
为了一成不变,我们必须给子查询一个名字(你会想使用 table 的名字以避免出现问题),然后从子查询中获取所有内容。然后我们可以计算结果记录,我们也可以可靠地根据该属性进行排序或过滤:
>> @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
或者,如果您愿意,可以将方法编写为 class 的范围;结果是一样的。现在您可以编写清晰、富有表现力的查询:
>> 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
尝试了类似的查询,它按预期工作。