MongoDB 默认排序是大小写敏感的,因其使用二进制比较(ASCII/UTF-8 字节值),大写字母 A–Z(65–90)码点小于小写字母 a–z(97–122),故 "Apple" 恒排在 "apple" 前;要实现大小写不敏感排序,必须显式指定 collation(如 'locale'=>'en_US')。

MongoDB 的默认排序是大小写敏感的,想实现大小写不敏感排序,必须显式使用 collation 参数指定 locale,否则 sort({name: 1}) 永远把 "Apple" 排在 "apple" 前面。
为什么默认 sort 不区分大小写却表现得像区分?
MongoDB 默认使用二进制比较(simple locale),它按 UTF-8 字节值排序,而大写字母 A–Z 的码点(65–90)小于小写 a–z(97–122),所以 "Zoo"
要让 "apple"、"Apple"、"APPLE" 归为同一组并自然排序,必须启用语言感知排序:
-
collation必须作为sort()或find()的独立选项传入,不能写在sort键内部 -
locale: "en"是最常用选择,支持英文大小写折叠和重音忽略(如 "cafe" ≈ "café") - 若字段含中文/日文等,需改用对应 locale(如
"zh"、"ja"),否则可能排序异常或报错
正确写法:collation 必须和 sort 并列,不能嵌套
错误示范(无效,被忽略):
db.users.find().sort({ name: 1, collation: { locale: "en" } })
正确写法(collation 是顶层选项):
db.users.find().sort({ name: 1 }).collation({ locale: "en" })
聚合管道中同理:
db.users.aggregate([
{ $sort: { name: 1 } },
{ $collation: { locale: "en" } }
])
注意:$collation 阶段必须紧跟在 $sort 后,且仅对后续阶段生效;更稳妥的是在 $sort 阶段直接带 collation 选项:
db.users.aggregate([
{ $sort: { name: 1 } }
]).collation({ locale: "en" })
索引必须匹配 collation,否则无法高效排序
如果对 name 字段执行 collation: { locale: "en" } 排序,但现有索引是默认创建的:
db.users.createIndex({ name: 1 })
这个索引不会被用于加速该排序——MongoDB 会回退到内存排序(in-memory sort),数据量稍大就容易触发 errmsg: "Sort exceeded memory limit..."。
解决办法:创建带匹配 collation 的索引:
db.users.createIndex({ name: 1 }, { collation: { locale: "en" } })
关键点:
- 索引的
collation必须和查询时完全一致(包括caseLevel、strength等字段) - 同一个字段可建多个 collation 索引,例如一个给大小写敏感查,一个给不敏感排
- 用
db.users.getIndexes()检查索引是否带"collation"字段,避免误判
locale 不是万能的,strength 和 caseLevel 得按需调
locale: "en" 默认启用 strength: 3(区分大小写+重音+变音),但如果你只想忽略大小写、保留重音(如 "cafe" ≠ "café"),就得降级:
db.users.find()
.sort({ name: 1 })
.collation({ locale: "en", strength: 2 })
strength: 2 表示只区分 base 字符(忽略大小写和重音),strength: 1 连字母都忽略(基本不用)。
另一个实用参数是 caseLevel: true,它会在排序键中插入一个“大小写层级”,让所有小写词先于大写词排(但组内仍自然序),适合某些特殊 UI 需求,不过多数场景用不到。
真正容易被忽略的是:一旦用了非默认 collation,所有涉及该字段的 find、sort、distinct、甚至 $regex 都受其影响——不是只改排序,而是整个比较逻辑变了。


















