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

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

新規登録して質問してみよう
ただいま回答率
85.48%
データベース

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

Q&A

解決済

2回答

18850閲覧

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

gesorein

総合スコア101

データベース

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

0グッド

3クリップ

投稿2017/05/08 04:37

編集2017/05/08 08:45

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

##仕様について

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

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

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通りの方法でどちらが良いか、あるいは別に良い方法があれば教えていただきたいです。
よろしくお願いいたします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kunai

2017/05/08 04:57

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

2017/05/08 05:15

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

2017/05/08 06:51

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

回答2

0

ベストアンサー

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

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

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

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

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

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

投稿2017/05/08 07:43

szk.

総合スコア1400

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gesorein

2017/05/08 09:04 編集

回答ありがとうございます。もしチャット参加者が大量にいた場合、 データ容量が少し気になりますが、非同期で実行すればレスポンスに関しては問題ないですね。 ただ、今回はWebSocketでリアルタイムで既読判定を行うので、 処理の順序が入れ違いになった場合など想定することが多くなりそうです。 方法②の場合、たしかにouter joinを使えば検索は可能ですね。 たとえば、お気に入り機能の追加でis_favoriteカラムを追加した際など、 未読メッセージを抽出するときに、message_recipientsのレコードが存在しない、 または、レコードが存在して、かつis_readがtrueのときを考えれば大丈夫そうですね。 また、日時での管理方法のご提案もありがとうございます。 たしかに日時で既読管理すれば、処理も少なくすみますし実装も複雑になりにくいですね! 各メッセージごとに既読を管理するのはけっこう大変そうなので、こちらの方法も検討してみます。
guest

0

チャットルーム形式の場合メッセージ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 08:10

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

haru666

2017/05/08 08:13

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

2017/05/08 09:29

回答ありがとうございます。 やはり個別のメッセージごとに既読(部分既読)を管理しようとすると、 かなり実装が煩雑になりそうですね...。 room_usersに既読管理のカラム付ければ、 既読のレコードの作成タイミングを気にしなくて済みそうです。 日時でなくてもmessage_idでも実装できそうですね。 実はすべて最新のメッセージから遡るように読み込むようにしていて、 未読開始位置から表示するということは想定していませんでした。 message_idで既読管理すれば、一覧表示する時に読み込み位置 を指定(マーク)するのが楽そうですね。 ご提案いただきありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問