6

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
4

1 に答える 1

2

MongoId3.0を使用しているとのことです。私はあなたの最初の解決策に何の問題も見ていません:

messages_in = some_user.messages_received.where(:message_sender => current_user)

messages_out = some_user.messages_sent.where(:message_recipient => current_user).

さまざまな例を見つけることができます:

Rails3でのプライベートメッセージモデリングへの好ましい方法

http://pastebin.com/fKavivbp

https://groups.google.com/forum/?fromgroups=#!topic/mongoid/BOBqhYLb7O0

私はMongoIdを使用したいくつかのプロジェクトに内部メッセージングシステムを持っており、最初のソリューションを使用しています。

他のクラスを追加する場合"Conversation"会話には無制限の数のメッセージを含めることができるため、メッセージを埋め込むべきではありません。とを使用する必要がhas_may messagesありbelongs_to conversationます。

どちらのソリューションも優れていると思うので、プロジェクトロジックのニーズを選択します。ロジックが単純な場合は、最初のソリューションを選択できます。それ以外の場合、ロジックがより複雑な場合は、後者のソリューションを選択してください。

よろしく!

于 2013-01-24T11:04:14.900 に答える