質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.38%

  • データベース

    881questions

    データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

チャット機能の既読管理の方法について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 5,515

gesorein

score 86

現在チャットの既読管理するための機能を開発しています。

仕様について

  • 各チャットルームに参加しているユーザーに対して、メッセージを送信する
  • 後からチャットルームに参加したユーザーにも過去のメッセージを表示する
  • チャットルーム内で個別にメッセージを送信する機能はありません

データベースの各テーブルの詳細は以下の通りです。

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通りの方法を思いつきました。

[方法①]
メッセージを送信する際にmessegesテーブルにレコードを追加し、
同時にmessage_recipientsテーブルに受信者全員分のレコードも追加する。
そして、既読が付いたときにis_readカラムをtrueにする方法。

[方法②]
メッセージを送信する際にmessegesテーブルにレコードを追加する。
そして、既読がついたときに初めてmessage_recipientsテーブルレコードを作成し、
is_readカラムをtrueにする方法。
※is_readカラムのデフォルト値をtrueにしても良いかしれません。

方法①の場合はメッセージを送信する際に大量のレコードを同時に
作成しなければならない場合があり、データ容量が大きくなったり、
送信処理に時間がかかったりする可能性があると思います。

方法②の場合は、既読が付いていないメッセージ一覧を表示するときなど、
関連テーブルが存在しないものを検索する必要があり、処理が大変になる可能性があると思います。

以上の2通りの方法でどちらが良いか、あるいは別に良い方法があれば教えていただきたいです。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • kunai

    2017/05/08 13:57

    途中でチャットに参加したユーザーには過去のメッセージを見せるのでしょうか。また、チャットルーム内で個別の相手にのみ見えるメッセージ等、限定的なメッセージ機能はあるのでしょうか。それぞれ、今後の可能性についてもご検討の上教えてください。当然それにより返答が異なります。

    キャンセル

  • gesorein

    2017/05/08 14:15

    ご質問ありがとうございます!過去のメッセージも見せる予定です。また、限定的なメッセージ機能は検討していません。 ただし1対1(参加者が2人)のチャットルームは存在します。機能としてはFacebookのMessengerに似たようなものになると思います。

    キャンセル

  • szk.

    2017/05/08 15:51

    ユーザオペレーション単位またはclient側の処理単位で、既読になるタイミングはいつを想定していますか? ex) ユーザが既読ボタンみたいなものを押す時?画面がweb-apiを呼んだ時?etc...

    キャンセル

回答 2

checkベストアンサー

+2

方法①も②も、
受信メッセージテーブルのレコード作成は、非同期にすることでレスポンスの劣化はそれほどでもないと思います。
またメッセージ一覧はSQLでouter joinできるのであれば問題ありません。
質問にもありますが、新規ユーザが招待された時には特異な処理が必要です。

ただチャット素人の個人的な見解ですが、
チャットで未読か既読かをすべてのメッセージで管理する必要はないかと。

別の方法としては
最終既読日時(または最終メッセージID)をユーザとチャットルーム毎に作成して、
それより後は未読、それ以前は既読。
最終既読日時は都度updateで更新。

ご提示の方法と比べてできないことは

  • 受信ユーザが確認している確認していないに関係なく、最新のメッセージまで既読になる。
  • 任意のメッセージだけ既読/未読にすることできない。

くらいかと思います。想定の機能は踏襲できているかと。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/08 18:03 編集

    回答ありがとうございます。もしチャット参加者が大量にいた場合、
    データ容量が少し気になりますが、非同期で実行すればレスポンスに関しては問題ないですね。
    ただ、今回はWebSocketでリアルタイムで既読判定を行うので、
    処理の順序が入れ違いになった場合など想定することが多くなりそうです。

    方法②の場合、たしかにouter joinを使えば検索は可能ですね。
    たとえば、お気に入り機能の追加でis_favoriteカラムを追加した際など、
    未読メッセージを抽出するときに、message_recipientsのレコードが存在しない、
    または、レコードが存在して、かつis_readがtrueのときを考えれば大丈夫そうですね。

    また、日時での管理方法のご提案もありがとうございます。
    たしかに日時で既読管理すれば、処理も少なくすみますし実装も複雑になりにくいですね!
    各メッセージごとに既読を管理するのはけっこう大変そうなので、こちらの方法も検討してみます。

    キャンセル

+1

チャットルーム形式の場合メッセージIDで1件1件既読管理しているのか…という疑問が。
例えば未読がたまってる時、「未読を全て既読としてマーク」って機能をつけようと考えてるとします。
この時、数百件のデータを更新ないし挿入するのって微妙ですよねぇ…

チャットルームを作ったことはないので、思いつきですが、僕なりの回答を。
えー、回答としては方法③ってとこでしょうかね。(良いとは限りませんが。)


下記のようなカラムをチャットルーム参加者テーブルにつけます。

already_read_to:integer - どこまで読んだかのmessage_id

ただ頻繁に書き換えることを考えると別テーブルにはするかもしれません。
チャットルーム参加者ステータステーブル、とかにでも。

このIDベースで初期表示時、メッセージにジャンプします。
で、最新までスクロールが終わった時に、already_read_toの値を書き換えます。
これをもって既読とします。

あまりにメッセージが多い場合は、途中でalready_read_toの値を更新するかもしれませんし、バックスクロールで常にチャットメッセージの読み込み開始地点を動かさないようにするため、現在読んでいるメッセージのマーカーを列として追加するかもしれません。

例えば、already_read_tocurrent列の2つにします。ジャンプ位置の決定はcurrentで行い、未読開始マーカーとしてalready_read_toを使い、最新までスクロールするとalready_read_toを更新する、と。current列はチャット画面の表示中に一定時間経過した場合や、画面の切り替えごとに更新し、ジャンプ位置とします。

部分既読はデザイン的に結構難しいし、チャット欄のスクロールで内容を移動したりすることを考えると、個別に既読管理した場合でもその情報が正しいと言えるかは怪しいと思います。
厳密に既読管理しなきゃいけないんだったらこの案はダメですが、新規チャットルームを沢山生成するような運用なら十分とも言えますかね。

これで、1つないし2つの列だけ更新して全体の既読管理をします。

とりあえずは、一案として。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/08 17:13

    うーん…見事にかぶってしまったな… 違いとしては日時じゃないってことか。

    キャンセル

  • 2017/05/08 18:29

    回答ありがとうございます。
    やはり個別のメッセージごとに既読(部分既読)を管理しようとすると、
    かなり実装が煩雑になりそうですね...。
    room_usersに既読管理のカラム付ければ、
    既読のレコードの作成タイミングを気にしなくて済みそうです。

    日時でなくてもmessage_idでも実装できそうですね。
    実はすべて最新のメッセージから遡るように読み込むようにしていて、
    未読開始位置から表示するということは想定していませんでした。
    message_idで既読管理すれば、一覧表示する時に読み込み位置
    を指定(マーク)するのが楽そうですね。
    ご提案いただきありがとうございます!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.38%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • データベース

    881questions

    データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます