In MongoDB, the $sort
aggregation pipeline stage sorts all input documents and returns them to the pipeline in sorted order.
Syntax
The syntax goes like this:
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
Where <sort order>
can be 1
for ascending, -1
for descending, or { $meta: "textScore" }
to sort by the computed textScore
metadata in descending order.
Sample Data
Suppose we have a collection called pets
with the following documents:
{ "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : 4, "name" : "Scratch", "type" : "Cat", "weight" : 8 } { "_id" : 5, "name" : "Bruce", "type" : "Kangaroo", "weight" : 100 } { "_id" : 6, "name" : "Hop", "type" : "Kangaroo", "weight" : 130 } { "_id" : 7, "name" : "Punch", "type" : "Kangaroo", "weight" : 200 } { "_id" : 8, "name" : "Snap", "type" : "Cat", "weight" : 12 } { "_id" : 9, "name" : "Ruff", "type" : "Dog", "weight" : 30 }
Sort in Ascending Order
To sort in ascending order, we use 1
for the sort order.
Below is an example of a query that uses the $sort
operator to sort that collection by the weight
field in ascending order.
db.pets.aggregate([
{ $sort: { weight: 1 } }
])
Result:
{ "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : 4, "name" : "Scratch", "type" : "Cat", "weight" : 8 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : 8, "name" : "Snap", "type" : "Cat", "weight" : 12 } { "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 9, "name" : "Ruff", "type" : "Dog", "weight" : 30 } { "_id" : 5, "name" : "Bruce", "type" : "Kangaroo", "weight" : 100 } { "_id" : 6, "name" : "Hop", "type" : "Kangaroo", "weight" : 130 } { "_id" : 7, "name" : "Punch", "type" : "Kangaroo", "weight" : 200 }
Sort in Descending Order
To sort in descending order, we use -1
for the sort order.
db.pets.aggregate([
{ $sort: { weight: -1 } }
])
Result:
{ "_id" : 7, "name" : "Punch", "type" : "Kangaroo", "weight" : 200 } { "_id" : 6, "name" : "Hop", "type" : "Kangaroo", "weight" : 130 } { "_id" : 5, "name" : "Bruce", "type" : "Kangaroo", "weight" : 100 } { "_id" : 9, "name" : "Ruff", "type" : "Dog", "weight" : 30 } { "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 8, "name" : "Snap", "type" : "Cat", "weight" : 12 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : 4, "name" : "Scratch", "type" : "Cat", "weight" : 8 } { "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 }
Sort by Multiple Fields
To sort by more than one field, separate each field/sort order combo with a comma.
Example
db.pets.aggregate([
{ $sort: { type: 1, weight: -1, _id: 1 } }
])
Result:
{ "_id" : 8, "name" : "Snap", "type" : "Cat", "weight" : 12 } { "_id" : 4, "name" : "Scratch", "type" : "Cat", "weight" : 8 } { "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : 9, "name" : "Ruff", "type" : "Dog", "weight" : 30 } { "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : 7, "name" : "Punch", "type" : "Kangaroo", "weight" : 200 } { "_id" : 6, "name" : "Hop", "type" : "Kangaroo", "weight" : 130 } { "_id" : 5, "name" : "Bruce", "type" : "Kangaroo", "weight" : 100 }
In this example, we sorted by the type
field in ascending order first, then by the weight
field in descending order, then by the _id
field in ascending order.
This means that, if there are multiple pets of the same type, those pets are sorted by their weight
in descending order. If there are multiple pets with both the same type and weight, then those pets are sorted by the _id
field in ascending order. If we hadn’t included the _id
field in the sorting process, then those pets of the same type and weight could appear in any order. This is true each time we run the query. Without having a sort field on a unique field (such as the _id
field), it would be entirely possible (even probable) that the results would come back in a different order each time the query was run.
Sorting Different Types
When comparing values of different BSON types, MongoDB uses the following comparison order, from lowest to highest:
- MinKey (internal type)
- Null
- Numbers (ints, longs, doubles, decimals)
- Symbol, String
- Object
- Array
- BinData
- ObjectId
- Boolean
- Date
- Timestamp
- Regular Expression
- MaxKey (internal type)
Suppose we have a collection called posts with the following documents:
{ "_id" : 1, "title" : "Web", "body" : "Create a website with these three easy steps...", "date" : "2021-01-01T00:00:00.000Z" } { "_id" : 2, "title" : "Animals", "body" : "Animals are funny things...", "date" : ISODate("2020-01-01T00:00:00Z") } { "_id" : 3, "title" : "Oceans", "body" : "Oceans are wide and vast...", "date" : ISODate("2021-01-01T00:00:00Z") }
Notice that the first date
field contains a date string, whereas the other two documents use a Date object.
Also notice that the date string contains exactly the same date as document 3, and this date is a later date than the date in document 2.
Let’s apply $sort
to the date
fields of those documents:
db.posts.aggregate([
{ $sort: { date: 1 } }
]).pretty()
Result:
{ "_id" : 1, "title" : "Web", "body" : "Create a website with these three easy steps...", "date" : "2021-01-01T00:00:00.000Z" } { "_id" : 2, "title" : "Animals", "body" : "Animals are funny things...", "date" : ISODate("2020-01-01T00:00:00Z") } { "_id" : 3, "title" : "Oceans", "body" : "Oceans are wide and vast...", "date" : ISODate("2021-01-01T00:00:00Z") }
In this case we sorted in ascending order, which means earlier dates should come first. However, our first document contains a date string instead of a Date object, and so it came first – even though its date is later than the date in document 2.
Here it is again, but in descending order:
db.posts.aggregate([
{ $sort: { date: -1 } }
]).pretty()
Result:
{ "_id" : 3, "title" : "Oceans", "body" : "Oceans are wide and vast...", "date" : ISODate("2021-01-01T00:00:00Z") } { "_id" : 2, "title" : "Animals", "body" : "Animals are funny things...", "date" : ISODate("2020-01-01T00:00:00Z") } { "_id" : 1, "title" : "Web", "body" : "Create a website with these three easy steps...", "date" : "2021-01-01T00:00:00.000Z" }
Once again, the date ordering is out of whack, due to the different data types.
Text Score Metadata Sort
You can use the { $meta: "textScore" }
argument to sort by descending relevance score when using $text
searches.
Example
db.posts.aggregate(
[
{ $match: { $text: { $search: "funny" } } },
{ $sort: { score: { $meta: "textScore" }, title: -1 } }
]
).pretty()
Result:
{ "_id" : 2, "title" : "Animals", "body" : "Animals are funny things...", "date" : ISODate("2020-01-01T00:00:00Z") }
In this case, only one document matched our query.
In this example, we sorted by { $meta: "textScore" }
, then by title
in descending order. We used score
as an arbitrary field name, but this is ignored by the query system.
Doing $text
searches like this requires that we’ve created a text index. If not, an IndexNotFound
error will be returned.
Sorting Grouped Results
Going back to our pets
collection, we can use the $sort
stage after a $group
stage to sort a group of documents by the number of values in a particular field.
db.pets.aggregate([
{
$match: { weight: { $lt: 30 } }
},
{
$group: { _id: "$type", count: { $sum: 1 } }
},
{
$sort : { count : -1 }
}
])
Result:
{ "_id" : "Cat", "count" : 3 } { "_id" : "Dog", "count" : 2 }
However, you might be better off using the $sortByCount
operator in such cases.
More Information
See the MongoDB documentation for more information.