Database Design: private chat, group chat, and emails - mysql

The communication between Facebook users seem to be stored in one long "conversation." So, emails sent and private chat messages exchanged all seem to be part of one long ongoing conversation.
I think this implementation works well for users (at least it does for me). I assume the table design for this part could be implemented this way:
TABLE: message
- message_id
- timestamp
- from_user_id
- to_user_id
- message
What if I wanted to support group chat? Would I do something like this:
TABLE: message
- message_id
- timestamp
- from_user_id
- message
TABLE: message_recipient
- message_recipient_id
- message_id
- to_user_id
I think it'll work. However, I'm wondering if it would make sense to the user if I displayed every single things that user has ever messaged anyone in one long conversation. It probably wont. Imagine a conversation with Person A mixed with group conversation with Person A, B, C, D mixed with conversation with Person E and so on ....
Any suggestion on what would be a usable concept to implement?

I believe a message should be an entity, regardless of platform or sender/receiver, with id,message,timestamp fields, and a message relation table - like you suggested - with id,message_id,from_id,to_id.
Then, if you are showing a single user to user conversation, you can show every message between them.
For group chats, you should have a table with id,title,timestamp that holds the group chat main record, and another table that holds the users that are part of that group chat, with id,group_chat_id,user_id fields.
Just my opinion and how I would implement it.
Edit: Maybe it would make sense to have from_id on the message entity itself, as a message has to have a singular sender id.

You could also group messages by topics.
You add a topic table. You add a recipients table, tied to a topic. Messages will also be tied to a topic.
You can programmatically limit the topics between two users by looking which topic has those two users in its recipients.
You could also separate your messages by giving them a type attribute. For example, type 0 will be an inbox message, type 1 will be a chat message and so on.
If I wanted to have an arbitrary number of recipients in one topic, I would avoid the from_id/to_id combo.

Related

What is the optimal way of setting up a database for a messaging/email application?

I am currently trying to create an email style web app to allow users of my site to contact one another. I have created an SQL table for this, which has the following headings:
id
senderID
recipientID
timestamp
message
read (Boolean to record whether message has been read by recipient)
starred (Boolean to record whether message has been starred by recipient)
archived (Boolean to record whether message has been archived by recipient)
deleted (Boolean to record whether message has been deleted by recipient)
convoID
I have started to now realise that this table is insufficient. For example, if the conversation has been starred by a user, this does not tell me which of the 2 users has starred the convo, etc.
Can anyone suggest a way to avoid the above issue, and maybe suggest a better database structure?
I would recommend split your table into two, let's call them "message" and "star". So, they are:
message
-------
id
sender_id
recipient_id
timestamp
read
archived
deleted
convo_id
parent_id
star
----
message_id
user_id
timestamp
As you can see, I added parent_id into message. If you don't need hierarchical structure - you may kick this column. A star table gives possibility to enhance starring feature. Who knows, maybe in future all users may put a star, not only participants of conversation.
In addition, there is a nice articles about DB normalization. It will really helps you to build well-organized DB structure:
What is Normalisation (or Normalization)?
http://www.studytonight.com/dbms/database-normalization.php
http://searchsqlserver.techtarget.com/definition/normalization
depend on your application and how many users you will have.
About the starred, archived and other stuff where both users can do things, you can use an enumeration or simply a couple of values. Not just a boolean.
Or you can split every read with a senderRead and recipentRead

Database design dilemma on connecting users with messages

Here's what I came up with but I'm not sure which one of these is "the best". Perhaps there's another, better one that I may not know of. Keep in mind that I have both inbox and outbox in my app and messages deleted by either sender or recipient should still be visible to other related users unless they delete it themselves.
Option 1 - simple ManyToMany:
Tables:
User - just user fields
Message - just message fields
User_Message - contains 2 foreign keys: user_id and message_id
Example: When user sends a message, ONE message row is added to the Message table, and TWO rows are added to User_Message, obviously connecting sender and recipient with the added message. Now, this might get a little problematic when let's say I want to fetch only inbox messages because ManyToMany will fetch all of them so I came up with option 2.
Option 2 - OneToMany:
Tables:
User - just user fields
MessageReceived - message fields AND foreign key to user_id
MessageSent - message fields AND foreign key to user_id
Example: When user sends a message, this message is added to both received and sent tables but with different user_id. Of course senders id will be in sent table and recipient id in received table.
Now, when I want to fetch only inbox messages, I'm fetching messages from MessageReceived table and while deleting for example inbox (MessageReceived) message, copy of it still stays in MessageSent and is available to sender so everything is fine, however I feel like there's something "not cool" about this one because I'm basically keeping ALMOST the same data in both tables.
Please, let me know what do you think about this and if there is any better way to do it, I'm also listening.Thanks for your time.
EDIT :
Both Madbreaks and Tab Alleman provided really good and somewhat similar solutions so thanks for that. I'm gonna go with Madbreaks one, simply because I prefer to delete the relations in join table instead of keeping a 'deleted' column but that's just my taste. Nevertheless, thank you both for your time and answers.
You shouldn't need to add 2 rows in user_messages for each message - have 3 columns in that table: sender_id, recipient_id, message_id.
EDIT
The deletion scenario you describe in your question, below, changes things. Instead of a n-to-n approach, you likely now have two 1-to-n relationships:
the relationship between sender and their many sent messages
the relationship between a recipient and their many received messages
I would probably have the messages table have a sender ID foreign key. I would then have a message_recipients table that maps user (recipient) ID to message ID.
Now, if a sender can delete a message but the recipients should still be able to access it (and know who the sender is), then you'll need four tables:
users
messages
message_sender (1-to-1 map) -- senders deleting sent messages deletes from her
message_recipients (1-to-n map) -- recipients deleting received messages deletes from here
It's not clear from your question whether or not this is a requirement, I only add it for completeness. You may want a trigger or a subsequent query to determine if/when there are no remaining relationships between the users and messages tables, and at that time (possibly) delete the message itself.
Here's what I would do (I am assuming a message can only have one sender, but multiple recipients)
UserTable - Contains UserID and Other info
MessageTable - Has MessageID, SenderID (FK to UserTable.UserID) and other info
MessageRecipientsTable - Has MessageID, RecipientID (FK to UserTable.UserID), and possibly other info like when/if it was received, etc.
If you want a recipient to be able to delete a message and still have it show for the sender (and other recipients), then you would add a "Deleted" column to the MessageRecipientsTable. You would never actually delete a row from the messages table, but when populating a recipients inbox you would filter out the rows where "Deleted" is true.

mysql: Complex conditional query before group by

I have a Postings table (with data of people posting a service they offer) and a table of people that have corresponded (written mails) to these Posting authors thus starting a Transaction (inserted into a second table: Transactions).
Each Posting can have many transactions. Each time a user Logs-in he/she (Transaction_Taker) can send mail to the author (Posting_Author) of his choice.
Each first mail generates a new Transaction and its Transaction_Id (int) is appended to Postings table in the varchar, hyphen-separated Posting_Transaction_List field.
The contents of each subsequent mail that same (logged-in) user (aka Transaction_Taker) sends, does not create/insert a new transaction (nothing inserted to field Posting_Transaction_List) but rather is appended (update) to the Transaction that was started initially by that user for that Posting.
For easy navigation and search, once a user has logged in, I show an ajax generated list of all these postings such that each Posting only shows once though it can have many transactions. In other words I need to show a list of all available Postings including the ones this (logged-in) user has started a Transaction for, but these postings (the ones in which this user has an ongoing transaction) should show, but we should not show that same Posting with OTHER user's transactions. That is, only the logged-in user should see his/her transactions.
Assuming I have table Postings with fields: Posting_Id (int), Posting_Author (varchar), Posting_Content (text), Posting_Transaction_List (varchar)
…and table Transactions with fields: Transaction_Id (int), Transaction_Posting_Id (int), Transaction_Taker_Id (int)
I am (almost) achieving my goal with the following SQL:
$AlmostGoodSQL = "SELECT *, Posting_Id FROM Postings LEFT JOIN Transactions ON
Postings.Posting_Id = Transactions.Transaction_Posting_Id WHERE Posting_Content
LIKE '%"+$SomeSearchString+"%' GROUP BY Posting_Id";
The problem is this shows a distinct instance of each Posting, but not necessarily the ones that have to do with the logged-in user (in the case where there are many transactions -including hers- for a Posting). To do this, I would need to select ALL Postings without transactions attached PLUS those that have Transactions just for this user BEFORE doing the group by. This is what I cannot achieve. I believe that due to the way 'group by' works you could maybe select maximum or minimum values, but not an exact match, say for all the Postings that have Transactions with user (Transaction_Taker) '123456'. I think "group by" shows whichever instance it finds first. How to make it match my criteria?
It does not look like a subquery would do, but rather like something conditional, like: "Search for all Postings and if the Posting has a Transaction listed in the Posting_Transaction_List that points to a Transaction where the Transaction_Taker_Id is the one of the logged-in user ($UserId), then show it distinctly (just that one, once)"… and I don't know how to do all that in SQL: Can anybody please help?

Representation of a simple messenger application using a database

In this application there are users, conversations and messages.
More than 2 users could participate in a conversation.
I was thinking this:(---- are relations, CAPITAL_WORDS are entities)
MESSAGE ---- CONVERSATION ---- USER
msg contains sender and the content to be sent;
conversation contains the users that participate in that conversation;
But this is not enough because the sender is a user and there is another link between MSG and USER. If I add this relation I have a problem because the sender should be a user that participate to the conversation.(there is a IS-A relation if I'm not mistaking)
I really don't know how to model this problem. If the conversation was between only 2 users, I would need only MESSAGE and USER. In fact the CONVERSATION entity is pretty strange.
If I eliminate the CONVERSATION from the initial problem I have to add in MESSAGE a field that contains a list of partecipants. But in this way I miss the concept of conversation and then if I have to retrieve the msgs related to a conversation I need a join of all data :) . --> impractical
I will appreciate every suggestion. I don't even know if the database is a good idea for this kind of application. My thought was to do something not too difficult to work with.(keeping in mind that I can lose performance)

Messaging system database schema

I'm trying to implement a messaging system in PHP and MySQL but I'm having some trouble deciding on how I should do the tables and queries.
What would be the best approach for a system that allows for multiple participants? I'm thinking I'd probably need 3 tables (aside from an users table).
Something like
Conversation
------------
id
Messages
--------
id
conversation_id
from_id
subject
message
from_timestamp
Participants
------------
conversation_id
user_id
last_read_timestamp
The way it is setup I'd have to check for read messages by the timestamp instead of ticking off each message. I'd also be able to add participants at any time.
What do you guys think?
Thanks in advance.
I don't see much to complain about :) I'd probably cache the last modification date on the conversations table so that a list of "unread" conversations can be displayed quickly. This date would be updated each time a message is posted on the conversation.
I would have put the subject field on the conversation table. Putting it on every single message looks redundant.
Also, I'd save the creation time and the author user id of the conversation in its table.