Mongoose: How is sort order maintained when using the object notation?

Created on 30 Jan 2017  路  11Comments  路  Source: Automattic/mongoose

ECMAScript defines an object as follows:

It is an unordered collection of properties each of which contains a primitive value, object, or function.

So when using the sort method with the object notation (e.g. query.sort({ field: 'asc', test: -1 });), how can the order of the keys actually be maintained? Currently, V8 does maintain the order, but can we rely on that to never change?

The official Node MongoDB driver actually suggests to use different notations for whether there is one or several sort properties:

Sorting can be acieved with option parameter sort which takes an array
of sort preferences

{   "sort": [['field1','asc'], ['field2','desc']] }

With single ascending field the array can be replaced with the name of the field.

{   "sort": "name" }
won't fix

Most helpful comment

@vkarpov15

I tried to use this syntax [['field1', 'asc'],['field2', 'desc']] inside .aggregate

but I got this error:

"name":"MongoError","message":"the $sort key specification must be an object"

All 11 comments

Yep, that's an unfortunate dilemma for both mongoose and the mongodb driver. ECMAScript says that keys are not ordered, but they are actually ordered in V8 (except for numeric keys, which is a nasty edge case), and likely will continue to be ordered in V8 for the foreseeable future. You can use the [['field1', 'asc']] syntax or the mongoose-specific 'field1 -field2' syntax or the ES2015 Map class (which guarantees insertion order for keys) if you're concerned with key order.

Thanks for clarifying and adding support for Maps.

@vkarpov15

I tried to use this syntax [['field1', 'asc'],['field2', 'desc']] inside .aggregate

but I got this error:

"name":"MongoError","message":"the $sort key specification must be an object"

@aguyinmontreal what version of mongoose and mongodb?

@vkarpov15 Mongoose 4.7.4 MongoDB 3.4.1

Sort using Map fails for me (mongoose v 4.10.5, mongodb 3.4.10). Here's what I've tried:
SomeModel.find({}).sort(new Map([['eventId','asc'],['level','desc']]))
SomeModel.find({}).sort(new Map([['eventId',1],['level',1]]))
SomeModel.find({},{},{sort: new Map([['eventId',1],['level',1]])})
SomeModel.find({}).sort(new Map().set('field', 1).set('test', -1))

And the error I get:

MongoError: bad sort specification
      at Function.MongoError.create (node_modules\mongoose\node_modules\mongodb-core\lib\error.js:31:11)
      at queryCallback (node_modules\mongoose\node_modules\mongodb-core\lib\cursor.js:212:36)
      at node_modules\mongoose\node_modules\mongodb-core\lib\connection\pool.js:469:18
      at _combinedTickCallback (internal/process/next_tick.js:131:7)
      at process._tickDomainCallback (internal/process/next_tick.js:218:9)

@tonybranfort you don't need to use Map, just do [['eventId', 1]]

Thanks vkarpov15. I don't actually create the Map for the sort. It already exists before the sort - thought I could sort without converting it out of a Map. No problem to convert it before the sort. And thanks for all your work on Mongoose.

Still getting the "the $sort key specification must be an object" error when running:

aggregate([{"$sort":[["name",-1]]}, {"$limit":10}]);

Mongoose 5.3.1.
Mongo Server: 3.4.2

@NikitaVlaznev I'm not certain MongoDB aggregation supports that syntax, but I'll double check

Yeah unfortunately the MongoDB server does not support the array-based syntax. However, you don't actually need this syntax since the ES6 spec specifies the order of keys in an object, so any spec-compliant JS runtime like node is going to give you the right key order every time.

Was this page helpful?
0 / 5 - 0 ratings