Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveRecord - select first record from each group

I want most recent message sent by each users. Here is sample data

Table : conversations

sender  receiver message                        date
============================================================
    1   2   "hi"                            "2013-03-04 09:55:01.122074"
    2   1   "hello"                         "2013-03-04 09:55:32.903975"
    1   2   "have you done with the taks?"  "2013-03-04 09:57:46.383007"
    2   1   "almost..."                     "2013-03-04 09:58:55.783219"
    2   1   "should be able to finish 2day" "2013-03-04 09:59:28.950705"
    2   3   "shall we start?"               "2013-03-04 10:01:16.842725"
    3   2   "give me a minute"              "2013-03-04 10:01:41.994589"
    3   2   "let us start"                  "2013-03-04 10:02:14.04551"

Where for user with id 2, I should be able to get following two record

1   2   "have you done with the taks?"  "2013-03-04 09:57:46.383007"        
3   2   "let us start"                  "2013-03-04 10:02:14.04551"

Here is my solution

Model : User

class User < ActiveRecord::Base
has_many :chats_received, class_name: 'Conversation', foreign_key: 'receiver_id',order: "created_at DESC"
end

Model : Conversation

class Conversation < ActiveRecord::Base
  attr_accessible :message, :read

  belongs_to :sender, class_name: 'User'
  belongs_to :receiver, class_name: 'User'

  def most_recent_chat_received_from_connected_users
    connected_users_chats = . . . # Get all conversations which are sent to current user. e.g., user with id 2
    chats_grouped_by_senders = connected_users_chats.group_by { |conversation| conversation.sender_id }
    chats_grouped_by_senders.inject([]){|memo , (sender_id, conversations)| memo << conversations.first; memo}
  end
end

Get most recent message from connected users:

user = User.find 2
user.most_recent_chat_received_from_connected_users

Though this solution works, it select and create models for each conversion between two user. Also I feel it is not rails way of getting required rows.

I have been using postgresql. I have been getting following error when I try to use group method on the mode.

ActiveRecord::StatementInvalid: PG::Error: ERROR:  column "conversations.id" must appear in the GROUP BY clause or be used in an aggregate function

Is there any better way to get the same results?

like image 931
Amit Patel Avatar asked Mar 04 '13 16:03

Amit Patel


1 Answers

I'm not sure how you can call most_recent_chat_received_from_connected_users, which is an instance method of your Conversation class, on an instance of User without getting an error, but i would add a custom finder to the Conversation model:

class Conversation < ActiveRecord::Base
  # ...

  def self.most_recent_for(user_id)
    select('DISTINCT ON (sender_id) *').where(reciever_id: user_id).order("sender_id, created_at DESC")
  end

  # For MySQL you could have used:
  #
  # def self.most_recent_for(user_id)
  #   where(reciever_id: user_id).group("sender_id").order("created_at DESC")
  # end

  # ...
end

Now you can get the desired conversations in your controller with:

@conversations = Conversation.most_recent_for(current_user.id)
like image 166
mttmzr Avatar answered Oct 22 '22 11:10

mttmzr