通配符索引(WildcardIndex)仅适用于已知父路径、子字段名动态的等值或范围查询,如{"metadata.$**": 1}支持{"metadata.color": "blue"},但不加速{name: {$regex: ".liu."}}、不索引数组元素、不替代text索引。

通配符索引(WildcardIndex)不是用来加速字符串模糊搜索的,它只对「已知路径 + 动态字段名」的等值或范围查询有效;如果你正试图用 {"metadata.$**": 1} 加速 {name: {$regex: ".*liu.*"}},那它根本不会起作用。
什么时候该建 {"field.$**": 1} 索引
你只有在以下情况才需要它:
- 文档里有个固定字段(比如
metadata),但它内部的子字段名完全不固定:可能是metadata.color、metadata.priority_2024、metadata.custom_tag_v3,且这些字段名由用户或爬虫动态生成 - 你需要频繁查其中某个具体路径,例如
{"metadata.color": "red"}或{"metadata.version": {"$gte": "2.0"}},但无法提前预知所有可能的子键名 - 你已经排除了重构 Schema 的可能性(比如无法把所有自定义属性统一存到
metadata.key和metadata.value这样的标准结构里)
$** 不支持数组内字段索引
通配符索引不会展开数组内容——这是最容易踩的坑。假设你有:
db.items.insert({ metadata: { tags: ["fast", "secure"] } })
即使建了 {"metadata.$**": 1},下面这个查询也**无法走索引**:
db.items.find({ "metadata.tags": "fast" })
原因:tags 是数组,而 $** 只索引字段路径本身,不索引数组元素值。MongoDB 不会为 "fast" 单独建索引条目。
正确做法是:
- 如果必须按 tag 查询,改用
{"metadata.tags": 1}普通索引(前提是tags字段名固定) - 或者把 tag 扁平化为独立字段,如
metadata.tag_fast: true,再配合{"metadata.$**": 1}
$regex 查询别指望 $** 加速
很多用户误以为通配符索引能优化模糊匹配,但事实是:
-
{name: {$regex: "^Alice"}}→ 可走普通{name: 1}索引(必须带^锚点、无i标志) -
{name: {$regex: ".*lice.*"}}或{desc: {$regex: /fast/i}}→ 必然全表扫描,$**完全无效 -
$text查询要用{"$**": "text"}(通配符文本索引),不是{"$**": 1}
真要模糊查字符串,优先考虑:text 索引、MongoDB Search、或前置计算前缀字段(如 name_prefix_3)。
通配符索引的真实代价容易被低估
它不是“设了就完事”的银弹:
- 索引体积可能数倍于原始数据——每个匹配的嵌套路径都单独建 B-tree 条目,
metadata下有 50 个动态字段,就相当于建了 50 个单字段索引 - 写入变慢:每次更新任意一个
metadata.xxx字段,都要同步刷多个索引页 - 不支持
sparse、TTL、partialFilterExpression等常用选项 - 查询必须显式写出完整路径,
{"metadata": {"$exists": true}}这种宽泛条件无法利用它
真正关键的一点:它只在查询**精确命中某个已展开的字段路径**时才生效,多一层间接(比如用变量拼接字段名)、少一个点(比如写成 metadata.color 但实际是 metadata.colors),索引就彻底失效。

















