ruby-on-rails - 按带有分页的关联模型排序
问题描述
导轨:'4.2.10' kaminari:'1.0.1'
我正在处理一个非常简单的关系,其中 aConversation
有很多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
同时使用分页和分页排序时,一切似乎都变得混乱了。
有什么想法吗?
解决方案
该查询适用于 ajoins
而不是 a 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
,它按预期工作。
推荐阅读
- c++ - 如何计算字符串 C++ 中“xxx”的数量?
- php - 如何将两个或多个关联数组与键组合为前一个数组元素的值,创建一个多级数组?
- swift - 具有自定义类类型的 Swift CoreData 保存属性
- html - 如何使 child z-index 超过 parent 或任何其他方式来达到效果?
- apache-flink - 在我的简单应用程序中没有创建检查点文件
- postgresql - postgres数据与同一张表中的多个服务器同步
- ruby-on-rails - 如何在点击 ruby on rails 时从下拉列表中获取选定的值?
- angular - 如何在使用 observable 更新数组时更新 *ngFor 以及如何在网站加载时初始化 observable?
- typo3 - TYPO3 routeEnhancers 在根页面上带有后缀“.html”
- java - 在 UPDATE 语句中绑定和添加参数:PostgreSQL