[英]Rails 5: How to order models by associated model attribute, and make them distinct
I have a scope in rails that is supposed to order a set of conversations by the last chat message created_at attribute. 我在rails中有一个作用域,应该通过最后一个聊天消息created_at属性来订购一组对话。 That goes like this: 就像这样:
class Conversation < ApplicationRecord
has_many :chat_messages, -> { order(created_at: :asc) }, dependent: :nullify do
def last_from_user
from_user.order("created_at ASC").last
end
def last_delivered_from_user
from_user.order("delivered_at ASC").last
end
def last_from_agent
from_agent.order("created_at ASC").last
end
end
default_scope { joins(:chat_messages).order("chat_messages.created_at desc").distinct }
end
When, for example, an organization with id 4 gets a new message I expect the order to be: 例如,当一个ID为4的组织收到一条新消息时,我希望订单为:
[4,1,2,3]
However, what I get is: 但是,我得到的是:
[1,2,3,4]
if I remove the distinct method like this: 如果我删除像这样的独特方法:
class Conversation < ApplicationRecord
default_scope { joins(:chat_messages).order("chat_messages.created_at desc") }
end
Conversations are ordered fine, but get duplicated: [4,1,2,3,4] 会话被安排得很好,但是重复了:[4,1,2,3,4]
my chat_message model looks like this: 我的chat_message模型如下:
class ChatMessage < ApplicationRecord
# Associations
# ------------------------------------------------------------
belongs_to :conversation
end
I cannot use the 'unique' method because this would make it impossible for the pagination to work appropriately. 我无法使用“唯一”方法,因为这将使分页无法正常工作。
However, when a conversation has more than one message the 'distinct' method messes up with the order. 但是,当对话中包含多个消息时,“区别”方法会弄乱顺序。 this scope returns the conversations in the order they were created and not the order I actually need. 此作用域按创建的顺序(而不是我实际需要的顺序)返回对话。
If we remove Rails and just look at the SQL its clearer. 如果我们删除Rails并只看一下SQL,它就会更加清晰。 What you have is a classic SQL problem of trying to get a list sorted by the max or min based on a join. 您所遇到的是一个经典的SQL问题,即试图获取基于联接的最大或最小排序的列表。
You're running a query like this: 您正在运行这样的查询:
SELECT conversations.*
FROM conversations
INNER JOIN chat_messages
ON chat_messages.conversation_id = conversations.id
ORDER BY chat_messages.created_at desc
And you will get back a row for every pair of conversations and chat_messages . 您将在每一对对话和chat_messages中返回一行 。 That's why you have duplicates. 这就是为什么重复的原因。
You can't use distinct
with this. 您不能对此使用不distinct
。
SELECT conversations.*
FROM conversations
INNER JOIN chat_messages
ON chat_messages.conversation_id = conversations.id
ORDER BY chat_messages.created_at desc
ERROR 3065 (HY000): Expression #1 of ORDER BY clause is not in SELECT list,
references column 'test.chat_messages.created_at' which is not in SELECT list;
this is incompatible with DISTINCT
If you use select distinct conversations.*, chat_messages.created_at
you're back where you started. 如果您使用select distinct conversations.*, chat_messages.created_at
您将回到开始的地方。
I'm not sure what .distinct
is doing, but it should probably raise an exception. 我不确定.distinct
在做什么,但是它可能会引发异常。
Instead, get rid of the duplicates with group by conversation.id
. 相反,请删除带有group by conversation.id
的重复项。 Now that you have a grouped query you can order by max(chat_messages.created_at) desc
. 现在您有了分组查询,可以order by max(chat_messages.created_at) desc
。
select c.*
from conversations c
join chat_messages cm
on cm.conversation_id = c.id
group by c.id
order by max(cm.created_at) desc
Translating this into Rails... 将其转换为Rails ...
Conversations
.joins(:chat_messages)
.group(:id)
.order("max(chat_messages.created_at) desc")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.