2010-03-10 74 views
3

我有兩個型號:這可能在一個查詢中完成嗎?

class User 
end 

class Message 
belongs_to :sender, :class_name=> 'User' 
belongs_to :recipient, :class_name=> 'User' 
end 

我希望獲取通過最新消息的日期是什麼給用戶和他的好友和之間出現在談話下令給定用戶的所有好友,如果有可能的相同的查詢,以獲取其對話中的消息數量。

現在,我被困在此:

Messages.all(:joins => :sender, 
    :conditions => ['sender_id = ? OR recipient_id = ?', some_user.id, some_user.id], 
    :select => 'users.*, sender_id, recipient_id, MAX(messages.created_at) as last_created, COUNT(messages.id) as messages_count', 
    :group => 'messages.sender_id, messages.recipient_id', 
    :order => 'last_created DESC' 

該查詢產生這樣的輸出:

一)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count 
user1 | 1   | 2   | bla    | bla 
user1 | 1   | 3   | bla    | bla 
user1 | 1   | 4   | bla    | bla 

因爲模型由messages.sender_id = user.id加入我只有USER1記錄提取但我需要user2,user3user4記錄在特殊情況下當user1只發送消息給他的好友。

B)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count 
user2 | 2   | 1   | bla    | bla 
user3 | 3   | 1   | bla    | bla 
user4 | 4   | 1   | bla    | bla 

在情況B,否則,我有什麼,我想有 - 通過最新的消息顯示的內容在給定的用戶和他的朋友之間的談話日期排序,所有三個哥們。

C)

users.* | sender_id | recipient_id | MAX(last_created) | messages_count 
user1 | 1   | 2   | bla    | bla 
user3 | 3   | 1   | bla    | bla 
user4 | 4   | 1   | bla    | bla 

現狀C. USER2作爲USER1的好友丟失原因:joins => :sender。否則,如果:joins => :recipient將缺少user3和user4。這就是餅乾。 不管我們如何加入模型。如何在一個查詢中解決這種情況?

+0

只是一件事:它是':class_name'。 – 2010-03-10 11:55:12

+0

謝謝,拼寫錯誤 – yukas 2010-03-10 13:27:46

回答

2

您需要select_extra_columns寶石返回加入/聚合列。假設你已經安裝了寶石,修改你的用戶模型如下所示。

class User 
    select_extra_columns 


    def friends_with_conversation 
    User.all(
    :select => "users.*, b.last_message_at, b.message_count", 
    :joins => " 
      RIGHT JOIN 
      (SELECT IF(a.sender_id=#{self.id}, a.recipient_id, 
          a.sender_id) AS friend_id, 
         MAX(a.created_at) AS last_message_at, 
         COUNT(a.id)  AS message_count 
       FROM  messages AS a 
       WHERE a.sender_id = #{self.id} OR 
         a.recipient_id = #{self.id} 
       GROUP BY IF(a.sender_id=#{self.id}, a.recipient_id, 
          a.sender_id) 
      ) AS b ON users.id = b.friend_id 
      ", 
     :order => "b.last_message_at DESC",  
     :extra_columns => {:last_message_at=>:datetime, :message_count => :integer} 
    ) 
    end 
end 

現在您可以撥打以下電話獲取好友詳情。

user.friends_with_conversation.each do |friend| 
    p friend.name 
    p friend.last_message_at 
    p friend.message_count 
end 

您需要的寶石在查詢返回的User對象返回last_message_atmessage_count

編輯 我對PostgresSQL並不熟悉。對文檔的粗略閱讀表明,遵循SQL可能有效。

:joins => " 
RIGHT JOIN 
(SELECT CASE WHEN a.sender_id=#{self.id} 
       THEN a.recipient_id 
       ELSE a.sender_id 
      END    AS friend_id, 
      MAX(a.created_at) AS last_message_at, 
      COUNT(a.id)  AS message_count 
    FROM  messages AS a 
    WHERE a.sender_id = #{self.id} OR 
      a.recipient_id = #{self.id} 
    GROUP BY CASE WHEN a.sender_id=#{self.id} 
       THEN a.recipient_id 
       ELSE a.sender_id 
      END 
) AS b ON users.id = b.friend_id 
" 
+0

謝謝,我認爲它實際上可能工作,但我在PostgreSQL中開發,似乎不能理解這種語法。你介意爲PostgreSQL添加這個查詢的版本嗎?再次感謝。 – yukas 2010-03-12 07:43:38

+0

我已經將PostgresSQL添加到我的答案 – 2010-03-12 08:39:53

+0

工程就像一個魅力。謝謝。非常複雜的答案,非常有幫助。我發現,所有作品沒有select_extra_columns gem。無論如何,爲什麼我需要它? – yukas 2010-03-12 09:03:03

1

看起來你想要一個替代的「連接」字符串(即不是:joins => :sender)。 :joins => :recipient會給出情況B的正確答案嗎?

如果不是 - 您也可以將手工製作的SQL傳遞給:joins密鑰並按照您的喜好加入表格。

看起來有一個很好的教程覆蓋聯接位置: http://www.railway.at/articles/2008/04/24/database-agnostic-database-ignorant/

+0

:joins =>:收件人爲B中的所有記錄提供user1。因此,這是不正確的。在那:)的整個伎倆。 – yukas 2010-03-10 13:32:47

+0

剛剛編輯。我添加了涵蓋您的問題的情況C. – yukas 2010-03-10 13:39:51

相關問題