简体   繁体   English

Rails 5:如何通过关联的模型属性对模型进行排序,并使其与众不同

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM