Rails と Mongoid をよりよく理解するために、Rails でフォーラム システムを構築しています。私が追加したい機能は、ユーザーがお互いにメッセージを送るために使用できるプライベート メッセージ システム フォーラムです。スキーマ設計に関しては、次の 2 つの解決策が考えられます。
解決策 1
ユーザーとメッセージは、"has_many" と "belongs_to" を使用して互いにリンクされた別個のドキュメントです。
ユーザー文書
has_many :messages_sent, :class_name => 'Message', :inverse_of => :message_sender
has_many :messages_received, :class_name => 'Message', :inverse_of => :message_recipient
と
メッセージ文書
フィールド :created、タイプ: DateTime、デフォルト: -> { Time.now }
フィールド:コンテンツ、タイプ:文字列
所属先 :message_sender, :class_name => 'User', :inverse_of => :messages_sent
所属先:message_recipient, :class_name => 'User', :inverse_of => :messages_received
ユーザーに自分の受信トレイを表示するために、並べ替えとフィルター処理を行って、最後のメッセージが送信された時刻でsome_user.messages_received
並べ替え:created
られた一意の送信者 ID のリストを作成しsome_user
ます。
次に、特定の会話を表示するには、2 人の参加者間のすべてのメッセージを取得し、タイムスタンプに従ってインターリーブします。
messages_in = some_user.messages_received.where(:message_sender => selected_correspondent)
messages_out = some_user.messages_sent.where(:message_recipient => selected_correspondent)。
私はこの解決策が好きではありません。メッセージ コレクションに "where" クエリを何度も実行し、送受信されるメッセージを手動でフィルタリングおよびインターリーブする必要があるからです。努力。
解決策2(現在使用しています)
会話ドキュメントにメッセージを埋め込みます。以下に、ユーザー、メッセージ、および会話のコードを示します。has_and_belongs_to_many
会話は、(ユーザーは多くの会話を持つ場合があるため、nn)を介して 2 人以上のユーザーにリンクされます。これにより、マルチユーザーの会話が可能になる可能性もあります。
私がこのソリューションを気に入っているのは、ユーザーの受信トレイを表示するために、会話ドキュメントに保存および更新されたsome_user.conversations
順序で使用するだけでよく:last_message_received
、フィルター処理が不要だからです。特定の会話を表示するために、送受信されたメッセージをインターリーブする必要はありません。メッセージは既に会話ドキュメントに正しい順序で埋め込まれているからです。
このソリューションの唯一の問題は、メッセージを追加するときに、2 人 (またはそれ以上) のユーザーが共有する正しい会話ドキュメントを見つけることです。ここで 1 つの解決策が提案されています: mongodb conversation systemですが、クエリが比較的高価に見え、マルチユーザーの会話のスケーリングが難しくなるように見えるため、私はそれが好きではありません。:lookup_hash
代わりに、会話に参加している各ユーザーのオブジェクト ID から計算された SHA1 ハッシュであるという名前の会話ドキュメントにフィールドがあります。このように、2 人以上のユーザーがいる場合、対応する会話ドキュメントを見つけるのは簡単です (または、まだ存在しない場合は作成します)。
会話にメッセージを追加するには、Conversation.add_message
(会話がまだ存在しない可能性があるため、インスタンス メソッドではなく、クラス メソッドを使用します) を使用して、送信者、受信者、および新しいメッセージ オブジェクトを指定します。
質問
私の質問は次のとおりです。Mongoid (または一般的に NoSQL) スキーマ設計のベスト プラクティスを考慮して、明らかに間違ったことをしていますか? ソリューションを改善するためにできることはありますか? ハッシュを使用して会話を検索するという私の考えは悪い考えですか?
コード
user.rb
class User
include Mongoid::Document
field :username, type: String
field :joined, type: DateTime, default: ->{ Time.now }
field :last_activity, type: DateTime, default: -> { Time.now }
has_and_belongs_to_many :conversations
end
会話.rb
require 'digest/sha1'
class Conversation
include Mongoid::Document
field :lookup_hash, type: String
field :created, type: DateTime, default: -> { Time.now }
field :last_message_time, type: DateTime, default: -> { Time.now }
# Array of user ids of users that have read all messages in this conversation
field :last_message_seen_by, type: Array, default: []
embeds_many :messages
has_and_belongs_to_many :participants, :class_name => 'User'
validates_presence_of :lookup_hash
index({ lookup_hash: 1 }, { unique: true, name: "lookup_hash_index" })
# Used to show a user a list of conversations ordered by last_message_time
index({ _id: 1, last_message_time: -1 }, { unique: true, name: "id_last_message_time_index" })
def self.add_message(recipient, sender, message)
# Find or create a conversation:
conversation = Conversation.find_or_create_by(
:lookup_hash => get_lookup_hash([recipient.id, sender.id])) do |c|
c.participants.concat [recipient, sender]
end
conversation.messages << message
conversation.last_message_time = Time.now
conversation.last_message_seen_by.delete(recipient)
conversation.save
end
private
def self.get_lookup_hash(participant_ids)
lookup_key = participant_ids.sort.join(':')
Digest::SHA1.hexdigest lookup_key
end
end
メッセージ.rb
class Message
include Mongoid::Document
field :created, type: DateTime, default: -> { Time.now }
field :text, type: String
embedded_in :conversation
belongs_to :author, :class_name => 'User'
validates_length_of :text, minimum: 2, maximum: 256
validates_presence_of :author
end