42

I am just trying to figure out how Facebook's database is structured for tracking notifications.

I won't go much into complexity like Facebook is. If we imagine a simple table structure for notificaitons:

notifications (id, userid, update, time);

We can get the notifications of friends using:

SELECT `userid`, `update`, `time`
FROM `notifications`
WHERE `userid` IN 
(... query for getting friends...)

However, what should be the table structure to check out which notifications have been read and which haven't?

reformed
  • 4,505
  • 11
  • 62
  • 88
Atif
  • 10,623
  • 20
  • 63
  • 96

6 Answers6

41

I dont know if this is the best way to do this, but since I got no ideas from anyone else, this is what I would be doing. I hope this answer might help others as well.

We have 2 tables

notification
-----------------
id (pk)
userid
notification_type (for complexity like notifications for pictures, videos, apps etc.)
notification
time


notificationsRead
--------------------
id (pk) (i dont think this field is required, anyways)
lasttime_read
userid

The idea is to select notifications from notifications table and join the notificationsRead table and check the last read notification and rows with ID > notificationid. And each time the notifications page is opened update the row from notificationsRead table.

The query for unread notifications I guess would be like this..

SELECT `userid`, `notification`, `time` from `notifications` `notificationsRead`
WHERE 
`notifications`.`userid` IN ( ... query to get a list of friends ...) 
AND 
(`notifications`.`time` > (
    SELECT `notificationsRead`.`lasttime_read` FROM `notificationsRead` 
    WHERE `notificationsRead`.`userid` = ...$userid...
))

The query above is not checked. Thanks to the idea of db design from @espais

Jez
  • 305
  • 2
  • 12
Atif
  • 10,623
  • 20
  • 63
  • 96
  • 2
    Why is this join necessary? Just put the read field in the notifications table and you've saved yourself time and space. – atp Jul 13 '10 at 14:17
  • 2
    The `notificationsread` table will store only 1 row for each user. Which will be updated when the user views his notifications to the latest notificationID. So we dont actually need that field itself. Thanks for the comment :) – Atif Jul 14 '10 at 15:08
  • 3
    I think what `Jasie` mean is that, why do you even need the `notificationRead` table. Is there something wrong with just adding a `readStatus` field into your `notification` table. So when a notification create, its `readStatus` is false, and only turn `true` when the user open a notification page. – Thang Pham Sep 23 '10 at 16:32
  • 1
    updated. The `notificationRead` will keep a track of last time the user viewed their notifications and show all other notifications as unread. – Atif Sep 25 '10 at 18:36
  • I also think this is the best design posibel, so you you don't have update constantly notofications table (that would be pretty heavy) just push data there and select using 2 conditions (user_id, time) instead of 3 (user_id, time, notificationRead) – Mike Oct 09 '10 at 21:24
  • 15
    If you don't want the second table, you can save the last read property on user table for example – Gui Aug 22 '12 at 01:42
  • @AtifMohammedAmeenuddin what you store inside the `notification` column? Thanks – Luccas Feb 24 '13 at 03:28
  • 1
    the notification message/text – Atif Feb 25 '13 at 07:05
  • 2
    I think this design is better than the single table design when you have same notification for a number of people(eg. a group notification etc.). Such notifications will use only one row in th main table and subsequently new rows can be added with a foreign key to that notification as people are notified. What say? – mlakhara Apr 06 '13 at 10:42
  • How are you providing a link to the notification content? On Facbook you can click the notification and it will take you to the notification. – BugHunterUK Jan 26 '17 at 10:31
  • 1
    @BugHunterUK it's 2017. This answer is almost obsolete. There is better technology out there for push notifications instead of using MySQL. – Atif Jan 30 '17 at 10:22
  • 1
    What about shared notifications? E.g for a group message, we must notify all participants that a new message has came through. Shouldn't we have a junction table: `notification_users`, where we could link multiple users to a notification. – James111 Aug 07 '17 at 04:10
  • Cool design just be careful when you store your notification text or JSON to sperate variables and text so you can handle localization :) – Fareed Alnamrouti Aug 13 '18 at 16:20
9

You could add another table...

tblUserNotificationStatus
-------------------------
- id (pk)
- notification_id
- user_id
- read_status (boolean)

If you wanted to keep a history, you could keep the X latest notifications and delete the rest that are older than your last notification in the list....

erik
  • 3,810
  • 6
  • 32
  • 63
  • so we add rows on this table when a notification is created or when a notification is read by the user ? – Atif Dec 11 '09 at 15:43
  • i would imagine that when you have a notification created, this table would be updated as well...once your user views the notification you would update the read_status field from false to true – erik Dec 11 '09 at 22:16
  • that means when a notification is added, we need to find the users friends and insert all those rows in the second table right. But the problem arising would be friend requests made after notifications. Friends made after a notification wont have their row in tblUserNotificationStatus – Atif Dec 12 '09 at 04:25
  • I guess your answer is not what I was actually looking for but it gave me a big hint. – Atif Dec 12 '09 at 04:26
7

If, when you give notifications, you give all relevant notifications available at that time, you can make this simpler by attaching timestamps to notifiable events, and keeping track of when each user last received notifications. If you are in a multi-server environment, though, you do have to be careful about synchronization. Note that this approach doesn't require true date-time stamps, just something that increases monotonically.

Joe Mabel
  • 1,372
  • 10
  • 29
6

I see no-one here addresses the fact, that notifications are usually re-occurring, aka. notification of an upcoming transaction is always going to be the same, but with a different transaction ID or Date in it. as so: { You have a new upcoming payment: @paymentID, with a due date of @dueDate }. Having texts in a different table can also help with

  1. If you want to change the notification text later on
  2. Making the app multilingual is easier, because I can just layer the notifications table with a language code and retrieve the appropriate string

Thus I also made a table for those abstract notifications, which are just linked under the the user with a middle table, where one notification type can be sent to one user at multiple times. I also linked the notifications to the user not by a foreign key ID, but I made notification codes for all notifications and full_text indexed the varchar field of those codes, for faster read speeds. Due to the fact that these notifications need to be sent at specific times, it is also easier for the developer to write

NotificationService::sendNew( Notification::NOTE_NEW_PAYMENT, ['paymentId'] => 123, ['dueDate'] => Carbon::now(), 'userIdToSendTo' );

Now since my messages are going to have custom data in them, that is inserted into the string, as you can see from the second argument beforehand, then I will store them in a database blob. as such

$values = base64_encode(serialize($valuesInTextArray));

This is because I want to decouple the notifications from other tables and as such I dont want to crete unnessecary FK relations from and to the notifications table, so that I can for example say notification 234 is attached to transaction 23 and then join and get that transaction ID. Decoupling this takes away the overhead of managing these relations. The downside is, it is nigh impossible to delete notifications, when for example a transaction is deleted, but in my use case I decided, this is not needed anyway.

I will retrieve and fill the texts on the App side as follows. Ps. I am using someones vksprintf function (https://github.com/washingtonpost/datawrapper/blob/master/lib/utils/vksprintf.php), props to him!

$valuesToFillInString = unserialize(base64_decode($notification->values));
vksprintf( $notificationText->text, $valuesToFillInString )

Notice also which fields I index, because I am going to find or sort by them

My Database design is as follows

==============================

TABLE: Users

  • id (pk)

==============================

TABLE: Notifications

  • id (pk)
  • user_id (fk, indexed)
  • text_id (fk - NotificationTexts table)
  • values (blob) [containing the array of values, to input into the text string]
  • createdDateTime (DateTime)
  • read (boolean)

[ClusterIndex] => (user_id, createdDateTime)

==============================

TABLE: NotificationTexts

  • id (pk)
  • text_id (uniquem indexed)
  • text (varchar) [{ You have a new upcoming payment: @paymentID, with a due date of @dueDate }]
  • note (varchar, nullable) [notes for developers, informational column]
Karl Johan Vallner
  • 3,980
  • 4
  • 35
  • 46
2

I am also trying to figure out how to design a notification system. Regarding notification status (read, unread, deleted, archived, ect) I think that it would be good a good candidate to for ENUM. I think it is possible that there will be more than two different types of status other than READ and UNREAD such as deleted, archived, seen, dismissed, ect.

That will allow you to expand as your needs evolve.

Also I think it may make sense (at least in my case) to have a field to store an action url or a link. Some notifications could require or prompt the user to follow a link.

It also may make sense to have a notification type as well if you want different types. I am thinking there could be system notifications (such as a verify email notification) and user prompted notifications (such as a friend request).

Here is the structure I think would be a minimum to have a decent notification system.

users
-------------
id
username
password
email

notifications
-------------
id
user_id (fk)
notification_type (enum)
notification_status (enum)
notification_action (link)
notification_text
date_created (timestamp)
Radmation
  • 1,486
  • 1
  • 13
  • 30
0

Table are following

User

  • userId (Integer)
  • fullName(VarChar)

Notification

  • notificationId (Integer)
  • creationDate (Date)
  • notificationDetailUrl (VarChar)
  • isRead (bollean)
  • description (VarChar)
  • userId (F.K)
Yasir Shabbir Choudhary
  • 2,458
  • 2
  • 27
  • 31