MongoDB 排序(sort())完整指南
(MongoDB 8.0+,2025 年最新实践)
核心结论:
sort()= 控制查询结果顺序,必须配合索引才能高效
语法:.sort({ field1: 1, field2: -1 })→1升序,-1降序
一、基本语法
db.collection.find()
.sort({ field: 1 }) // 升序(ascending)
.sort({ field: -1 }) // 降序(descending)
| 值 | 含义 |
|---|---|
1 | 升序(从小到大) |
-1 | 降序(从大到小) |
二、单字段排序示例
use company
// 按年龄升序
db.employees.find().sort({ age: 1 }).pretty()
// 按薪资降序
db.employees.find().sort({ salary: -1 }).limit(5).pretty()
// 按入职日期最新优先
db.employees.find().sort({ hireDate: -1 })
三、多字段排序(复合排序)
// 先按部门升序,再按薪资降序(同部门高薪优先)
db.employees.find()
.sort({
department: 1, // 先排部门
salary: -1 // 再排薪资
})
.pretty()
| 排序字段 | 顺序 |
|---|---|
department: 1 | 部门 A → Z |
salary: -1 | 薪资 高 → 低 |
执行顺序:从左到右,先排第一个字段,再排第二个。
四、与 limit、skip 结合(分页)
// 第 2 页,每页 10 条,按创建时间倒序
db.posts.find()
.sort({ createdAt: -1 })
.skip(10)
.limit(10)
必须先 sort() 再 skip()/limit(),否则分页不稳定!
五、索引是排序性能之魂!
| 排序方式 | 是否需要索引 | 推荐索引 |
|---|---|---|
| 单字段 | 是 | { age: 1 } |
| 复合排序 | 是 | { department: 1, salary: -1 } |
| 无索引 | 否 | 全表扫描,慢! |
// 创建支持排序的索引
db.employees.createIndex({ department: 1, salary: -1 })
// 查看执行计划
db.employees.find()
.sort({ department: 1, salary: -1 })
.explain("executionStats")
索引使用规则(ESR 原则):
Equality → Sort → Range
即:等值匹配 → 排序字段 → 范围查询
// 最佳索引:支持过滤 + 排序
db.employees.createIndex({
status: 1, // E: 等值
department: 1, // S: 排序
salary: -1 // S: 排序
})
六、特殊字段排序
1. 嵌套字段
// 按地址城市排序
db.users.find().sort({ "address.city": 1 })
// 索引
db.users.createIndex({ "address.city": 1 })
2. 数组字段(按数组中某个值)
// 按 scores 数组第一个元素排序
db.students.find().sort({ "scores.0": -1 })
// 按数组中最大值排序(需聚合)
db.students.aggregate([
{ $addFields: { maxScore: { $max: "$scores" } } },
{ $sort: { maxScore: -1 } }
])
3. 文本得分排序(全文搜索)
// 创建文本索引
db.articles.createIndex({ title: "text", content: "text" })
// 按相关度排序
db.articles.find(
{ $text: { $search: "MongoDB" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
七、内存限制:排序超过 100MB 报错!
Error: error processing query: ... Sort exceeded memory limit of 104857600 bytes
解决方案
| 方案 | 说明 |
|---|---|
| 创建索引 | 让排序走索引,不占内存 |
| 允许磁盘排序 | .allowDiskUse(true)(仅聚合) |
| 分批处理 | 小批量排序 |
// 聚合中允许使用磁盘
db.logs.aggregate([
{ $sort: { timestamp: -1 } }
]).allowDiskUse(true)
八、生产级排序最佳实践
| 场景 | 推荐方案 |
|---|---|
| 列表页 | sort + limit + 索引 |
| 分页 | skip 小数据,范围查询 大数据 |
| 排行榜 | sort + limit(100) + 索引 |
| 实时 feeds | sort({ ts: -1 }) + 索引 |
// 排行榜:前 100 名高薪员工
db.employees.find()
.sort({ salary: -1 })
.limit(100)
.projection({ name: 1, salary: 1, _id: 0 })
九、GUI 工具排序
| 工具 | 操作 |
|---|---|
| MongoDB Compass | 查询栏 → Sort → 拖拽字段 |
| MongoDB Atlas | Query → Sort: { age: 1 } |
| VS Code | .sort({ field: -1 }) |
十、完整排序脚本示例
// sort_demo.js
use company
print("=== MongoDB 排序示例 ===\n")
// 1. 高薪前 5 名(降序)
print("1. 薪资最高前 5 人:")
db.employees.find()
.sort({ salary: -1 })
.limit(5)
.projection({ name: 1, salary: 1, department: 1, _id: 0 })
.forEach(printjson)
// 2. 按部门 + 薪资排序
print("\n2. 按部门升序、薪资降序:")
db.employees.find()
.sort({ department: 1, salary: -1 })
.limit(10)
.projection({ name: 1, department: 1, salary: 1, _id: 0 })
.forEach(printjson)
// 3. 最近入职的 3 人
print("\n3. 最近入职员工:")
db.employees.find()
.sort({ hireDate: -1 })
.limit(3)
.projection({ name: 1, hireDate: 1, _id: 0 })
.forEach(printjson)
// 4. 创建性能索引
print("\n创建复合索引:department + salary")
db.employees.createIndex({ department: 1, salary: -1 })
print("索引创建完成!")
运行:
mongosh sort_demo.js
十一、一句话总结
**“排序用
sort({ field: -1 }),性能靠 *索引*,分页弃skip,用 **范围查询”
快速排序模板
// 降序(最新)
.sort({ createdAt: -1 })
// 升序(最早)
.sort({ createdAt: 1 })
// 复合排序
.sort.sensor({ status: 1, priority: -1, createdAt: -1 })
// 高性能:确保有索引
db.collection.createIndex({ status: 1, priority: -1 })
官方文档:
- https://www.mongodb.com/docs/manual/reference/method/cursor.sort/
- https://www.mongodb.com/docs/manual/indexes/
如需 动态排序字段、多级排序聚合、从 SQL ORDER BY 迁移 或 实时排序流,欢迎继续提问!