I have a scope in rails that is supposed to order a set of conversations by the last chat message created_at attribute. 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:
[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]
my chat_message model looks like this:
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. What you have is a classic SQL problem of trying to get a list sorted by the max or min based on a join.
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 . That's why you have duplicates.
You can't use distinct
with this.
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.
I'm not sure what .distinct
is doing, but it should probably raise an exception.
Instead, get rid of the duplicates with group by conversation.id
. Now that you have a grouped query you can 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...
Conversations
.joins(:chat_messages)
.group(:id)
.order("max(chat_messages.created_at) desc")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.