37

I would like to sort a populated Document from the collection i fetch, i get an error requesting it.

Let's admit a Document Group (Group) and 'Member' (Group.Members)

Group
  .find({})
  .populate('Members')

Works perfectly, but i would like to sort it so i do this:

Group
  .find({})
  .populate('Members', ['_id', 'name'], null, { sort: [[ 'created_at', 'desc' ]] })

I get the error TypeError: Invalid select() argument. Must be a string or object. by adding this...

Ludo
  • 5,060
  • 15
  • 53
  • 85

8 Answers8

82

You can also explicitly specify only required parameters of populate:

Group
  .find({})
  .populate({path: 'Members', options: { sort: { 'created_at': -1 } } })

Have a look at http://mongoosejs.com/docs/api.html#document_Document-populate

Artem Fedosov
  • 2,163
  • 2
  • 18
  • 28
  • Thanks, Artem. It really solved my problem, but I am still unable to find a resource which digs deep into how Sorting with Mongoose really works! – MHBN May 10 '20 at 23:35
26

This example above works with Mongoose 2.x and above, use this syntax:

Group
  .find({})
  .populate('Members', '_id name', null, { sort: { 'created_at': -1 } })
Vishal Kumar
  • 403
  • 4
  • 13
Ludo
  • 5,060
  • 15
  • 53
  • 85
18

And for Mongoose 4.x use this syntax:

Kitten.find().populate({
    path: 'owner'
  , select: 'name'
  , match: { color: 'black' }
  , options: { sort: { name: -1 }}
}).exec(function (err, kittens) {
  console.log(kittens[0].owner.name) // Zoopa
})

// alternatively
Kitten.find().populate('owner', 'name', null, {sort: { name: -1 }}).exec(function (err, kittens) {
  console.log(kittens[0].owner.name) // Zoopa
})

Reference: Mongoose docs

Wtower
  • 18,848
  • 11
  • 103
  • 80
  • Also, I can not find exact reference of the behavior of the sort option in mongoose. But for sure options is the 5th parameter in the alternative syntax, after path, select, model and match. – Nico Passerini Sep 29 '16 at 12:43
  • @NicoPasserini sorry if it does not work for you, it is the exact example from the official documentation that is linked in the answer. So you probably doing sth wrong. – Wtower Sep 29 '16 at 13:25
  • 3
    I think it is a confirmed bug, please take a look at: https://github.com/Automattic/mongoose/issues/2202. – Nico Passerini Oct 06 '16 at 22:50
  • 2
    I am using mongoose 4.6 and my code looks like this ` .populate({ path: 'bookedFor.id', select: 'name mobileNumber', options: { sort: { mobileNumber: -1 } } })` But it is not working, Can anyone point out the error? – Gaurav Jan 03 '17 at 12:46
8

This worked correctly for me in Mongoose Versions 5 and above.

Clinics.findById(req.params.id).populate({path:'users',options:{ sort:{date : 1}}}).exec(callback);
Gaurang Dave
  • 3,956
  • 2
  • 15
  • 34
yogesh joshi
  • 81
  • 1
  • 2
  • 1
    Welcome to StackoverFlow. Please [take the tour](https://stackoverflow.com/tour), [read about how to ask good questions](https://stackoverflow.com/help/how-to-ask) and learn [how to create a Minimal, Complete and Verifiable Example](https://stackoverflow.com/help/mcve). – Gaurang Dave Mar 29 '18 at 06:58
  • I wonder, is there any documentation where they mention populate options? What I mean is that in https://mongoosejs.com/docs/populate.html#query-conditions some path, select, perDocumentLimit, match, name and options are mentioned. Are there other options besides these? It's all very little detailed. For example, they only mention options for limit, but they don't even explain options, where did you get that sort could be included in options? – Luis May 19 '23 at 14:51
2

The following worked for me in Mongoose v5.0.5:

    Schedule.find({})
        .populate({path: 'eventComments', options: {sort:{"commentDate": "descending"}}})
        .exec(function(err, result) {
            if (err) {
                throw err
            } 

            else {
                return res.json(result);
            }
    });

P.S. The key difference between this example and the Kitten example is that commentDate is in quotes, whereas Date (in the Kitten example) is not. This alteration may be necessary for some of you. Hope this helps.

NBB
  • 21
  • 1
0

I ended up needing to populate a nested document, this worked for me:

const video = await Video.findOne({ urlId }) // find video
        .populate('likes user') // populate the likes and owner (user) of video
        .populate({
            path: 'comments', // populate the comments as well,
            options: { sort: { date: -1 } }, // sorting the comments by date
            populate: {
                path: 'user', // and populating the owner (user) of each comment,
                select: 'username, date', // plucking whichever fields necessary from the User model
            },
        })
]);
Mike K
  • 7,621
  • 14
  • 60
  • 120
0

Comment.find({}).populate({path: 'comments', options: { sort: '-createdAt'} });

-createdAt -> here - is for descending order

0

This really worked for me:

User.find({}).populate({path: 'photos', options: { sort: { 'CreatedAt': -1 } } })
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77