Search code examples
node.jsmongodbexpressaggregateaggregation

I need help to Aggregate for mongoDb Chat App to receive latest message from each related chat


I'm making a chat app, and there will be only one conversation between two person so I didn't make conversation table or ID and the way I want to show list of chats to clients will be like this:

I will get the latest message related to each conversation, conversations are filtered by sender/receiver ID's,

So if there is 2 message in database and the first one has this data :

{
    sender: "User.x.ID",
    receiver: "User.y.ID",
    msg: "How are you?",
},
{
    sender: "User.y.ID",
    receiver: "User.x.ID",
    msg: "Good thanks, and you?",
},

The latest message will be "Good thanks, and you?"

This my messageSchema:

{
    sender: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
    },
    receiver: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
    },
    usersRelated: {
        type: Array,
    },
    msg: {
        type: String,
    }
}

This is my code:

const Chats = await Message.aggregate([
    { $sort: { createdAt: -1 } },
    {
        $match: {
            $expr: {
                usersRelated: [req.user.id],
            },
        },
    },
    {
        $group: {
            _id: {
                sender: "$sender",
                receiver: "$receiver",
            },
            messages: {
                $top: {
                    sortBy: { createdAt: -1 },
                    output: "$$ROOT",
                },
            },
        },
    },
    { $replaceWith: "$messages" },
])

And it returns both of the messages which I don't want, I only want the latest message which will be : "Good thanks, and you?".

The reason behind of my need is :

I am trying to show the lists of chats with the preview of latest message same as most of chat apps.

So if you are logged in as User X what you will see in your list of chat will be one chat with User Y and they responded to you: "Good thanks, and you?" and if you click on the chat, then I will load all of the messages related to this two user which will be like this :

User X said : How are you?

User Y Said : Good thanks, and you?

Solution

  • Maybe this one:

    db.collection.aggregate([
       {
          $group: {
             _id: { $sortArray: { input: ["$sender", "$receiver"], sortBy: 1 } },
             messages: {
                $top: {
                   sortBy: { createdAt: -1 },
                   output: "$$ROOT"
                }
             }
          }
       },
       { $replaceWith: "$messages" },
    ])
    

    $top was introduced in MongoDB version 5.2. If you run an older version, then try this:

    db.collection.aggregate([
       { $sort: { createdAt: -1 } },
       {
          $group: {
             _id: { $sortArray: { input: ["$sender", "$receiver"], sortBy: 1 } },
             messages: { $first: "$$ROOT" }
          }
       },
       { $replaceWith: "$messages" },
    ])