Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose - find last message from each user

I'm working on message system and I need to get last message from each user who sent message to logged user. I have this structure in mongoDB:

[{
    "_id": "551bd621bb5895e4109bc3ce",
    "from": "admin",
    "to": "user1",
    "message": "message1",
    "created": "2015-04-01T11:27:29.671Z"
}, {
    "_id": "551bd9acf26208ac1d9b831d",
    "from": "user1",
    "to": "admin",
    "message": "message2",
    "created": "2015-04-01T11:42:36.936Z"
}, {
    "_id": "551bdd6d849d53001dd8a64a",
    "from": "user1",
    "to": "user2",
    "message": "message3",
    "created": "2015-04-01T11:58:37.858Z"
}, {
    "_id": "551bdd99849d53001dd8a64b",
    "from": "user2",
    "to": "admin",
    "__v": 0,
    "message": "message4",
    "created": "2015-04-01T11:59:21.005Z"
}, {
    "_id": "551bdda1849d53001dd8a64c",
    "from": "user1",
    "to": "admin",
    "__v": 0,
    "message": "message5",
    "created": "2015-04-01T11:59:29.971Z"
}]

I need to get fields from, message and created from last message of each user who sent message to logged user. I tried use distinct but it returns only one field. I have this:

Message.find({
        to: req.user.username
    })
    .select('message created')
    .sort('-created')
    .exec(function (err, messages) {
        if (err) {
            return res.status(400).send({
                message: getErrorMessage(err)
            });
        } else {
            res.json(messages)
        }
    });

but it returns all users who sent messages to logged user and I need to have only unique users and their last message. Is there some way how to do it with mongoose?

like image 524
Jaroslav Klimčík Avatar asked Apr 02 '15 10:04

Jaroslav Klimčík


1 Answers

Use aggregation framework where your pipeline stages have $match, $sort, $group and $project expressions:

Message.aggregate(
    [
        // Matching pipeline, similar to find
        { 
            "$match": { 
                "to": req.user.username
            }
        },
        // Sorting pipeline
        { 
            "$sort": { 
                "created": -1 
            } 
        },
        // Grouping pipeline
        {
            "$group": {
                "_id": "$from",
                "message": {
                    "$first": "$message" 
                },
                "created": {
                    "$first": "$created" 
                }
            }
        },
        // Project pipeline, similar to select
        {
             "$project": { 
                "_id": 0,
                "from": "$_id",
                "message": 1,
                "created": 1
            }
        }
    ],
    function(err, messages) {
       // Result is an array of documents
       if (err) {
            return res.status(400).send({
                message: getErrorMessage(err)
            });
        } else {
            res.json(messages)
        }
    }
);

If req.user.username = "admin", with your sample collection then the result is:

{
    "result" : [ 
        {
            "message" : "message4",
            "created" : "2015-04-01T11:59:21.005Z",
            "from" : "user2"
        }, 
        {
            "message" : "message5",
            "created" : "2015-04-01T11:59:29.971Z",
            "from" : "user1"
        }
    ],
    "ok" : 1
}
like image 126
chridam Avatar answered Nov 03 '22 00:11

chridam