質問: 現在チャットの既読管理するための機能を開発しています。 仕様について ------ * 各チャットルームに参加しているユーザーに対して、メッセージを送信する * 後からチャットルームに参加したユーザーにも過去のメッセージを表示する * チャットルーム内で個別にメッセージを送信する機能はありません データベースの各テーブルの詳細は以下の通りです。 ``` rooms - チャットルームテーブル id:integer - ID creator_id:integer - 作成者 room_users - チャットルーム参加者テーブル id:integer - ID room_id:integer - チャットルームID user_id:integer - 参加者ID messages - メッセージテーブル id:integer - ID content:text - メッセージ内容 sender_id:integer - 送信者ID room_id:integer - 送信先チャットルームID message_recipients - 受信メッセージテーブル id:integer - ID message_id:integer - メッセージID recipient_id:integer - 受信者ID is_read:boolean - 既読フラグ ``` ※Ruby on Railsで作成しているので、Railsの型で表記しています 既読管理はmessage_recipientsテーブルのis_readカラムをtrueにすることで実現しようと考えています。 既読を付けるタイミングは2つあり、1つ目はメッセージ一覧を表示したときです。 メッセージは最新の数件ずつ表示し、表示されたもののみ既読を付けます。 2つ目は、クライアント側でメッセージを受信したときです。 WebSocketを使用しており、リアルタイムにメッセージを受信できるので、 受信時にJavaScriptでAjaxを使って既読をサーバーに知らせます。 また、今後もしかしたらis_favoriteカラムを追加し、お気に入り機能を追加するかもしれません。 聞きたいこと ------ 今回は、既読管理時にmessage_recipientsテーブルのレコードを作成するタイミングについて質問したいです。 既読管理の方法として以下の2通りの方法を思いつきました。 [方法1] メッセージを送信する際にmessegesテーブルにレコードを追加し、 同時にmessage_recipientsテーブルに受信者全員分のレコードも追加する。 そして、既読が付いたときにis_readカラムをtrueにする方法。 [方法2] メッセージを送信する際にmessegesテーブルにレコードを追加する。 そして、既読がついたときに初めてmessage_recipientsテーブルレコードを作成し、 is_readカラムをtrueにする方法。 ※is_readカラムのデフォルト値をtrueにしても良いかしれません。 方法1の場合はメッセージを送信する際に大量のレコードを同時に 作成しなければならない場合があり、データ容量が大きくなったり、 送信処理に時間がかかったりする可能性があると思います。 方法2の場合は、既読が付いていないメッセージ一覧を表示するときなど、 関連テーブルが存在しないものを検索する必要があり、処理が大変になる可能性があると思います。 以上の2通りの方法でどちらが良いか、あるいは別に良い方法があれば教えていただきたいです。 よろしくお願いいたします。
回答: チャットルーム形式の場合メッセージIDで1件1件既読管理しているのか...という疑問が。 例えば未読がたまってる時、「未読を全て既読としてマーク」って機能をつけようと考えてるとします。 この時、数百件のデータを更新ないし挿入するのって微妙ですよねぇ... チャットルームを作ったことはないので、思いつきですが、僕なりの回答を。 えー、回答としては方法3ってとこでしょうかね。(良いとは限りませんが。) 下記のようなカラムをチャットルーム参加者テーブルにつけます。 `already_read_to:integer - どこまで読んだかのmessage_id` ただ頻繁に書き換えることを考えると別テーブルにはするかもしれません。 チャットルーム参加者ステータステーブル、とかにでも。 このIDベースで初期表示時、メッセージにジャンプします。 で、最新までスクロールが終わった時に、already_read_toの値を書き換えます。 これをもって既読とします。 あまりにメッセージが多い場合は、途中でalready_read_toの値を更新するかもしれませんし、バックスクロールで常にチャットメッセージの読み込み開始地点を動かさないようにするため、現在読んでいるメッセージのマーカーを列として追加するかもしれません。 例えば、`already_read_to`と`current`列の2つにします。ジャンプ位置の決定はcurrentで行い、未読開始マーカーとしてalready_read_toを使い、最新までスクロールするとalready_read_toを更新する、と。current列はチャット画面の表示中に一定時間経過した場合や、画面の切り替えごとに更新し、ジャンプ位置とします。 部分既読はデザイン的に結構難しいし、チャット欄のスクロールで内容を移動したりすることを考えると、個別に既読管理した場合でもその情報が正しいと言えるかは怪しいと思います。 厳密に既読管理しなきゃいけないんだったらこの案はダメですが、新規チャットルームを沢山生成するような運用なら十分とも言えますかね。 これで、1つないし2つの列だけ更新して全体の既読管理をします。 とりあえずは、一案として。