I am creating a forum system in Rails to become better acquainted with Rails and Mongoid. A feature I would like to add is a private message system forum that users can use to communicate with each other. Regarding circuit design, I can present two solutions:
Solution 1
Users and messages are separate documents linked to each other using has_many and belongs_to.
User Document
has_many: messages_sent ,: class_name => 'Message' ,: inverse_of =>: message_sender
has_many: messages_received ,: class_name => 'Message' ,: inverse_of =>: message_recipient
and
Message document
field: created, type: DateTime, default: β {Time.now}
: content, type: String
belongs to_to: message_sender ,: class_name => 'User' :: inverse_of =>: messages_sent
belongs to_to: message_recipient ,: class_name => 'User' :: inverse_of =>: messages_received
To show the user my mailbox, I would look at some_user.messages_received , sorted by :created , and filter it out, so I have a list of unique sender IDs ordered by the time their last message was sent to some_user .
Then, to show a specific conversation, I just get all the messages between the two participants and alternate them according to timestamps:
messages_in = some_user.messages_received.where (: message_sender => selected_correspondent)
messages_out = some_user.messages_sent.where (: message_recipient => selected_correspondent).
I donβt like this solution because it includes getting into the collection of messages with βwhereβ requests several times and a lot of manual filtering and alternating messages sent and received. Effort.
Solution 2 (which I use now)
Insert messages into the conversation document. I will give the code for the user, message and conversation below. A conversation is associated with two or more users through has_and_belongs_to_many (nn, since the User can also have many conversations). It can also allow multi-user conversations.
I like this solution, because in order to show the user my mailbox, I can simply use some_user.conversations sorted by :last_message_received , saved and updated in the Conversation document, no filtering is required. To show a specific conversation, I do not need to alternate between messages sent and received, as the messages are already embedded in the conversation document in the correct order.
The only problem with this solution is to find the correct dialog document shared by two (or more) users when you want to add a message. One solution is proposed here: the mongodb communication system , but I donβt like it because the request seems relatively expensive and scaling for multi-user chains looks as if it would be difficult. Instead, I have a field in the Conversation document with the name :lookup_hash , which is the SHA1 hash computed from the Identifier of the object of each User participating in the conversation. Thus, given two or more users, it is trivial to find the appropriate conversation document (or create one if it does not already exist).
To add a message to the conversation, I simply use Conversation.add_message (a class method, not an instance method, because the conversation may not yet exist), providing it with the sender, recipient, and a new message object.
Question
My question is: am I doing something obviously wrong, given the Mongolian (or just NoSQL as a whole) better schema schemas? Can I do something to improve my decision? Is my idea to use hash for search. Conversation with a bad idea?
the code
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
conversation.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 }
message.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