web-dev-qa-db-ja.com

FacebookやGmailのようなスレッド化されたプライベートメッセージングシステムを作成する

私はGmailやFacebookのようなスレッドメッセージシステムを作成しています。受信トレイには、件名、送信者の名前、最新メッセージのタイムスタンプを表示する最新のスレッドが一覧表示されます。

テーブルの設定方法は次のとおりです。

users:
    user_id
    user_name

thread:
    thread_id
    title
    to_id
    to_keep
    to_read
    from_id
    from_keep
    date

message:
    message_id
    thread_id
    to_id
    from_id
    message_text
    date

ユーザーが新しいメッセージを作成すると、スレッドテーブルに新しいスレッドが作成され、メッセージテーブルに新しいメッセージが作成されます。ユーザーがスレッドに応答すると、現在のスレッドがそれ以外のスレッドテーブルは、to_idfrom_idを交換し、それに基づいて新しいメッセージを作成します。

また、受信トレイビューでは、user_idに基づいてすべてのスレッドをクエリすることができます。 SELECT * FROM thread WHERE to_id = 2 and to_keep = TRUE ORDER BY date DESCのようなものか、送信トレイでメッセージを表示したい場合はSELECT * FROM thread WHERE from_id = 2 and from_keep = TRUE ORDER BY date DESCのようになります。

新しいメッセージがあるときにユーザーがスレッドを開くと、to_readがtrue UPDATE thread SET to_read = TRUE WHERE thread_id = 4に更新されます。

私はこのプロセスを複雑にしていますが、これを行うにはもっと良い方法があるはずだと感じています。

任意の助けやアイデアをいただければ幸いです。

この方法では、スレッドテーブルからすべてを選択し、ユーザーテーブルと結合して、必要なすべてを表示します。しかし、これを行うにはもっと良い方法があるはずだと私は感じています。

32
bigmike7801

メッセージの関係を各メッセージのユーザーの視点から分離してみませんか?

メッセージの自己参照関係でスレッディングを行います。つまり、メッセージには「responding_to_message_id」列があります。

"to_id"がある理由がわかりません。メッセージは個々のユーザーに送信されますか?これは非常に限られているようです。受信者がいない(つまり、受信者が誰でも読むことができるメッセージボードである)か、電子メールと同じように複数の受信者を指定できると思います。おそらく、システムの使用方法について詳しく説明できます。

(簡単にするために)ボードに投稿していると想定し、「from」だけが重要である場合、スレッド化の自己参照関係を持つメッセージテーブル、ユーザーテーブル、そしてユーザーとメッセージの間の交差テーブルがあります。各ユーザーがどのメッセージを読んだかを保存します。

このようにして、ユーザーがメッセージを読んだかどうかを知りたい場合は、特定のメッセージの交差テーブルにあるユーザーIDを読み取ってみてください。 ではないがある場合、そのメッセージはそのユーザーによって未読です。

単一の受信者が必要な場合はこの設計が保持され、複数の受信者が必要な場合は、交差テーブルを使用して各メッセージの受信者のリストを保持できます。受信者交差テーブルがある場合は、読み取りステータステーブルの役割を2倍にできます。

編集:ERDスケッチ:

これが私が話していることの簡単なスケッチです...

ERD Sketch

送信者がメッセージを保持することを選択したかどうかは、メッセージ自体にフラグが付けられます。メッセージが新しいスレッドの開始である場合、reply_to_message_id列はNULLです。それ以外の場合は、親メッセージのmessage_idです。複数の受信者が存在する可能性があり、それぞれがメッセージを保持するかどうかを決定する独自の機能と、受信者がメッセージを読んだ日時を追跡する機能があります。

編集2:最新のメッセージの代替ERDおよびクエリ

@OPは、スレッド内の最新のメッセージを照会する方法を尋ねました。答えはスレッドの形式によって異なります。スレッドのルートでない限り、すべてのメッセージがメッセージの線形ストリームの最後に移動するフラットスレッドを使用するか、各メッセージに特定の親があるツリー型のスレッドを使用することができます。上記のERDでは、reply_to_message_idフィールドはどちらの方法でも使用できます。スレッドがフラットの場合、FKは常にルートMESSAGEになります。スレッドがツリー型の場合、FKは応答MESSAGEの直接の親になります。

実行する典型的なクエリが「スレッド内の最新のメッセージは何ですか?」スレッドがフラットであれば、SQLを次のように使用できます。

select top 1
  M.message_id
, M.sent_datetime
, M.title
, M.message_text
, S.user_id
, S.user_name
-- and anything else you want...
from MESSAGE M inner join USER S
  on M.sender_user_id = U.user_id
where M.reply_to_message_id = @ThreadRootMessageID
order by
  M.sent_datetime desc

一方、スレッドがツリー型であり、これが迅速かつ簡単に実行できるようにしたいクエリである場合、上記のERDのスキーマの操作はそれほど簡単ではありません。 SQLは木が苦手です。非正規化を少し行うことで問題を解決できます。以下のERDを参照してください。

Tree Thread ERD

現在、直接の親を示す1つのFKと、ルートを示す1つのFKがあることに注意してください。スレッドは編集の対象ではないため、少なくともメッセージのルートが別のスレッドを指すように変更されている場合、これが伴う非正規化は更新の異常のリスクを意味しないため、冗長性があまり問題になりません。

このERDを使用する場合、「スレッドXの最新メッセージ」のクエリは上記と同じですが、where句にM.reply_to_message_idではなくM.thread_root_message_idが含まれます。

46
Joel Brown