Приложение Rails 3 с PostgreSQL - получение списка сообщений, сгруппированных по разговорам - PullRequest
1 голос
/ 06 сентября 2011

Я запускаю приложение rails 3, которое моделирует сообщения электронной почты. Приложение развернуто на Heroku, поэтому внутренняя база данных - это PostgreSQL. Потоки сообщений просто моделируются полем thread_id в таблице сообщений. Когда пользователь публикует новое сообщение, назовем его p1, затем p1.thread_id = p1.id. Если пользователь отвечает на p1 с помощью p2, тогда p2.thread_id = p1.thread_id.

Мне нужно написать запрос, чтобы выбрать сообщения, отправленные определенному пользователю. Результирующий список должен содержать только одно сообщение в потоке, и это сообщение должно быть последним сообщением в потоке. Мне также нужно знать, сколько сообщений в каждой теме.

С помощью следующего выбора:

SELECT DISTINCT ON(thread_id) * FROM "posts"

не работает, так как не возвращает сообщение, отсортированное по дате последнего.

Этот тоже не работает:

ВЫБРАТЬ DISTINCT ON (идентификатор_потока) * ОТ "сообщения", ЗАКАЗАТЬ по thread_id, posts.created_at DESC

, поскольку посты упорядочены в первую очередь по thread_id.

Таблица сообщений:

create_table "posts", :force => true do |t|
    t.string   "title"
    t.text     "content"
    t.string   "content_type"
    t.text     "options"
    t.string   "type"
    t.integer  "receiver_id"
    t.integer  "sender_id"
    t.integer  "circle_id"
    t.text     "data_bag"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "parent_id"
    t.integer  "thread_id"
    t.datetime "posted_at"
  end

Спасибо за любую помощь.

1 Ответ

2 голосов
/ 06 сентября 2011

Если вы не возражаете запачкать руки небольшим количеством SQL, вы можете использовать оконную функцию , чтобы выполнить работу. Вы можете получить идентификаторы постов с этим 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

Если вы хотите больше столбцов, добавьте их в оба предложения SELECT. #{user.id} - это, конечно, интересующий вас получатель.

Интересная часть - оконная функция:

rank() over (partition by thread_id order by created_at desc)

Это разделит таблицу на группы на основе thread_id (что-то вроде локализованного GROUP BY), упорядочит их по метке времени (сначала самое последнее), а затем rank() даст 1 для первой записи в каждой группе 2 для второго и т. Д.

Учитывая таблицу, которая выглядит следующим образом:

=> 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)

Внутренний запрос дает вам это:

=> 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)

А затем мы оборачиваем внешний запрос, чтобы отделить только самые ранжированные совпадения:

=> 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)

Итак, добавьте нужные столбцы и оберните их в Post.find_by_sql, и все готово.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...