MongoDB Conversation / private message diagram using Mongoid

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 } # 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 

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 
+6
source share
1 answer

I understand that you are using MongoId 3.0. I do not see any problems in your first solution:

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

You can find various examples:

Preferred way to model private messages in Rails 3

http://pastebin.com/fKavivbp

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

I have an internal messaging system in several projects with MongoId and use the first solution.

If you add another "Conversation" class, you should not embed a message because there can be an unlimited number of messages in a conversation. you should use has_may messages and belongs_to conversation .

I think both solutions are good, so you choose your needs for your project logic. If your logic is simpler, you can choose the first solution. Otherwise, if your logic is more complex, choose the last solution.

Hello!

+2
source

All Articles