Need database structure for private, group and public chat - MySQL - mysql

My basic idea is to build a web-based chat application. I have checked a lot of database structures over the internet for my requirements and the conclusion is that there are so many solutions for that.
So here is the database structure I thought (But I'm pretty sure that it is not 100% correct or at least it can be improved)
Table users:
id | username | email | created_at | updated_at
Table chat_rooms
id | room_type | created_at | updated_at
Table: room_members
id | room_id | user_ids (in serialized form)
Table: messages
id | room_id | sender_id | message | created_at | updated_at
Table: receivers
id | message_id | receiver_id | read_at
There are 3 types of chats:
i) Private chat - a chat between two users
A user will select another user to start to chat with him/her
ii) Group chat - a chat between group of users
A user will add other users into the room to start a group chat
iii) Public chat - a chat between all the users
An open chat room to which anyone can join and send messages
So here is how it will work:
I logged into the site, redirected to the dashboard page. On that page, I have 3 options to start a chat as described above.
Notes:
1) If there is a previous chat between the users it should be shown as soon as I start a chat (pretty similar to Facebook) - This applies to all the three types of chats
2) I want to have a feature which says that when a specific user read a message.
3) I want to keep it scalable as much as possible for the future enhancement
Thanks

Yes this DB structure is workable for make alpha version.
you can make some small changes according to your requirement.

Related

DB schema for a resource-based messaging system

My laravel application has some services and products, for simplicity's sake, I will keep it as generic as possible and I am going to call an individual service/product a resource.
I will have to implement a messaging system in such a way, so that a client can send message to the admin (and vice versa) against a single resource.
For example, consider project as being one such resource. An admin can create a project, a client can send messages to the admin who created that project. A client cannot create a project.
This is how I designed the db schema:
Message Storing Part:
conversations(id, message_id, from, to, body, is_seen)
messages(id, resource_id, resource_name)
Resource Storing Part:
projects(id, admin_id, client_id, some_other_column)
another_resource(id, admin_id, client_id, some_other_column)
yet_another_resource(id, admin_id, client_id, some_other_column)
Notice the resource_id and resource_name in the messages table:
resource_id is the id from one of the resource tables (projects, another_resource, yet_another_resource).
resource_name is where my problem is. If I implement this schema, I would have to hardcode a tablename in this column. An entry in the messages table would look like this:
+----+---------------+-------------+---------------------+---------------------+
| id | resource_name | resource_id | created_at | updated_at |
+----+---------------+-------------+---------------------+---------------------+
| 1 | project | 1 | 2018-01-13 15:11:07 | 2018-01-13 15:11:07 |
+----+---------------+-------------+---------------------+---------------------+
As you can see, if some client had to send a message on an entry of another_resource I would have to store the string "another_resource" as the resource_name in messages table and the id of that row of another_resource as the resource_id
I admit that I am not a good db designer but I have a strong feeling that storing a tablename as a column value is not a good idea. There must be some dynamic way of handling it. How could I improve my schema? Any modification, suggestion or advice will be greatly appreciated.

mysql error : "subquery returns more than one row" but i need more than one row from subquery

I have two tables in my problem, one is the USER table with all details of user and a REQUEST table with friend requests from all users to all users. I want to list all the friends of one user who logs into my application.
The USER table attributes are,
id | uname | passwd | fname | e mail | dob | mobileno | habbits | tastes | profession | image
11 | nagaraj | ***** | naga | ng#gml | 3/94 | 998345 | singing | sports | teacher
12 | chiraag | ******* | chiru | ch#gml | 2/93 | 894617 | writing | music | student
Similarly, the REQUEST table attributes are,
rqstTO | rqstFM | fname | e mail | mobile | status
chirag | nagaraj | nagaraj | ng#gml | 99821 | accepted
The tables look like this. Now I want to display the details of the friends( from USER table), only those friends who have accepted the request from the user who logged in. Output may be multiple users. The conditions to be satisfied are
all details of friends (from user table)
rqstFM should be the user logged in and rqstTO must be the friends to whom he sent requests (since request table has request details from all to all users)
status=accepted (they must have accepted the request)
The query I have written is
SELECT *
FROM user
WHERE habits='singing' AND uname = (
SELECT rqstTO
FROM request
WHERE rqstFM ='"+username+"' AND status='Accepted'
);
(here +username+ is the uname of the person logged in obtained from HTML FORM tag). The error I keep getting is "subquery returns more than one row" but I do need multiple rows as logged in user may have sent requests to many ppl and among those all who accepted his request, their details need to be displayed from user table. Please, correct my query or provide an appropriate query for my problem
You need to use IN instead of =:
You can use as mentioned below:
SELECT * FROM user
WHERE habits='singing' AND uname IN
( SELECT rqstTO FROM request WHERE rqstFM ='"+username+"' AND status='Accepted');
However using subqueries are not recommended due to performance issues. You should rewrite your query by using JOIN
You can use 'JOIN' like below: (I haven`t tested it)
SELECT u.* FROM user u
JOIN request r ON u.uname = r.rqstTO
WHERE u.habits='singing' AND r.rqstFM ='"+username+"' AND r.status='Accepted';
You want something like this (not tested it however):
SELECT user.uname, request.fname
FROM user
INNER JOIN request ON request.fname = user.fname
WHERE request.status = 'Accepted';
However, I would probably have a bit of a rethink of those tables, maybe this is personal preference but you would likely be better off having a join table that simply contains an id and the username, and then join on two other tables. One containing the requests and one containing the member details.

Database issue - how do I set up user accounts/pswds so they can ONLY add/change THEIR data?

Okay... I am working to create a mobile app that allows two groups of users to do two different things.
Essentially, the goal of the project is this:
Group A users: create account/pswd and can enter THEIR data into the database and/or change THEIR existing data (but ONLY their data)
Group B users: can SEARCH the database for information that is inserted by Group A. Down the track I'd like to set it up so that they can create an user account so they can also SAVE key information to THEIR account for faster recall (so they don't have to look up the info they search for regularly) -- but that is down the track.
I have a relational database set up using the mySQL that is available with my web-hosting account (it seemed to be the easiest way to go).
I'm just trying to work out how to handle the user account creation/authentication bit, because each group should ONLY be able to CHANGE/INSERT data to their own account, but can search for information submitted by anyone else.
Thanks in advance.
Use mysql facilites to manage permissions: roles, users and privileges.
Navigate through mysql official documentation (i.e. http://dev.mysql.com/doc/workbench/en/wb-adding-roles.html).
You can create two roles: groupA that can INSERT/SELECT/UPDATE one set of tables, groupB that can do the same but in another set of tables.
You can assign INSERT privilege in just the table you want, but SELECT privileges on all the tables.
Hope this info brings you some light...
Firstly this sounds like a huge project, I am sure there are frameworks out there that can do this for you. However, if you are trying to do this on your own continue reading.
This can be done several ways. I will try to be as detailed as possible. This requires SQL as well as application development/Software engineering knowledge.
Step 1: Setup your database
You will need the following tables: All ids are primary keys auto incremented, the other fields can be varchar, except fields that have date in their name
sessions [id, uid, random_token, datecreated]
resourcescope [rid, name]
user [uid, first, last, email, username, salted_pwd]
user_type [id, name, description]
user_resourcescope [id, uid, rid] //lookup table between userid and resourcescope
I prefer using Java or python because you can use dependency injection or decorators. As a result, you don't have to write a lot of code when checking if a user has access.
Putting it all into practice.
1. When a user signs up, you save them into a user database. Depending on the user type, you give them different permissions. Next, you save the user permissions inside the user_resourcescope table.
You should now have the following.
User Table
UID | first | last | email | username | salted_pwd | usertype
1 | james | iri | example#isp.com | jiri1928 | klasdjf8$kljs | 1
UserType table
usetype_id | Name
1 | Basic users
2 | Searcher
ResourceScope Table
rid | Name
1 | FindContent
2 | CreateContent
3 | DeleteContent
User_Resourcescope
id | uid | rid
1 | 1 | 1
2 | 1 | 3
Session
id | uid | random_token | datecreated
1 | 1 | ldkjfald882u3u | 1391274870322
Each resource represents a request within the system. For example,
http://api.myapi.com/content/add - This would be associated with the ResourceScope CreateContent
http://api.myapi.com/content/delete- This would be associated with the ResourceScope CreateDelete
http://api.myapi.com/content/search - This would be associated with the ResourceScope SearchContent
When someone tries to create content, you check if their cred are correct by validating their session information and you check to see if they have the correct permission by checking the User_Resourcescope table.
To prevent users from deleting content that is not theirs. Inside the content table you can add a creator field and put the user id associated with the content. And if someone try to delete content you can check their user id against the creator field.

PHP/MYSQL - showing newest distinct value from multiple columns

This is my mysql table structure:
msgid | senderid | sender | recipientid | recipient | title | message | date
I'm looking to present in inbox for my private messaging system as Facebook have done. That is, each conversation is output ONE TIME from the table irrespective of whether it is incoming or outgoing. For example:
My username is 'admin'.
USER | MESSAGE | DATE | DIRECTION
Dan | Hello | 1/2/10 | Incoming
Bob | How are you Bob?| 30/1/10 | Outgoing
The problem is not repeating the username because of differing directions. For example:
USER | MESSAGE | DATE | DIRECTION
Dan | Hello | 1/2/10 | Incoming
Bob | How are you Bob?| 30/1/10 | Outgoing
Bob | Hi admin | 30/1/10 | Incoming
So I need to make sure that the only message that shows is the newest communication irrespective of the direction (i.e., the newest message with another user, irrespective of whether that user is the 'sender' or 'recipient').
I'm using PHP/MYSQL for this website. I've tried thousands of ways to do this but I just can't figure it out. It might be that my database is incorrectly structured. I will be online for a few hours (and will continually check after that) so feel free to ask any questions.
If I understand correctly this should do what you need:
SELECT
IF(senderid = :userId,recipient,sender) AS USER,
message,
date,
IF(senderid = :userId,'Outgoing','Incoming') AS DIRECTION
FROM message
WHERE senderid = :userId OR recipientid = :userId
ORDER BY date DESC
The USER and DIRECTION is determined based on whether this user is the sender or not.
If you are only interested in the latest message you can add a LIMIT to this query.
I have somewhat solved my own question with the following:
SELECT * FROM (
SELECT * FROM (
(SELECT id, reciever, recieverid, message, datetime FROM messages WHERE sender = '$user')
UNION
(SELECT id, sender, senderid, message, datetime FROM messages WHERE reciever = '$user')
) as t
ORDER BY datetime DESC
) as t2
GROUP BY reciever ORDER BY datetime DESC
It seems to do the trick, although I don't know which messages are incoming or outgoing. I'll know if it works properly when I have a chance to invite some actual users.

store calculated data or pre-calculated data on table (calculated user age or timestamp)

I have some tables, they are:
user
==============================
user_id | username | etc..
==============================
user_metadata
====================================
user_id | birthday | gender | etc..
====================================
game
========================
game_id | name | etc..
========================
I want to store users liking games, and since age attribute is important, I need to differ when did the like was made, it should be different entity if someone liked a game when he/she was in the age of 5 or was in the age of 8. So which of table structure would you recommend?
Store the age of the user when the likes made:
user_likes
==========================================
user_id | game_id | user_age_when_liking
==========================================
Store the timestamp
user_likes
==========================================
user_id | game_id | liked_at (timestamp)
==========================================
So with the option number 2, if I need to get all users like with a certain age, I will calculate the year difference between the user birthdate and liked_at.
Other suggestion is very welcomed.
It mostly depends on the purpose of the use of the column. If you never need to do any time to time calculations (addition, subtraction etc) based on when the like occurred, than the age of the person is completely valid (basically you're pre-evaluating birthDate-likeDate). However, if you have any intention of saying that someone liked a game 10 days ago, it will be much more work to reverse the pre-evaluated user-age back to a likeDate to allow another calucation. Keeping in mind that you most likely want to keep your database data extensible, using likedAt is preferred in my opinion.
I would also note that stackoverflow is not a site designed to answer theoretical questions that don't have a specific code answer, this question should probably be on programmers.stackexchange.com.