Rails,SQL:私聊,如何在每个对话中查找最后一条消息

时间:2013-11-19 13:10:10

标签: sql ruby-on-rails-3

我得到了下面的架构

+----+------+------+-----------+---------------------+--------+
| id | from | to   | message   | timestamp           | readed |
+----+------+------+-----------+---------------------+--------+
| 46 |    2 |    6 | 123       | 2013-11-19 19:12:19 |      0 |
| 44 |    2 |    3 | 123       | 2013-11-19 19:12:12 |      0 |
| 43 |    2 |    1 | ????????? | 2013-11-19 18:37:11 |      0 |
| 42 |    1 |    2 | adf       | 2013-11-19 18:37:05 |      0 |
+----+------+------+-----------+---------------------+--------+

from / to是用户的ID,消息 - 显然是消息,时间戳和读取标志。 当用户打开他的个人资料时,我希望他在此对话框中查看他参与的最后一条消息的对话列表。 为了找到2个人之间的对话,我编写了这段代码,这很简单(消息模型):

def self.conversation(from, to)
  where(from: [from, to], to: [from, to])
end

所以,我现在可以对消息进行排序并获取最后一个消息。但是为每个对话框发出大量查询并不酷。 我怎么能用更少的查询来实现我正在寻找的结果?

更新

好吧,看起来不太清楚,我想要实现的目标。 例如,4个用户--Kitty,Dandy,Beggy和Brucy使用了这个聊天。 当布鲁西进入对话时,她会看到

Beggy: hello brucy haw ar u! | <--- the last message from beggy
------- 
Dandy: Hi brucy! | <---- the last message from dandy
--------
Kitty: Hi Kitty, my name is Brucy! | <–– this last message is from current user

所以,三个分开的对话框。然后,Brucy可以进入任何对话框继续私人谈话。 而且我无法弄清楚如何在不为用户之间的每个对话框触发查询的情况下获取此记录。

2 个答案:

答案 0 :(得分:0)

这个答案有点晚了,但至少在Rails 3.2.x中似乎没有很好的方法。

然而,这是我提出的解决方案 (因为我在我的网站上遇到了同样的问题)。

  @sender_ids =
    Message.where(recipient_id: current_user.id)
           .order("created_at DESC")
           .select("DISTINCT owner_id")
           .paginate(per_page: 10, page: params[:page])
  sql_queries =
    @sender_ids.map do |user|
      user_id = user.owner_id
      "(SELECT * FROM messages WHERE owner_id = #{user_id} "\
      "AND recipient_id = #{current_user.id} ORDER BY id DESC "\
      "LIMIT 1)"
    end.join(" UNION ALL ")

  @messages = Message.find_by_sql(sql_queries)
  ActiveRecord::Associations::Preloader.new(@messages, :owner).run

这会收到您向其发送邮件的最后10位唯一身份用户。 对于每个人,它会创建一个UNION ALL查询,以便将最后一条消息发送给这10个唯一身份的每个人。对于50,000行,查询在大约20ms内完成。当然要获得预加载的关联,使用.includes时必须使用.find_by_sql

答案 1 :(得分:-1)

def self.conversation(from, to)
  order("timestamp asc").last
end

修改

这个railscast会有所帮助..

http://railscasts.com/episodes/316-private-pub?view=asciicast

<强> EDIT2:

def self.conversation(from, to)
    select(:from, :to, :message).where(from: [from, to], to: [from, to]).group(:from, :to, :country).order("timestamp DESC").limit(1)
end