$mod 查询无法直接走索引,必须用计算字段 + 索引替代;因 MongoDB 索引不支持动态取模运算,需预存如 qty_mod4 字段并为其建索引,且负数需按 JavaScript 规则严谨计算。

$mod 查询无法直接走索引,必须用计算字段 + 索引替代
MongoDB 的 $mod 操作符本身不支持索引加速——无论你对 qty 字段建了什么索引,{ qty: { $mod: [4, 0] } } 这类查询永远触发全集合扫描(COLLSCAN)。这是引擎层限制,不是配置或写法问题。
为什么 $mod 不能用索引?
MongoDB 索引只支持范围、等值、前缀匹配等可预判的查找模式。$mod 需要对每个文档实时计算 value % divisor === remainder,这个运算结果无法从 B-tree 索引结构中推导出来。官方文档明确说明:$mod 是“non-indexable operator”。
创建计算字段(computed field)并建索引
真正可行的优化路径是:把取模逻辑提前固化到文档里,再对这个新字段建索引。
- 在写入或更新时,同步写入一个预计算字段,比如
qty_mod4,值为doc.qty % 4 - 查询时不再用
$mod,而是查{ qty_mod4: 0 } - 对该字段建普通索引:
db.inventory.createIndex({ qty_mod4: 1 })
示例:
db.inventory.insertMany([
{ _id: 1, item: "abc123", qty: 0, qty_mod4: 0 },
{ _id: 2, item: "xyz123", qty: 5, qty_mod4: 1 },
{ _id: 3, item: "ijk123", qty: 12, qty_mod4: 0 }
])
db.inventory.find({ qty_mod4: 0 }) // ✅ 走索引注意 divisor 变化时的维护成本
计算字段和索引是绑定具体除数的。如果你需要同时支持 mod 2、mod 4、mod 5 多种场景:
- 得分别建多个字段,如
id_mod2、id_mod4、id_mod5 - 每次更新
id时,所有相关字段都要重算 - 索引数量增加,写入开销变大,需权衡读/写比例
别指望用一个通用字段解决所有 $mod 场景——MongoDB 没有表达式索引(不像 PostgreSQL 的 functional index),db.collection.createIndex({ "$expr": { $eq: [{ $mod: ["$qty", 4] }, 0] } }) 是非法语法,会报错。
最易被忽略的一点:负数取模结果依赖 JavaScript 行为(余数符号与被除数一致),如果业务数据含负 qty,计算字段的值必须严格按 Math.floor(value / divisor) * divisor + remainder 规则生成,不能简单用语言内置 % 运算符——否则索引值和查询条件会错位。



















