Etanor
Etanor

Reputation: 138

MongoDB User notifications scheme suggestions

I was wondering what is the best scheme for user/notifications kind of scenario like the following:

1 | 01-04-2020 | X | John liked your post2 
2 | 01-03-2020 | X | Mike and 9 other persons liked your post1
3 | 01-02-2020 |   | Rose and 2 other persons liked your post1
4 | 01-01-2020 |   | Bernard liked your post1

x = notification has not been read by the user yet
post1 = the same post in all the notifications

Let's say I have a Notification collection like:

_id: ObjectID
receiver: ObjectID (User)
sender : ObjectID (User)
type: String ("post", "comment", etc...)
typeID: ObjectID (Post, Comment, etc...)
message: String
isRead : Boolean
timestamp: Date

A User collection like :

_id: ObjectID
username: String
.
.
.
email: String

A Post Collection like :

_id: ObjectID
postedBy: ObjectID (User)
.
.
.
content: String

A Like collection like :

_id: ObjectID
type: String ("post", "comment", etc...)
typeID: ObjectID (Post, Comment, etc...)
likedBy: ObjectID (User)

I want the user to be able to see all his previous notifications as well as the notifications he hasn't read yet. If the user has several notifications for the same post (X people liked his post since the last time he checked his notifications), I want to group them as 1 notification (I will mark all of them as read once he read that one grouped notification).

How can I do that?

Please let me know if you need more information or if I am being unclear.

Thanks


Edit:

I'm having a hard trying to figure out how to aggregate those notifications. I think I need some kind of read date marker as well to group the notifications that were grouped and read at the same time, but maybe I need another collection to store the grouped notifications?

Notification.aggregate([
        {
            $group: {
                _id: {
                    typeID : "$typeID",
                    receiver: "$receiver",
                    isRead: "$isRead"
                    // maybe something with a read date?
                },
                count: {$sum: 1}
            }
        }

])

Upvotes: 3

Views: 1037

Answers (1)

AlexZeDim
AlexZeDim

Reputation: 4352

I guess this article at Mongo University is a relevant answer to your question.

Use at least two collections: users and notifications also, you your users _id field isn't something like name and you'll allow them to be renamed, then it's perfect to have 3-rd collection likes, instead making likes as a embedded documents in array, like this:

User's schema:

  _id: ObjectID,
  likes: [{
     _id: ObjectID //like_id,
     other: "field"
  }]

Pattern

Notifications:

_id: ObjectID
receiver: ObjectID (User)
sender : ObjectID (User)
type: {
        type: String,
        enum: ["Post", "Comment"] /** Use enum for avaliable values */
    },
typeID: {
    type: ObjectID, /** It's better if every ID field have an index */
    index: true, 
    unique: true
}
message: String
isRead : Boolean
timestamp: { 
   type: Date,
   default: Date.now /** Don't forget about default values, but check my advice below about $timestamps */
}
  • Not sure that timestamp field is needed for you, as for me, it's better to use {timestamps: true} option.

  • Also, every field with ObjectID should be indexed, it you needed this fields for aggregation framework. It's a perfect performance case for $lookup

I want the user to be able to see all his previous notifications as well as the notifications he hasn't read yet.

You needed a compound index for this, like {sender:1, createdAt: -1, isRead: 1}

I want to group them as 1 notification (I will mark all of them as read once he read that one grouped notification).

This is a job for aggregation framework, via:

{
   $match: { query_criteria },

},
{
   $group: { query_group_by $notification.typeID }
}

So your schema is fine, it's possible to do that. By the way, to test your own queries, you could use MongoPlayground, instead of production DB.

As for the likes schema, it's for you to decide, but maybe it's better to have them as an embedded (child) documents, like:

Post

_id: ObjectID
postedBy: ObjectID (User)
likes: [{
   /** Likes Sub-schema */
}]
content: String

Take a look at sub-schema pattern in mongoose.

Hope it will helps you!

Upvotes: 5

Related Questions