0

I have a Chat model which has a messages array which holds Messages. Each Message has both a dateSent property and a dateViewed property. The Message items are embedded, not referenced.

How can I sort Chats by most recent not viewed Message (ie, empty dateViewed) first and then by dateSent? (think Facebook Messenger behavior...)

This is what I've tried so far (note that Chat also has a users array):

Chat.find({users._id: userId}).sort({messages.dateViewed: -1, messages.dateSent: -1}).exec(function(err, chats){
    // handle err
    // handle no docs found
    // handle docs found
});

The above does not seem to work, as the Chats come out in a different order (the same as without sort()). Any thoughts?

EDIT: The suggested answer didn't help me because I don't want to sort the Message objects in the message array, but rather sort the Chats resulting from the query, by the dates in the Message object they hold in a messages array.

Joum
  • 3,189
  • 3
  • 33
  • 64
  • 1
    I'm not sure how `messages.dateViewd: -1` is interpreted, I would suggest at least adding quotes, just to be sure everything's fine there :) – Andrey Popov Mar 21 '16 at 17:40
  • @AndreyPopov Yes, I've changed it to `{"messages.dateViewed": -1, "messages.dateSent": -1}`, but it still doesn't work... – Joum Mar 21 '16 at 17:54
  • Possible duplicate of [Mongodb: sort documents by array objects](http://stackoverflow.com/questions/5315658/mongodb-sort-documents-by-array-objects) – TomG Mar 21 '16 at 20:45
  • Another possible duplicate http://stackoverflow.com/questions/28889240/mongodb-sort-documents-by-array-elements – TomG Mar 21 '16 at 20:50
  • Possible duplicate of [Sort the array in the document with MongoDB](http://stackoverflow.com/questions/35889780/sort-the-array-in-the-document-with-mongodb) – Blakes Seven Mar 21 '16 at 21:10
  • @Tom The problem with peoples "possible duplicate" selection is that they rarely really look to see that the answer actually addresses the problem. For instance the answer I gave on [Sort the array in the document with MongoDB](http://stackoverflow.com/questions/35889780/sort-the-array-in-the-document-with-mongodb) is done in realization that [Mongodb: sort documents by array objects](http://stackoverflow.com/questions/5315658/mongodb-sort-documents-by-array-objects) does not actually answer the question asked, thought it will regularly come up in a search. – Blakes Seven Mar 21 '16 at 21:13
  • The other thing you may be asking is how to search by the "min" or "max" value from within the array on the results. That is answered here: [sort by embedded object value in Mongodb](http://stackoverflow.com/questions/33592200/sort-by-embedded-object-value-in-mongodb/33592563#33592563) – Blakes Seven Mar 21 '16 at 21:18

1 Answers1

1

The Aggregation pipeline should be used in order to achieve the required sort.

chats/messages:

> db.chats.find()
{ "_id" : "c1", "messages" : [ { "_id" : "m1_1", "dv" : -1, "ds" : 9000 }, { "_id" : "m1_2", "dv" : 8010, "ds" : 8000 } ] }
{ "_id" : "c2", "messages" : [ { "_id" : "m2_1", "dv" : -1, "ds" : 11000 } ] }
{ "_id" : "c3", "messages" : [ { "_id" : "m3_1", "dv" : -1, "ds" : 700 }, { "_id" : "m3_2", "dv" : 7010, "ds" : 7000 } ] }

The code to sort the data by the give criteria:

db.chats.aggregate([
{ $unwind: '$messages' },
{ $addFields: { 
        ts: { 
            $cond: [ 
                { $eq: ['$messages.dv', -1 ] }, 
                '$messages.ds', 
                '$messages.dv']
        }
    }
}, 
{ 
    $sort: { 'ts': -1 }
}, 
{     
    $group: {
        _id: '$_id',
        ts: { $max: '$ts'},
        messages: { $push: '$messages' }     
    }
}, 
{ 
    $sort: {ts: -1}
}]);

The results:

{ "_id" : "c2", "ts" : 11000, "messages" : [ { "_id" : "m2_1", "dv" : -1, "ds" : 11000 } ] }
{ "_id" : "c1", "ts" : 9000, "messages" : [ { "_id" : "m1_1", "dv" : -1, "ds" : 9000 }, { "_id" : "m1_2", "dv" : 8010, "ds" : 8000 } ] }
{ "_id" : "c3", "ts" : 7010, "messages" : [ { "_id" : "m3_2", "dv" : 7010, "ds" : 7000 }, { "_id" : "m3_1", "dv" : -1, "ds" : 700 } ] }
DraganS
  • 2,621
  • 1
  • 27
  • 40