必须为sort()显式指定collation才能使用collation索引,否则索引失效;复合索引中collation字段须最左;不同语言需匹配对应locale字符串,大小写敏感。

collation 索引必须和 sort() 显式配对使用
建了 {locale: "en", strength: 2} 的索引,但 db.users.find().sort({name: 1}) 依然慢?问题就出在这里:MongoDB 默认排序规则是二进制(simple),和 collation 索引不匹配,索引直接被跳过。你得在每次 sort() 后加 .collation({locale: "en", strength: 2}),否则 explain() 里一定看到 "stage": "SORT" 或 "stage": "COLLSCAN"。
常见错误包括:
- 只在
find()条件里用了 collation,但漏掉sort()后的 collation - 用
aggregate()时,在$sort阶段没传collation选项 - 驱动层(如 Node.js 的 mongodb driver)调用
sort()方法时,误以为 collation 是全局配置,其实必须每条命令单独传
复合索引中 collation 字段必须放最左,且顺序不能错
如果你要按 name(大小写不敏感)升序、再按 created_at 降序,索引定义必须是:db.users.createIndex({name: 1, created_at: -1}, {collation: {locale: "en", strength: 2}})。这里 name 是 collation 字段,它必须放在复合索引最左侧——哪怕你只查 {created_at: {$gt: ISODate(...)}},这个索引也完全无效。
原因很简单:MongoDB 的 ESR 规则(Equality → Sort → Range)在 collation 场景下依然生效,但 collation 只作用于它声明的字段及其右侧字段。所以:
-
{name: 1, status: 1}带 collation → 支持{name: "ALICE"}+.sort({name: 1, status: 1}) -
{status: 1, name: 1}带 collation →name的 collation 规则不生效,等同于没开 - 想同时支持
name和email的大小写不敏感排序?得建两个独立 collation 索引,不能合在一个复合索引里
别用 $regex 或 toLower() 替代 collation 索引
有人写 db.users.find({name: {$regex: "^alice$", $options: "i"}}) 或 db.users.find().sort({$expr: {$toLower: "$name"}}),短期能跑通,但一上量就崩:
-
$regex前缀匹配(如^ali)还能走索引,但全字段^alice$就是全表扫,explain()显示"nReturned"接近总文档数 -
$toLower在$sort中强制内存计算,无法利用任何索引;数据量超 32MB 会直接报错Sort exceeded memory limit - 驱动层手动转小写(如
.toLowerCase())看似简单,但破坏了大小写语义:比如用户存的是"McDonald",转成"mcdonald"后,排序结果和 collation 规则下的"McDonald"≠"mcdonald"不一致
中文或特殊语言排序需显式指定 locale
如果字段含中文、德语变音符或西班牙语重音字,strength: 2 配 locale: "en" 会出问题——“张”和“章”在英文 locale 下可能被当成相同,但中文排序需要拼音或笔画逻辑。这时必须换 locale:
- 中文推荐
{locale: "zh", strength: 2}(基于 Unicode 排序,兼容多数拼音顺序) - 德语用
{locale: "de", strength: 2}才能让ä和a等价 - 所有查询、索引、聚合中的
$sort必须用完全相同的locale字符串,连大小写都不能错("ZH"≠"zh")
最容易被忽略的一点:MongoDB 的 collation 是严格字符串匹配,locale 值不是自动推导的,也不是区域设置继承来的。你建索引时写 "zh",查询时写 "Chinese" 或留空,索引就废了。











