2010-03-10 7 views
3

나는 두 가지 모델이 있습니다하나의 쿼리로이 작업을 수행 할 수 있습니까?

나는 주어진 사용자와 그의 친구와 사이에 대화에 표시되는 내용을 메시지의 가장 최근 날짜순으로 특정 사용자의 모든 친구를 가져 오기 할
class User 
end 

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

에 가능하다면 대화에서 메시지 수를 가져 오는 동일한 쿼리.

지금, 나는이에 붙어 :

A)

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을 가지고 있기 때문에 :

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' 

즉, 쿼리가이 출력을 생성 레코드를 가져 왔지만 필요합니다. user2, user3user4의 기록은 특별한 상황 A 일 때 사용자 1은 그의 친구들에게만 메시지를 보냅니다. 지정된 사용자와 그의 친구 사이의 대화에서 나타나는 어떤 메시지의 가장 최근 날짜별로 정렬 된 세 친구를 -

b)는 상황 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 

, 그렇지 않으면, 내가 갖고 싶은 있습니다.

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'입니다. –

+0

감사합니다, 맞춤법이 틀린 – yukas

답변

2

당신은/가입 집계 열을 반환하는 select_extra_columns 보석이 필요합니다. gem을 설치했다면 아래와 같이 사용자 모델을 수정하십시오.

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

+0

내 대답에 PostgresSQL을 추가했습니다 –

+0

매력처럼 작동합니다. 고맙습니다. 매우 복잡한 대답과 매우 도움이. 그리고 select_extra_columns gem이 없어도 모두 작동합니다. 어쨌든 왜 필요합니까? – yukas

1

대체 조인 문자열 (예 : :joins => :sender)이 아닌 것처럼 보입니다. :joins => :recipient은 상황 B에 대한 올바른 응답을 제공합니까?

그렇지 않은 경우 - :joins 키에 수작업으로 작성된 SQL을 전달하고 원하는대로 테이블을 조인 할 수도 있습니다.

좋은 튜토리얼 커버 여기가 조인처럼 보이는 : http://www.railway.at/articles/2008/04/24/database-agnostic-database-ignorant/

+0

: 조인 => :받는 사람은 B의 모든 레코드에 대해 user1을 제공합니다. 따라서 올바르지 않습니다. 그 모든 속임수 :). – yukas

+0

방금 ​​편집했습니다. 나는 당신의 문제를 다루는 상황 C를 추가한다. – yukas

관련 문제