$expr配合$strLenCP是MongoDB中安全过滤字符串长度的唯一正确方式,需处理null、空字符串、类型校验及UTF-8多字节字符等边界情况。
直接用 $strLenCP 计算字符串长度并参与条件判断,必须配合 $expr,因为普通查询操作符(如 $gt)不能作用于表达式结果。常见错误是写成 { name: { $gt: { $strLenCP: "$name" } } } —— 这语法非法,MongoDB 会报 unknown operator: $strLenCP。
正确写法是:
db.users.find({ $expr: { $gt: [{ $strLenCP: "$name" }, 5] }})
$strLenCP 正确处理 UTF-8 多字节字符(比如中文、emoji),$strLenCP("??") 返回 1,而 $strLenBytes 会返回 4 或更多null 或缺失,$strLenCP 返回 null,整个 $expr 条件为 false,该文档被排除{ name: { $ne: "" } }
聚合里不能把 $strLenCP 直接塞进第一级 $match 的普通字段条件,但可以放在 $match 的 $expr 分支下——这是唯一合法且常用的位置。别试图在 $project 里先算出长度再 $match,那多走一步毫无必要,还增加内存开销。
例如:只保留用户名长度在 2~10 之间、且非空的用户
db.users.aggregate([ { $match: { $expr: { $and: [ { $gt: [{ $strLenCP: "$name" }, 1] }, { $lt: [{ $strLenCP: "$name" }, 11] } ] } } }])
$match 支持 $expr,语义和 find() 中一致$match 里混用普通条件和 $expr 条件去查同一个字段(比如 { name: "a", $expr: { $gt: [{ $strLenCP: "$name" }, 1] } }),虽然语法允许,但逻辑矛盾且易误读$addFields 抽出长度字段,避免重复计算$where 确实支持 JavaScript 表达式,比如 { $where: "this.name && this.name.length > 5" },但它有硬伤:完全无法使用索引、执行慢、禁用在分片集群的某些模式下,且不支持 UTF-8 字符长度的准确统计(.length 是字节数或 UCS-2 单位,对 emoji 和中文不可靠)。
$strLenCP 是服务端原生操作符,性能远高于 $where
$where 在 MongoDB 5.0+ 已被明确标记为“不推荐”,6.0+ 默认禁用部分场景$where
最常漏掉的是字段类型校验。如果 name 有时是字符串、有时是数组或数字,$strLenCP 会直接报错:“$strLenCP requires a string argument, found: array”。
{ $expr: { $and: [ { $eq: [{ $type: "$name" }, "string"] }, { $gt: [{ $strLenCP: "$name" }, 5] } ] } }
$convert 强转(但失败时返回 null,需配合 $isString 判断)$ifNull 包一层:{ $strLenCP: { $ifNull: ["$name", ""] } }
真正麻烦的不是怎么写对,而是写对之后忘了字段类型混合、null/空值、多字节字符这些隐性坑——它们不会立刻报错,但会在某个凌晨三点的线上查询里突然暴露。