$size仅支持精确匹配数组长度,性能好但不支持比较操作;$where可写js表达式但性能差且默认禁用;推荐用$expr+$size组合实现范围查询,并注意类型校验与索引优化。

用 $size 筛选数组长度最直接,但只支持精确匹配
$size 是 MongoDB 原生操作符,性能好、语法简洁,适用于「数组长度等于某值」的场景。它不支持 $gt、$lt 等比较操作,也不能组合多个条件(比如「长度 ≥ 3 且包含某个元素」)。
常见错误是误以为 { tags: { $size: { $gt: 2 } } } 能工作——这会直接报错 errmsg: "size operator must be followed by a number"。
- ✅ 正确写法:
{ "tags": { "$size": 3 } }(匹配tags恰好含 3 个元素的文档) - ✅ 支持嵌套字段:
{ "comments.authorIds": { "$size": 1 } } - ⚠️ 注意:如果字段不存在或不是数组,该文档不会被匹配(即使
comments.authorIds是null或undefined)
$where 可写任意 JS 表达式,但性能差、不能走索引
$where 允许你用 JavaScript 判断数组长度,比如 arr.length > 2,但它在服务端逐文档执行 JS,无法利用索引,数据量稍大(几万条以上)就会明显变慢,生产环境应避免。
典型错误是忽略作用域和类型安全:MongoDB 的 $where 函数里 this 指向当前文档,但字段可能为 null、undefined 或非数组类型,直接调用 .length 会报错 TypeError: Cannot read property 'length' of null。
- ✅ 安全写法:
{ $where: "Array.isArray(this.tags) && this.tags.length > 2" } - ✅ 避免隐式转换:
this.tags && this.tags.length > 2不够健壮(字符串也有length) - ⚠️ 限制:MongoDB 6.0+ 默认禁用
$where(需启动时加--javascriptEnabled),云服务(如 Atlas)通常彻底禁用
替代方案:用 $expr + 聚合操作符更安全、可索引
从 MongoDB 3.6 开始,$expr 支持在查询中使用聚合表达式,比如 $size、$gt、$isArray,既保留了 $where 的灵活性,又避免了 JS 执行开销,部分表达式还能配合索引(如 $size 单独使用时)。
- ✅ 匹配长度大于 2:
{ $expr: { $gt: [{ $size: "$tags" }, 2] } } - ✅ 带类型校验:
{ $expr: { $and: [{ $isArray: "$tags" }, { $gt: [{ $size: "$tags" }, 2] }] } } - ⚠️ 注意:
$expr中所有字段引用必须带$前缀("$tags"),不能写"tags" - ⚠️ 性能提示:虽然比
$where快很多,但$expr整体仍难走复合索引,大数据集建议预计算长度字段(如存tagsCount: 3)并单独建索引
真正要查「至少 N 个元素」时,别只盯着数组长度
很多时候业务真实需求是「有足够多有效数据」,比如「至少 3 个非空标签」或「最近 5 条评论里有 2 条含图片」。这时候硬算 length 反而不准,应该用 $elemMatch 或聚合管道过滤后再计数。
- ✅ 查「至少 2 个非空字符串标签」:
{ tags: { $elemMatch: { $type: "string", $ne: "" } } }再配合$size不适用,得用聚合 - ✅ 聚合示例(统计非空标签数):
{ $addFields: { validTagCount: { $size: { $filter: { input: "$tags", cond: { $and: [{ $eq: [{ $type: "$$this" }, "string"] }, { $ne: ["$$this", ""] }] } } } } } } - ⚠️ 关键点:数组长度只是表象,业务语义往往藏在元素内容里;提前在写入时归一化(如清理空值、补
count字段)比每次查时动态计算更可靠











