Сложный MySQL Query для системы обмена сообщениями в Rails - пожалуйста, помогите - PullRequest
1 голос
/ 31 мая 2009

Я пишу систему обмена сообщениями в стиле Facebook для приложения Rails, и у меня возникают проблемы при выборе сообщений для входящих сообщений (с will_paginate).

Сообщения организованы в потоки, в папке «Входящие» появится самое последнее сообщение в теме со ссылкой на ее тему. Поток организован через отношения parent_id 1-n с самим собой.

Пока я использую что-то вроде этого:

class Message < ActiveRecord::Base
  belongs_to :sender, :class_name => 'User', :foreign_key => "sender_id"
  belongs_to :recipient, :class_name => 'User', :foreign_key => "recipient_id"
  has_many :children, :class_name => "Message", :foreign_key => "parent_id"
  belongs_to :thread, :class_name => "Message", :foreign_key => "parent_id"
end

class MessagesController < ApplicationController

  def inbox
    @messages = current_user.received_messages.paginate :page => params[:page], :per_page => 10, :order => "created_at DESC"
  end
end

Это дает мне все сообщения, но для одного потока появится сам поток и самое последнее сообщение (и не только самое последнее сообщение). Я также не могу использовать предложение GROUP BY, потому что для самого потока (так сказать, родителя) parent_id = nil, конечно.

Кто-нибудь получил идею, как решить это элегантным способом? Я уже думал о добавлении parent_id к самому родителю, а затем группировать по parent_id, но я не уверен, работает ли это.

Спасибо

Ответы [ 5 ]

0 голосов
/ 20 мая 2010

Я не знаю, как это сделать в Rails, но именно так я и сделал в MySQL:

выберите * из сообщений, в которых message_id ( выберите max (message_id) из сообщений, где to_uid = 51 group by thread_id ) упорядочить по метке времени desc

Я использовал подзапрос, чтобы получить самое последнее сообщение в потоке, а затем основной запрос, чтобы получить все поля сообщений, найденных в подзапросе.

0 голосов
/ 03 июня 2009

Я полагал, что единственным хорошим решением является использование второй модели для хранения самых последних сообщений для каждого потока (из-за проблем с производительностью при использовании GROUP BY с вложенным выбором, см. Мои комментарии). Это не займет много места в БД, потому что мы храним только идентификаторы, а не текст или даже BLOB-объекты.

Модель RecentMessages будет выглядеть примерно так:

create_table :recent_messages do |t|
  t.integer :sender_id
  t.integer :recipient_id
  t.integer :message_id
  t.integer :message_thread_id

  t.timestamps
end

class RecentMessage < ActiveRecord::Base

  belongs_to :message
  belongs_to :message_thread, :class_name => 'Message'
  belongs_to :sender, :class_name => 'User', :foreign_key => "sender_id"
  belongs_to :recipient, :class_name => 'User', :foreign_key => "recipient_id"

end

Основная идея: все сообщения хранятся в одной модели (сообщения). Каждый раз, когда новое сообщение добавляется в поток (или создается поток), происходят две вещи (например, с обратным вызовом after_save):

  • Сохранить новое сообщение в модели RecentMessages (что означает sender_id, receient_id, message_id, message_thread_id (= parent_id || id))
  • Получить самое последнее сообщение (из этой цепочки в сообщениях), где sender_id == receient_id и наоборот (примечание: это работает только в том случае, если модель сообщений должна поддерживать сообщения только между двумя пользователями), и сохранить его в модели RecentMessages как хорошо (если найден и если его там еще нет)

Конечно, должно быть только макс. 2 последних сообщения хранятся в БД для каждого сообщения в любой момент времени.

Если кто-то хочет показать входящие, должно произойти следующее:

@messages = current_user.recent_received_messages.paginate :page => params[:page], :per_page => 10, :order => "created_at DESC", :include => :message

Это лучшее, что я понял до сих пор. Я все еще думаю, что это некрасиво, но это быстро, и это работает. Если кто-нибудь придумает лучшее решение, я буду благодарен!

0 голосов
/ 01 июня 2009

Единственным эффективным способом было бы иметь модель Thread и использовать GROUP BY, как вы упомянули - все остальное потребует итерации по сообщениям.

читать обновления в комментариях

0 голосов
/ 01 июня 2009

, предоставляя самого родителя в качестве родителя, очень легко создавать запросы, которые работают со всем потоком, потому что вы можете группировать (или что-то подобное) по parent_id.

если вы по-разному относитесь к родителям, все ваши запросы тоже должны позаботиться об этом

0 голосов
/ 31 мая 2009

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

(псевдо-) код:

class Message < ActiveRecord::Base
  belongs_to :sender, :class_name => 'User', :foreign_key => "sender_id"
  belongs_to :recipient, :class_name => 'User', :foreign_key => "recipient_id"
  has_many :children, :class_name => "Message", :foreign_key => "parent_id"
  belongs_to :thread, :class_name => "Message", :foreign_key => "parent_id"

  def get_last_message_in_thread()
    last_message = self
    children.each do |c|
       message = c.get_last_message_in_thread()
       last_message = message if message.created_at > last_message.created_at
    end
    return last_message
  end
end

class MessagesController < ApplicationController

  def inbox
    @messages = current_user.received_messages.find_by_parent_id(Null).paginate :page => params[:page], :per_page => 10, :order => "created_at DESC"
  end
end

Возможно, вы могли бы сделать намного лучше, чем использовать рекурсивную функцию для поиска последнего сообщения в цепочке, но это самое простое решение, которое я могу придумать, чтобы продемонстрировать идею. Я также не уверен, что у меня правильный синтаксис для поиска неустановленных родительских идентификаторов в функции входящих сообщений, поэтому я пометил код как псевдокод:)

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