Hvis du ikke har noget imod at blive beskidte med lidt SQL, kan du bruge en vinduefunktion at få arbejdet gjort. Du kan få post-id'erne med denne SQL:
select id
from (
select id,
rank() over (partition by thread_id order by created_at desc)
from posts
where receiver_id = #{user.id}
) as dt
where rank = 1
Hvis du ønsker flere kolonner, skal du tilføje dem til begge SELECT-sætninger. #{user.id}
er selvfølgelig den modtager, du er interesseret i.
Den interessante del er vinduesfunktionen:
rank() over (partition by thread_id order by created_at desc)
Dette vil opdele tabellen i grupper baseret på thread_id
(en slags lokaliseret GROUP BY), sorter dem efter tidsstemplet (seneste først), og derefter rank()
giver 1 for den første post i hver gruppe, 2 for den anden osv.
Givet en tabel, der ser sådan ud:
=> select * from posts;
id | receiver_id | thread_id | created_at
----+-------------+-----------+---------------------
1 | 1 | 2 | 2011-01-01 00:00:00
2 | 1 | 2 | 2011-02-01 00:00:00
3 | 1 | 2 | 2011-03-01 00:00:00
4 | 1 | 3 | 2011-01-01 00:00:00
5 | 1 | 4 | 2011-01-01 00:00:00
6 | 1 | 3 | 2011-01-01 13:00:00
7 | 2 | 11 | 2011-06-06 11:23:42
(7 rows)
Den indre forespørgsel giver dig dette:
=> select id, rank() over (partition by thread_id order by created_at desc)
from posts
where receiver_id = 1;
id | rank
----+------
3 | 1
2 | 2
1 | 3
6 | 1
4 | 2
5 | 1
(6 rows)
Og så vikler vi den ydre forespørgsel omkring det for kun at fjerne de bedste kampe:
=> select id
from (
select id,
rank() over (partition by thread_id order by created_at desc)
from posts
where receiver_id = 1
) as dt
where rank = 1;
id
----
3
6
5
(3 rows)
Så tilføj de ekstra kolonner, du ønsker, og pak det hele ind i en Post.find_by_sql
og du er færdig.