Retrieving messages shared by the sender and receiver - chat mongodb, express, react

Viewed 542

I have two collections students and teachers in the mongodb database. Below is an example structure of a conversation between a student and a teacher. In the student and teacher object, I have an array of messages containing objects that are messages, and receiver and sender containing the id of the person sending and receiving the message. I am asking for advice on what function to create to download common messages for the sender and receiver and display them either on the sender's or the receiving side, depending on who sent. On the left side of the message if it was sent by the student, on the right side if it was sent by the teacher. Anyone have material explaining how to do this??

Students

{
    "_id": { "$oid": "600333" },
    "name": "MMMMMM",
    "surname": "xxxxx",
    "email": "mar@gmail.com",
    "role": "student",
    "messages": [
      {
        "contentInfo": {
          "viewed": false,
          "msg": "22222222ghjk",
          "createdAt": { "$date": { "$numberLong": "" } }
        },
        "_id": { "$oid": "" },
        "receiver": { "$oid": "5fea6e09" }
      },
      {
        "contentInfo": {
          "viewed": false,
          "msg": "heloooo",
          "createdAt": { "$date": { "$numberLong": "" } }
        },
        "_id": { "$oid": "" },
        "receiver": { "$oid": "5fea6e09" }
      },
      {
        "contentInfo": {
          "viewed": false,
          "msg": "dfdfdf",
          "createdAt": { "$date": { "$numberLong": "" } }
        },
        "_id": { "$oid": "" },
        "receiver": { "$oid": "600333" }
      }
    ]
  }
  

    

Teachers

{
  "_id": { "$oid": "5fea6e09" },
  "isActiveTutor": false,
  "youtubeUrlId": "",
  "name": "rrrr",
  "surname": "rrrrr",
  "email": "u@gmail.com",
  "initials": "",
  "role": "tutor",
  "createdAt": { "$date": "" },
  "updatedAt": { "$date": { "$numberLong": "" } },
  "messages": [
    {
      "contentInfo": {
        "viewed": false,
        "msg": "22222222ghjk"
        "createdAt": { "$date": "" }
      },
      "_id": { "$oid": "" },
      "sender": { "$oid": "600333" }
    },
    {
      "contentInfo": {
        "viewed": false,
        "msg": "heloooo",
        "createdAt": { "$date": { "$numberLong": "" } }
      },
      "_id": { "$oid": "" },
      "sender": { "$oid": "600333" }
    },
    {
      "contentInfo": {
        "viewed": false,
        "msg": "dfdfdf",
        "createdAt": { "$date": { "$numberLong": "" } }
      },
      "_id": { "$oid": "" },
      "sender": { "$oid": "5fea6e09" }
    }
  ]
}
2 Answers

I would personally recommend not to save the messages nested on the object, Imagine how quickly the scale on this can explode for a single teacher with many many students.

For a toy example this is fine but for an actual app you should never do this.

The more common approach is one of the following two:

  1. (My recommendation) is to have a separate collection for messages, each message will be with the same structure:
{ 
  created_time: Date,
  sender_id: ObjectId,
  receiver_id: ObjectId,
  message: string
}

With an index built on both sender_id and receiver_id with created_time this is the most scaleable solution, you can also "retire" old messages into a different collection based on your needs, for example for a chat feature you can fetch the last x messages between the two and allow loading of more, this can be easily done with skip and limit.

  1. Create a "conversation" collection, a conversation will hold nested messages in a similar way to the student / teacher, the advantage of this over your current structure is exactly as I explained before, a teacher with 30 students will have it's messages array explode, making querying / fetching data from him slow. this allows better management of that but this solution is also capped to an extent.

Check this: https://getstream.io/blog/how-to-create-a-chat-app-with-angular-9/

This is an Angular blog, but since you already have the message history, you can use the html and css to replicate the chatting feature with messages on both sides depending on who send the message.

Basically, you can copy-paste the code and just modify this part:

<li class="message" *ngFor="let message of messages">
              <div
                *ngIf="
                  message.user.id !== currentUser.me.id;
                  then incoming_msg;
                  else outgoing_msg
                "
              ></div>
              <ng-template #incoming_msg>
                <div class="incoming_msg">
                  <div class="incoming_msg_img">
                    <img
                      src="https://i.imgur.com/k2PZLZa.png"
                      alt="User avatar"
                    />
                  </div>
                  <div class="received_msg">
                    <div class="received_withd_msg">
                      <p>{{ message.text }}</p>
                    </div>
                  </div>
                </div>
              </ng-template>
              <ng-template #outgoing_msg>
                <div class="outgoing_msg">
                  <div class="sent_msg">
                    <p>{{ message.text }}</p>
                  </div>
                </div>
              </ng-template>
</li>

What this part does is that it iterate over the list of messages and check should the message go on the left or on the right side.

Let's say that if the Student is logged in, he can choose to open his conversation with some Teacher. In that case you will need to get all the messages between that particular Student and than particular Teacher. You can create an endpoint that will return the message history of the current user and selected interlocutor. For that let's create an endpoint that will take current_user_id, current_user_role and interlocutor_id and based on that will return the message-history.

The endpoint can look like this:

router.get('/message-history', (req, res, next) => {

  const { current_user_id, current_user_role, interlocutor_id } = req.query;

  if (current_user_role === 'student') {
    Students.findById(current_user_id).then((student) => {
      let messages = student.messages.filter((message) => (message.sender == interlocutor_id || message.receiver== interlocutor_id));
      res.status(200).json({ success: true, messages: messages })
    }).catch((error) => {
      res.status(404).json({ success: false, message: 'User can not be found.' })
    })
  } else {
    Teachers.findById(current_user_id).then((teacher) => {
      let messages = student.messages.filter((message) => (message.sender == interlocutor_id || message.receiver== interlocutor_id));
      res.status(200).json({ success: true, messages: messages })
    }).catch((error) => {
      res.status(404).json({ success: false, message: 'User can not be found.' })
    })
  }

});

Now when you have the message-history array you can divide the messages. If the message has the property receiver you can put it on one side, and if it has property sender you can put it on other side.

Related