用$regex/i查询慢且无法走普通索引,因其实质是全表扫描;需建collation索引并查询时显式指定相同collation才能高效实现大小写不敏感匹配。
直接用
加
选项最简单,但性能差、无法走普通索引;真要兼顾性能和大小写不敏感,必须用
创建带排序规则的索引,并在查询时显式指定相同
,否则索引压根不会被使用。
为什么 $regex /i 查询慢且不能用普通索引
正则表达式匹配(如
)本质是全表扫描:MongoDB 无法利用字段上的标准 B-tree 索引做前缀或等值查找,因为
的执行逻辑绕过了索引的有序结构。即使字段有索引,该查询也基本等于没建。
每次查询都触发字符串逐字符比对,CPU 开销大
索引中存储的是原始字节值,
不会自动映射到大小写归一化的索引项
除非正则以锚点
开头且无通配符,否则连「前缀索引」都用不上
创建 collation 索引的正确写法(Python / PyMongo)
索引必须明确声明
,且
和
要匹配实际需求。常见错误是只设
却忽略
,导致大小写仍被区分。
(Primary):只比较字母基础形,忽略大小写、重音、变音 —— 这才是真正的「大小写不敏感」
(Secondary):区分重音但忽略大小写(如
和
视为不同)
必须用
显式创建,不能靠查询自动触发
注意:
值不是随便填的字符串,必须是 MongoDB 支持的 ICU 区域 ID(如
,
,
),不支持
这类带下划线的格式(部分旧版本会静默失败)。
查询时必须传相同的 collation 才能命中索引
这是最容易漏掉的关键点:索引建了,但查询不带
,MongoDB 默认用
二进制排序规则,完全不走你刚建的索引。
MongoDB For Windows v3.5.4
MongoDB For Windows v3.5.4
下载
正确写法(PyMongo):
错误写法:
—— 看似一样,但底层排序规则不同,索引失效
聚合操作同理:
可以用
验证是否用了索引:
应为
,而非
。
collation 索引的限制与替代场景
collation 索引不是万能的,它只对字符串字段的等值、范围、排序类操作生效,且要求查询条件和索引定义严格对齐。
复合索引中,只有前缀字段是字符串且带 collation 时,该部分才受排序规则影响;后续数字/日期字段不受影响
不能用于
索引,
自带语言处理,但不支持自定义
如果业务需要同时支持「大小写敏感」和「不敏感」两种查询,得建两个索引(一个带 collation,一个不带),空间成本翻倍
真正难处理的是模糊搜索(比如查
能匹配
)—— collation 索引对此无效,此时只能回归
或引入全文索引 +
,但后者对拼写容错能力有限。
$regexicollationcollation{"name": {"$regex": "^john$", "$options": "i"}}$regex$regex^collationlocalestrengthlocalestrengthstrength: 1strength: 2cafecafécreate_indexcollection.create_index(
[("name", 1)],
collation={"locale": "en", "strength": 1}
)locale"en""zh""fr""en_US"collation"simple"collection.find({"name": "John"}).collation({"locale": "en", "strength": 1})collection.find({"name": "John"})collection.aggregate([...]).collation({...})explain()find(...).collation(...).explain()["executionStats"]["executionStages"]["stage"]IXSCANCOLLSCAN$text$textstrength"jo""John"$regex$text